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/app.go b/app.go index a29e0d1..58554a9 100644 --- a/app.go +++ b/app.go @@ -32,15 +32,69 @@ 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 _, 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) + + 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) { - config, spaces, err := fetchSpacesAndSetActiveState(configId) +func processSpaces(config apiserver.Configuration) { + spaces, err := fetchSpaces(config) if err != nil { return } 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 { @@ -54,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) { @@ -96,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 { @@ -124,10 +168,10 @@ 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") + 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/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, + }) +} 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..77233a2 100644 --- a/main.go +++ b/main.go @@ -41,19 +41,14 @@ 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) } log.Info("main", "Terminate the app.") } 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.