package persistence import ( "encoding/json" "errors" "log" "os" "git.hamburg.ccc.de/ccchh/spaceapid/types" ) const ( persistentStateFile = "spaceapid-state.json" persistentStatePath = persistentStateDir + persistentStateFile ) // MergeOldState merges a given SpaceAPIResponse with the state saved at the time of last program exit and then deletes // the file containing the old state. func MergeOldState(response *types.SpaceAPIResponseV14) { var ( err error oldState []byte persistedState types.PersistentStateV14 ) log.Println("Merging old state from", persistentStatePath, "...") // Check if state.json is present _, err = os.Stat(persistentStatePath) if err != nil { log.Println("Old state json not accessible at", persistentStatePath, ", skipping merge... error:", err) if errors.Is(err, os.ErrNotExist) { return } goto removeOld } // Read file and load persisted state oldState, err = os.ReadFile(persistentStatePath) if err != nil { log.Println("Error reading old state from", persistentStatePath, ", skipping merge... error:", err) goto removeOld } err = json.Unmarshal(oldState, &persistedState) if err != nil { log.Println(persistentStatePath, "doesn't seem to contain valid data... error:", err) goto removeOld } // Merge state response.State = persistedState.State // Merge sensors for key, environmentSensors := range persistedState.Sensors { for _, sensor := range environmentSensors { // Order or amount of sensors might have changed, so check sensors already present in response // and then look for matching values in persisted state for i := range response.Sensors[key] { if rs := &response.Sensors[key][i]; rs.Location == sensor.Location && rs.Name == sensor.Name { rs.Value = sensor.Value rs.LastChange = sensor.LastChange } } } } // Delete old state json removeOld: err = os.RemoveAll(persistentStatePath) if err != nil { log.Println("Failed to remove", persistentStatePath, ", continuing... error:", err) } } func SaveCurrentState(response types.SpaceAPIResponseV14) { // Create state directory if not present err := os.MkdirAll(persistentStateDir, 0750) if err != nil { log.Fatalln("Failed creating", persistentStateDir, ", aborting... error:", err) } // Open persistent state file file, err := os.OpenFile(persistentStatePath, os.O_WRONLY|os.O_CREATE, 0644) if err != nil { log.Fatalln("Failed opening", persistentStatePath, "while trying to save current state... error:", err) } defer file.Close() // Create persistent state persistentStateV14 := types.PersistentStateV14{ State: struct { Open bool `json:"open"` LastChange int64 `json:"lastchange"` }{Open: response.State.Open, LastChange: response.State.LastChange}, } // Save sensor state persistentStateV14.Sensors = make(map[string][]types.PersistentEnvironmentSensor) for key, environmentSensors := range response.Sensors { persistentStateV14.Sensors[key] = make([]types.PersistentEnvironmentSensor, len(environmentSensors)) for i, sensor := range environmentSensors { persistentStateV14.Sensors[key][i].Value = sensor.Value persistentStateV14.Sensors[key][i].Location = sensor.Location persistentStateV14.Sensors[key][i].Name = sensor.Name persistentStateV14.Sensors[key][i].LastChange = sensor.LastChange } } // Serialize persistent state marshal, err := json.MarshalIndent(persistentStateV14, "", "\t") if err != nil { log.Fatalln("Failed serializing persistent state... error:", err) } // Write to file _, err = file.Write(marshal) if err != nil { log.Fatalln("Failed writing persistent state to file", file.Name(), "... error:", err) } }