Delete types/v14.go, replace with v15.go.
Rename internal types to not include message version.
I don't currently see the need to support multiple message
versions that have incompatible layouts.
v15 is backwards compatible with v14, so the config example now
specifies both.
Rename the "response" field in the config file to "static" as
dynamic/static makes more sense from the user's perspective.
(Internally it's still SpaceapidConfig.{Dynamic,Response}
to align with type names and usage.)
120 lines
3.7 KiB
Go
120 lines
3.7 KiB
Go
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.SpaceAPIResponse) {
|
|
var (
|
|
err error
|
|
oldState []byte
|
|
persistedState types.PersistentSpaceState
|
|
)
|
|
|
|
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.FromPersistentSpaceState(persistedState)
|
|
|
|
// 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.SpaceAPIResponse) {
|
|
// 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.PersistentSpaceState{
|
|
State: struct {
|
|
Open bool `json:"open"`
|
|
LastChange int64 `json:"lastchange"`
|
|
Message string `json:"message,omitempty"`
|
|
}{Open: response.State.Open, LastChange: response.State.LastChange, Message: response.State.Message},
|
|
}
|
|
// 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)
|
|
}
|
|
}
|