From 5fb3d470f9c0a410508d97a7f63ba74d08cddaf3 Mon Sep 17 00:00:00 2001 From: airvine-leicom Date: Wed, 22 Mar 2023 15:43:44 +0100 Subject: [PATCH 1/5] update active nullable --- apiserver/model_configuration.go | 2 +- openapi.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apiserver/model_configuration.go b/apiserver/model_configuration.go index f190628..629e630 100644 --- a/apiserver/model_configuration.go +++ b/apiserver/model_configuration.go @@ -31,7 +31,7 @@ type Configuration struct { RequestTimeout int32 `json:"requestTimeout,omitempty"` // Set to `true` by the app when running and to `false` when app is stopped - Active bool `json:"active,omitempty"` + Active *bool `json:"active,omitempty"` // List of Eliona project ids for which this endpoint should collect data. For each project id all smart devices are automatically created as an asset in Eliona. The mapping between Eliona is stored as an asset mapping in the thingdust app and can be read with the SpaceMapping endpoint. ProjIds *[]string `json:"projIds,omitempty"` diff --git a/openapi.yaml b/openapi.yaml index 15b2f3d..d3fcd74 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -281,7 +281,7 @@ components: type: boolean readOnly: true description: Set to `true` by the app when running and to `false` when app is stopped - nullable: false + nullable: true projIds: type: array description: List of Eliona project ids for which this endpoint should collect data. For each project id all smart devices are automatically created as an asset in Eliona. The mapping between Eliona is stored as an asset mapping in the thingdust app and can be read with the SpaceMapping endpoint. From 2000990a829dfe192a946801b0a6b81e97eee6fb Mon Sep 17 00:00:00 2001 From: airvine-leicom Date: Wed, 22 Mar 2023 15:45:11 +0100 Subject: [PATCH 2/5] added functions to check and set active flag --- conf/conf.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/conf/conf.go b/conf/conf.go index 40c58cc..341ecde 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -144,7 +144,7 @@ func apiConfigFromDbConfig(dbConfig *dbthingdust.Config) *apiserver.Configuratio apiConfig.Enable = &dbConfig.Enable.Bool apiConfig.RefreshInterval = dbConfig.RefreshInterval.Int32 apiConfig.RequestTimeout = dbConfig.RequestTimeout.Int32 - apiConfig.Active = dbConfig.Active.Bool + apiConfig.Active = &dbConfig.Active.Bool apiConfig.ProjIds = common.Ptr[[]string](dbConfig.ProjectIds) return &apiConfig } @@ -157,7 +157,7 @@ func dbConfigFromApiConfig(apiConfig *apiserver.Configuration) *dbthingdust.Conf dbConfig.Enable = null.BoolFromPtr(apiConfig.Enable) dbConfig.RefreshInterval = null.Int32FromPtr(&apiConfig.RefreshInterval) dbConfig.RequestTimeout = null.Int32FromPtr(&apiConfig.RequestTimeout) - dbConfig.Active = null.BoolFromPtr(&apiConfig.Active) + dbConfig.Active = null.BoolFromPtr(apiConfig.Active) if apiConfig.ProjIds != nil { dbConfig.ProjectIds = *apiConfig.ProjIds } @@ -171,3 +171,17 @@ func SetConfigActiveState(configID int64, state bool) (int64, error) { dbthingdust.ConfigColumns.Active: state, }) } + +func IsConfigActive(config apiserver.Configuration) bool { + return config.Active == nil || *config.Active +} + +func IsConfigEnabled(config apiserver.Configuration) bool { + return config.Enable == nil || *config.Enable +} + +func SetAllConfigsInactive(ctx context.Context) (int64, error) { + return dbthingdust.Configs().UpdateAllG(ctx, dbthingdust.M{ + dbthingdust.ConfigColumns.Active: false, + }) +} From 056f180e9ea12ebf650a29b295fc2b8eda483c5b Mon Sep 17 00:00:00 2001 From: andrew-leicom Date: Thu, 23 Mar 2023 10:47:01 +0100 Subject: [PATCH 3/5] Read Configuration periodically implemented --- app.go | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- go.mod | 2 +- go.sum | 4 ++-- main.go | 38 ++++++++++++++++++++++----------- 4 files changed, 93 insertions(+), 16 deletions(-) diff --git a/app.go b/app.go index a29e0d1..bbc2cf4 100644 --- a/app.go +++ b/app.go @@ -32,6 +32,68 @@ import ( "github.com/eliona-smart-building-assistant/go-utils/log" ) + +func CheckConfigsandSetActiveState() { + configs, err := conf.GetConfigs(context.Background()) + if err != nil { + log.Fatal("conf", "Couldn't read configs from DB: %v", err) + return + } + + for len(configs)< 1 { + configs, err = conf.GetConfigs(context.Background()) + if err != nil { + log.Fatal("conf", "Couldn't read configs from DB: %v", err) + return + } + time.Sleep(time.Second) + } + + for _, config := range configs { + // Skip config if disabled and set inactive + if !conf.IsConfigEnabled(config) { + if conf.IsConfigActive(config) { + conf.SetConfigActiveState(config.ConfigId, false) + } + continue + } + + // Signals that this config is active + if !conf.IsConfigActive(config) { + conf.SetConfigActiveState(config.ConfigId, true) + log.Info("conf", "Collecting initialized with Configuration %d:\n"+ + "API Endpoint: %s\n"+ + "API Key: %s\n"+ + "Enable: %t\n"+ + "Refresh Interval: %d\n"+ + "Request Timeout: %d\n"+ + "Active: %t\n"+ + "Project IDs: %v\n", + config.ConfigId, + config.ApiEndpoint, + config.ApiKey, + *config.Enable, + config.RefreshInterval, + config.RequestTimeout, + *config.Active, + config.ProjIds) + } + + // Runs the ReadNode. If the current node is currently running, skip the execution + // After the execution sleeps the configured timeout. During this timeout no further + // process for this config is started to read the data. + common.RunOnceWithParam(func(config apiserver.Configuration) { + log.Info("main", "Processing Spaces for Configuration with configId %d started", config.ConfigId) + + processSpaces(config.ConfigId) + + log.Info("main", "Processing Spaces for Configuration with configId %d finished", config.ConfigId) + + time.Sleep(time.Second * time.Duration(config.RefreshInterval)) + }, config, config.ConfigId) + } +} + // For each enabled configuration, processSpaces() performs Continuous Asset Creation // for each project_id and space pair, and writes corresponding data to each asset. func processSpaces(configId int64) { @@ -41,6 +103,7 @@ func processSpaces(configId int64) { } if config.ProjIds != nil { for _, projId := range *config.ProjIds { + log.Debug("projectid", "ProjId: %v", projId) for spaceName := range spaces { confSpace, err := getOrCreateMappingIfNecessary(config, projId, spaceName) if err != nil { @@ -127,7 +190,7 @@ func getOrCreateMappingIfNecessary(config *apiserver.Configuration, projId strin func createAssetAndMapping(projId string, spaceName string, config *apiserver.Configuration) (*apiserver.Space, error) { assetId, err := eliona.CreateNewAsset(projId, spaceName) if err != nil { - log.Error("spaces", "Error when creating new asset") + log.Error("spaces", "Error when creating new asset: %v", err) return nil, err } log.Debug("spaces", "AssetId %v assigned to space %v", assetId, spaceName) diff --git a/go.mod b/go.mod index b7e99d1..d56d654 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/eliona-smart-building-assistant/go-eliona v1.9.4 github.com/eliona-smart-building-assistant/go-eliona-api-client/v2 v2.4.8 - github.com/eliona-smart-building-assistant/go-utils v1.0.17 + github.com/eliona-smart-building-assistant/go-utils v1.0.18 github.com/friendsofgo/errors v0.9.2 github.com/gorilla/mux v1.8.0 github.com/volatiletech/null/v8 v8.1.2 diff --git a/go.sum b/go.sum index fa7a4c7..ceb411c 100644 --- a/go.sum +++ b/go.sum @@ -120,8 +120,8 @@ github.com/eliona-smart-building-assistant/go-eliona v1.9.4 h1:ejT0ekEp6itLXnSg9 github.com/eliona-smart-building-assistant/go-eliona v1.9.4/go.mod h1:ulSBkPPvjEXJBQEgZ+5/XLj/USrEFygj2EByr83f8dk= github.com/eliona-smart-building-assistant/go-eliona-api-client/v2 v2.4.8 h1:nWKpz32tk5y/zY3cBpe9J1aAuH0R88JOGSAIQXYdURY= github.com/eliona-smart-building-assistant/go-eliona-api-client/v2 v2.4.8/go.mod h1:AdhCjJsNuT2WA6HpC++Kf4SBO0ZDJhwc3QnYk59naFQ= -github.com/eliona-smart-building-assistant/go-utils v1.0.17 h1:bGNkuy2+/dja2nwH3Y9tBGi+5+MFqSrP6FLCLfBJLzQ= -github.com/eliona-smart-building-assistant/go-utils v1.0.17/go.mod h1:mDgXKlaqLWPdPu5QJVs7oxqRVqTKfgvmmqhMm1zFEyU= +github.com/eliona-smart-building-assistant/go-utils v1.0.18 h1:HwEmSKiMKyXVp2ZTMKOiluTdYzKsv8gX82CymfSTr+4= +github.com/eliona-smart-building-assistant/go-utils v1.0.18/go.mod h1:mDgXKlaqLWPdPu5QJVs7oxqRVqTKfgvmmqhMm1zFEyU= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= diff --git a/main.go b/main.go index dc66e7c..49a2ba9 100644 --- a/main.go +++ b/main.go @@ -41,20 +41,34 @@ func main() { conf.InitConfiguration, eliona.InitEliona, ) - var functions []func() - functions = append(functions, listenApi) - configs, err := conf.GetConfigs(context.Background()) + + common.WaitForWithOs( + common.Loop(CheckConfigsandSetActiveState, time.Second), + listenApi, + ) + + // At the end set all configuration inactive + _, err := conf.SetAllConfigsInactive(context.Background()) if err != nil { - log.Error("Configurations", "Error retrieving configurations") - } - for _, config := range configs { - log.Debug("main", "Appending processSpaces() with configID: %v and refresh interval %v", config.ConfigId, config.RefreshInterval) - functions = append(functions, common.LoopWithParam(processSpaces, config.ConfigId, time.Duration(config.RefreshInterval)*time.Second)) - } - common.WaitFor(functions...) - for _, config:= range configs { - conf.SetConfigActiveState(config.ConfigId, false) + log.Error("conf", "setting all configs inactive: %v", err) } + + + + // var functions []func() + // functions = append(functions, listenApi) + // configs, err := conf.GetConfigs(context.Background()) + // if err != nil { + // log.Error("Configurations", "Error retrieving configurations") + // } + // for _, config := range configs { + // log.Debug("main", "Appending processSpaces() with configID: %v and refresh interval %v", config.ConfigId, config.RefreshInterval) + // functions = append(functions, common.LoopWithParam(processSpaces, config.ConfigId, time.Duration(config.RefreshInterval)*time.Second)) + // } + // common.WaitFor(functions...) + // for _, config:= range configs { + // conf.SetConfigActiveState(config.ConfigId, false) + // } log.Info("main", "Terminate the app.") } From 2b390f73b40464921b41dd90bf5596c50f2c4f36 Mon Sep 17 00:00:00 2001 From: andrew-leicom Date: Thu, 23 Mar 2023 10:49:39 +0100 Subject: [PATCH 4/5] Remove unused code --- main.go | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/main.go b/main.go index 49a2ba9..77233a2 100644 --- a/main.go +++ b/main.go @@ -41,34 +41,15 @@ func main() { conf.InitConfiguration, eliona.InitEliona, ) - common.WaitForWithOs( common.Loop(CheckConfigsandSetActiveState, time.Second), listenApi, ) - // At the end set all configuration inactive _, err := conf.SetAllConfigsInactive(context.Background()) if err != nil { log.Error("conf", "setting all configs inactive: %v", err) } - - - - // var functions []func() - // functions = append(functions, listenApi) - // configs, err := conf.GetConfigs(context.Background()) - // if err != nil { - // log.Error("Configurations", "Error retrieving configurations") - // } - // for _, config := range configs { - // log.Debug("main", "Appending processSpaces() with configID: %v and refresh interval %v", config.ConfigId, config.RefreshInterval) - // functions = append(functions, common.LoopWithParam(processSpaces, config.ConfigId, time.Duration(config.RefreshInterval)*time.Second)) - // } - // common.WaitFor(functions...) - // for _, config:= range configs { - // conf.SetConfigActiveState(config.ConfigId, false) - // } log.Info("main", "Terminate the app.") } From 4dc2f5db1201ec5a03e6e0eba1c6c59da2ef54e8 Mon Sep 17 00:00:00 2001 From: andrew-leicom Date: Thu, 23 Mar 2023 12:10:58 +0100 Subject: [PATCH 5/5] simplified processSpaces, CheckConfigsandSetActive --- app.go | 37 +++++++++---------------------------- 1 file changed, 9 insertions(+), 28 deletions(-) diff --git a/app.go b/app.go index bbc2cf4..58554a9 100644 --- a/app.go +++ b/app.go @@ -40,15 +40,6 @@ func CheckConfigsandSetActiveState() { return } - for len(configs)< 1 { - configs, err = conf.GetConfigs(context.Background()) - if err != nil { - log.Fatal("conf", "Couldn't read configs from DB: %v", err) - return - } - time.Sleep(time.Second) - } - for _, config := range configs { // Skip config if disabled and set inactive if !conf.IsConfigEnabled(config) { @@ -85,7 +76,7 @@ func CheckConfigsandSetActiveState() { common.RunOnceWithParam(func(config apiserver.Configuration) { log.Info("main", "Processing Spaces for Configuration with configId %d started", config.ConfigId) - processSpaces(config.ConfigId) + processSpaces(config) log.Info("main", "Processing Spaces for Configuration with configId %d finished", config.ConfigId) @@ -96,8 +87,8 @@ func CheckConfigsandSetActiveState() { // For each enabled configuration, processSpaces() performs Continuous Asset Creation // for each project_id and space pair, and writes corresponding data to each asset. -func processSpaces(configId int64) { - config, spaces, err := fetchSpacesAndSetActiveState(configId) +func processSpaces(config apiserver.Configuration) { + spaces, err := fetchSpaces(config) if err != nil { return } @@ -117,29 +108,19 @@ func processSpaces(configId int64) { } } -func fetchSpacesAndSetActiveState(configId int64) (*apiserver.Configuration, thingdust.Spaces, error) { - config, err := conf.GetConfig(context.Background(), configId) - if err != nil { - log.Error("spaces", "Error reading configuration") - return nil, nil, err - } - if config.Enable == nil || !*config.Enable { - conf.SetConfigActiveState(config.ConfigId, false) - return nil, nil, err - } - conf.SetConfigActiveState(config.ConfigId, true) +func fetchSpaces(config apiserver.Configuration) (thingdust.Spaces, error) { log.Debug("spaces", "Processing space with configID: %v", config.ConfigId) request, err := http.NewRequestWithApiKey(config.ApiEndpoint + "/get_space_states", "X-API-KEY", config.ApiKey) if err != nil { log.Error("spaces", "Error with request: %v", err) - return nil, nil, err + return nil, err } spaces, err := http.Read[thingdust.Spaces](request, time.Duration(time.Duration.Seconds(1)), true) if err != nil { log.Error("spaces", "Error reading spaces: %v", err) - return nil, nil, err + return nil, err } - return config, spaces, nil + return spaces, nil } func sendData(confSpace *apiserver.Space, spaces thingdust.Spaces, spaceName string) { @@ -159,7 +140,7 @@ func sendData(confSpace *apiserver.Space, spaces thingdust.Spaces, spaceName str } } -func getOrCreateMappingIfNecessary(config *apiserver.Configuration, projId string, spaceName string) (*apiserver.Space, error) { +func getOrCreateMappingIfNecessary(config apiserver.Configuration, projId string, spaceName string) (*apiserver.Space, error) { var confSpace *apiserver.Space confSpace, err := conf.GetSpace(context.Background(), config.ConfigId, projId, spaceName) if err != nil { @@ -187,7 +168,7 @@ func getOrCreateMappingIfNecessary(config *apiserver.Configuration, projId strin return confSpace, nil } -func createAssetAndMapping(projId string, spaceName string, config *apiserver.Configuration) (*apiserver.Space, error) { +func createAssetAndMapping(projId string, spaceName string, config apiserver.Configuration) (*apiserver.Space, error) { assetId, err := eliona.CreateNewAsset(projId, spaceName) if err != nil { log.Error("spaces", "Error when creating new asset: %v", err)