// RestoreGatewayApp restores the Gateway App func (m *Mapper) RestoreGatewayApp() error { // return err when current state of gateway app is not WaitingRestore if err := m.StateTable.CheckGatewayState(state.WaitingRestore); err != nil { return state.ErrNotWaitingRestore } if err := m.StateTable.ChangeGatewayState(state.Restoring, common.ToLocalAppID(m.Config.MasterApp.Site, m.Config.MasterApp.AppID)); err != nil { return err } // change back to WaitingRestore only err happened during restoring gateway app hanleErr := func(err error) error { m.StateTable.ChangeGatewayState(state.WaitingRestore, common.ToLocalAppID(m.Config.MasterApp.Site, m.Config.MasterApp.AppID)) return err } // disconnect all connecting mqtt clients m.Cloud.DisconnectAllMQTTClients() // clear DB if err := m.Store.ClearDB(); err != nil { return hanleErr(err) } // onboard with gateway app if _, err := m.onboardgatewayApp(); err != nil { return hanleErr(err) } // restore gateway if err := m.restoreGateway(m.Config.MasterApp.Site, m.Config.MasterApp.AppID); err != nil { return hanleErr(err) } // change state of gateway app to DoneOnboard after restore if err := m.StateTable.ChangeGatewayState(state.DoneOnboard, common.ToLocalAppID(m.Config.MasterApp.Site, m.Config.MasterApp.AppID)); err != nil { return hanleErr(err) } // Restore all the end-node apps saved on cloud apps, err := m.ListAllEndnodeApps() if err != nil { return err } for _, a := range apps { m.StateTable.ChangeAppState(state.Restoring, common.LocalID(common.LocalAppID(a.ServerLocation, a.AppID))) err = m.RestoreEndnodeApp(a) if err != nil { return err } m.StateTable.ChangeAppState(state.DoneOnboard, common.LocalID(common.LocalAppID(a.ServerLocation, a.AppID))) } //change restoreMode to false m.StateTable.RestoreMode = false return nil }
// PendingEndnodes returns pending (not mapped to cloud) end-nodes. func (m *Mapper) PendingEndnodes(serverLocation, appID string) ([]common.PendingEndnode, error) { //verify state if err := m.gatewayRestoringOrNotReady(); err != nil { return []common.PendingEndnode{}, err } // check whether gateway id exists if _, err := m.Store.FindGatewayByAppID(appID); err != nil { return []common.PendingEndnode{}, err } if err := m.StateTable.CheckAppState(common.ToLocalAppID(serverLocation, appID), state.DoneOnboard); err != nil { return []common.PendingEndnode{}, state.ErrNotOnboarded } items, err := m.Store.FindNilCloud(serverLocation, appID) if err != nil { return nil, err } endnodes := make([]common.PendingEndnode, 0, len(items)) for _, o := range items { endnodes = append(endnodes, common.PendingEndnode{ VendorThingID: o.EndnodeAddress.VendorThingID, ThingProperties: m.toJSON(o.ThingProperties), States: m.toJSON(o.States), }) } return endnodes, nil }
// GetGatewayID gets the Gateway's ThingID for that app. func (m *Mapper) GetGatewayID(serverLocation, appID string) (string, error) { // verify state first if err := m.gatewayRestoringOrNotReady(); err != nil { return "", err } gca, err := m.Store.FindGatewayByAppID(appID) if err != nil { return "", err } if err := m.StateTable.CheckAppState(common.ToLocalAppID(serverLocation, appID), state.DoneOnboard); err != nil { return "", state.ErrNotOnboarded } return gca.ThingID, nil }
// CompleteEndnodeOnboarding notifies Endnode onboarding completion to the Gateway func (m *Mapper) CompleteEndnodeOnboarding(serverLocation, appID, vendorThingID, thingID string) error { //verify state if err := m.gatewayRestoringOrNotReady(); err != nil { return err } // check whether gateway id exists if _, err := m.Store.FindGatewayByAppID(appID); err != nil { return err } if err := m.StateTable.CheckAppState(common.ToLocalAppID(serverLocation, appID), state.DoneOnboard); err != nil { return state.ErrNotOnboarded } ea := common.EndnodeAddress{ ServerLocation: serverLocation, AppID: appID, VendorThingID: vendorThingID, } ca := common.CloudAddress{ ServerLocation: serverLocation, AppID: appID, ThingID: thingID, } item, err := m.Store.UpdateCloud(ea, ca) if err != nil { return err } // update endnode token gca, err := m.Store.FindGatewayByAppID(appID) if err != nil { return err } err = m.Cloud.GenerateEndnodeToken(ca, gca) if err != nil { return err } // update state return m.Cloud.SendState(*item.CloudAddress, item.States) }
// ReplaceEndnode update exsiting Thing with new vendorThingID func (m *Mapper) ReplaceEndnode(serverLocation, appID, newVendorThingID, thingID string) error { //verify state if err := m.gatewayRestoringOrNotReady(); err != nil { return err } // check whether gateway id exists if _, err := m.Store.FindGatewayByAppID(appID); err != nil { return err } if err := m.StateTable.CheckAppState(common.ToLocalAppID(serverLocation, appID), state.DoneOnboard); err != nil { return state.ErrNotOnboarded } ea := common.EndnodeAddress{ ServerLocation: serverLocation, AppID: appID, VendorThingID: newVendorThingID, } ca := common.CloudAddress{ ServerLocation: serverLocation, AppID: appID, ThingID: thingID, } // endnode address before updated oea, err := m.Store.FindByCloudAddress(ca) if err != nil { return err } _, err = m.Store.UpdateEndnode(ea, ca) if err != nil { return err } // update converter-endnode mapping return m.Endnode.ReplaceEndnode(*oea.EndnodeAddress, ea) }
// Run starts gateway-agent core procedure. // When restore is true, gateway-agent start as restore mode, then go to normal // mode. func Run(c *config.Config, restore bool) error { if !atomic.CompareAndSwapInt32(&running, 0, 1) { return errors.New("agent already running") } debug.Printf("Config: %#v\n", *c) // build asynchronous objects. quit = make(chan int) wait = &sync.WaitGroup{} // setup data store store, err := store.New(c.Store.Type, c.Store.Params) if err != nil { atomic.StoreInt32(&running, 0) return err } defer store.Close() // setup global context. g := common.Global{ Config: *c, Log: log.New(c.LogWriter(), "IoTGW ", log.Ltime), Quit: quit, Wait: wait, } st, err := state.LoadTable(store, common.ToLocalAppID(g.Config.MasterApp.Site, g.Config.MasterApp.AppID), restore) if err != nil { atomic.StoreInt32(&running, 0) return err } // prepare all inner-processes. mapper := &mapper.Mapper{ Global: g, Store: store, StateTable: st, } cg := &cloud.Gateway{ Global: g, Mapper: mapper, Store: store, } eg := &endnode.Gateway{ Global: g, Mapper: mapper, } local := &local.Server{ Global: g, Addr: c.LocalServer.Addr.NetworkAddress(), Mapper: mapper, Store: store, } ad := &advertise.Advertiser{ Global: g, Location: advertise.Location{ HTTPS: false, Port: c.LocalServer.Addr.Port, Path: "", }, Server: fmt.Sprintf("%s UPnP/1.1 gateway-agent", runtime.GOOS), } // connect inner-processes. mapper.Cloud = cg mapper.Endnode = eg // start all inner-processes. mapper.Run() cg.Run() eg.Run() local.Run() ad.Run() atomic.StoreInt32(&running, 2) // detect termination. mw := c.MessageWriter() fmt.Fprintln(mw, "Press CTRL-C to terminate") sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGINT) MainLoop: for { select { case <-quit: break MainLoop case s := <-sig: switch s { case syscall.SIGINT: stop() break MainLoop } } } signal.Stop(sig) wait.Wait() atomic.StoreInt32(&running, 0) fmt.Fprintln(mw, "Teminated completely") return nil }
// OnboardGatewayApp onboards the Gateway for the Gateway App func (m *Mapper) OnboardGatewayApp() (string, error) { // verify state if m.StateTable.RestoreMode { return "", state.ErrRestoringGateway } if err := m.StateTable.CheckGatewayState(state.Onboarding); err == nil { return "", state.ErrOnboarding } // start to onboard // change state to Onboarding before onboard if err := m.StateTable.ChangeGatewayState(state.Onboarding, common.ToLocalAppID(m.Config.MasterApp.Site, m.Config.MasterApp.AppID)); err != nil { return "", err } // change state of gateway app to onboarding before onboard gwLocalID := common.LocalAppID(m.Config.MasterApp.Site, m.Config.MasterApp.AppID) if err := m.StateTable.ChangeAppState(state.Onboarding, common.LocalID(gwLocalID)); err != nil { return "", err } handleErr := func(err error) (string, error) { m.StateTable.ChangeGatewayState(state.WaitingOnboard, common.ToLocalAppID(m.Config.MasterApp.Site, m.Config.MasterApp.AppID)) m.StateTable.ChangeAppState(state.Onboarding, common.LocalID(gwLocalID)) return "", err } // for multiple onboard, need to delete gateway from DB first if _, err := m.Store.FindGatewayByAppID(m.Config.MasterApp.AppID); err == nil { // disconnect mqtt connection if err := m.Cloud.DisconnectMQTTClient(common.App{ AppID: m.Config.MasterApp.AppID, ServerLocation: m.Config.MasterApp.Site, }); err != nil { return handleErr(err) } // delete gateway if err := m.Store.DeleteGateway(m.Config.MasterApp.AppID); err != nil { return handleErr(err) } } gwID, err := m.onboardgatewayApp() if err != nil { return handleErr(err) } // change state of gateway app to DoneOnboard after onboard if err := m.StateTable.ChangeAppState(state.DoneOnboard, common.LocalID(gwLocalID)); err != nil { return handleErr(err) } // change state to DoneOnboard after onboard if err := m.StateTable.ChangeGatewayState(state.DoneOnboard, common.ToLocalAppID(m.Config.MasterApp.Site, m.Config.MasterApp.AppID)); err != nil { return handleErr(err) } return gwID, nil }