Initial cloud-services repo - gateway service + pkg modules
This commit is contained in:
192
pkg/cache/digital_twin.go
vendored
Normal file
192
pkg/cache/digital_twin.go
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"fiskerinc.com/modules/dbc/state"
|
||||
"fiskerinc.com/modules/logger"
|
||||
"fiskerinc.com/modules/redis"
|
||||
"fiskerinc.com/modules/utils/querystring"
|
||||
redigo "github.com/gomodule/redigo/redis"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
pattern = "car:*:state"
|
||||
)
|
||||
|
||||
type DigitalTwinTimestampState struct {
|
||||
redisClient redis.Client
|
||||
}
|
||||
|
||||
func NewDigitalTwinTimestampState(redisClient redis.Client) *DigitalTwinTimestampState {
|
||||
return &DigitalTwinTimestampState{
|
||||
redisClient: redisClient,
|
||||
}
|
||||
}
|
||||
|
||||
// getStateKeys retrieves car state keys from Redis based on the specified pattern
|
||||
// and returns a sliced list of keys according to the provided offset and limit.
|
||||
//
|
||||
// Parameters:
|
||||
// - offset: An integer indicating the starting index of the slice.
|
||||
// - limit: An integer specifying the maximum number of elements in the sliced list.
|
||||
//
|
||||
// Returns:
|
||||
// - []string: A sliced list of car state keys based on the given offset and limit.
|
||||
// - error: An error, if any, encountered during the Redis operation or slicing process.
|
||||
func (dtts *DigitalTwinTimestampState) getStateKeys(offset, limit int) ([]string, error) {
|
||||
keys, err := redigo.Strings(dtts.redisClient.Execute("KEYS", pattern))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
totalKeys := len(keys)
|
||||
if totalKeys <= offset {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if (offset + limit) > totalKeys {
|
||||
limit = totalKeys - offset
|
||||
}
|
||||
|
||||
sort.Strings(keys)
|
||||
|
||||
keys = keys[offset : offset+limit]
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
// readCarStateByKey retrieves data from Redis based on the specified key using the HGETALL command.
|
||||
// It iterates over all keys and values returned by the command, sets them in a response map,
|
||||
// and returns the populated map along with any encountered errors.
|
||||
//
|
||||
// Parameters:
|
||||
// - key: A string representing the key to retrieve data from in Redis.
|
||||
//
|
||||
// Returns:
|
||||
// - map[string]interface{}: A map containing keys and values retrieved from Redis.
|
||||
// - error: An error, if any, encountered during the Redis HGETALL operation or mapping process.
|
||||
func (dtts *DigitalTwinTimestampState) readCarStateByKey(key string) (map[string]interface{}, error) {
|
||||
|
||||
keyval := make(map[string]interface{})
|
||||
batch := redis.NewRedisBatchCommands()
|
||||
|
||||
batch.Add("HGETALL", key)
|
||||
|
||||
payload, err := redigo.Values(dtts.redisClient.ExecuteBatch(batch))
|
||||
if err != nil {
|
||||
return keyval, err
|
||||
}
|
||||
|
||||
stateValues, err := redigo.Values(payload[0], nil)
|
||||
if err != nil {
|
||||
return keyval, err
|
||||
}
|
||||
|
||||
for i := 0; i < len(stateValues); i += 2 {
|
||||
key, okKey := stateValues[i].([]byte)
|
||||
value, okValue := stateValues[i+1].([]byte)
|
||||
|
||||
if !okKey || !okValue {
|
||||
continue
|
||||
}
|
||||
|
||||
err = dtts.parseCarState(string(key), value, keyval)
|
||||
// log error, do not return error so we can read other properties for digital twin
|
||||
if err != nil {
|
||||
logger.Warn().Err(err).Send()
|
||||
continue
|
||||
}
|
||||
}
|
||||
return keyval, nil
|
||||
}
|
||||
|
||||
// GetDigitalTwinSignals retrieves digital twin signals from Redis based on the specified offset and limit.
|
||||
// It reads all signals from Redis and returns a list of maps, where each map represents a cars signal with its properties.
|
||||
//
|
||||
// Parameters:
|
||||
// - offset: An integer indicating the starting index of the signals to retrieve.
|
||||
// - limit: An integer specifying the maximum number of signals to retrieve.
|
||||
//
|
||||
// Returns:
|
||||
// - []map[string]interface{}: A list of maps representing digital twin signals.
|
||||
|
||||
func (dtts *DigitalTwinTimestampState) GetDigitalTwinSignals(offset, limit int) (resp []map[string]interface{}) {
|
||||
|
||||
keys, err := dtts.getStateKeys(offset, limit)
|
||||
if err != nil {
|
||||
logger.Warn().Err(err).Send()
|
||||
return
|
||||
}
|
||||
for _, key := range keys {
|
||||
keyval, err := dtts.readCarStateByKey(key)
|
||||
if err != nil {
|
||||
logger.Warn().Err(err).Send()
|
||||
continue
|
||||
}
|
||||
if len(keyval) > 0 {
|
||||
keySlice := strings.Split(key, ":")
|
||||
keyval["VIN"] = keySlice[1]
|
||||
resp = append(resp, keyval)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// timestampKey generates a timestamp key based on the provided key by appending ":updated" to it.
|
||||
// It formats the key in a way suitable for storing timestamps associated with the original key in data storage systems.
|
||||
//
|
||||
// Parameters:
|
||||
// - key: A string representing the original key for which the timestamp key is generated.
|
||||
//
|
||||
// Returns:
|
||||
// - string: A formatted string representing the timestamp key.
|
||||
func (dtts *DigitalTwinTimestampState) timestampKey(key string) string {
|
||||
return fmt.Sprintf("%s:%s", key, "updated")
|
||||
}
|
||||
|
||||
// timestampVal parses a byte slice containing a JSON-encoded timestamp and returns a pointer to a time.Time
|
||||
// representing the parsed timestamp. It uses the UnmarshalJSON method of the time.Time type for decoding.
|
||||
//
|
||||
// Parameters:
|
||||
// - val: A byte slice containing the JSON-encoded timestamp to be parsed.
|
||||
//
|
||||
// Returns:
|
||||
// - time.Time: A time representing the parsed timestamp.
|
||||
// - error: An error, if any, encountered during the parsing process.
|
||||
func (dtts *DigitalTwinTimestampState) timestampVal(val []byte) (time.Time, error) {
|
||||
t := &time.Time{}
|
||||
err := t.UnmarshalJSON(val)
|
||||
return *t, err
|
||||
}
|
||||
|
||||
// parseCarState checks if the provided key is needed and, if so, sets the key and value in the given map.
|
||||
//
|
||||
// Parameters:
|
||||
// - key: A string representing the key to check and potentially set in the map.
|
||||
// - value: A byte slice containing the value associated with the key.
|
||||
// - keyval: A map[string]interface{} where the key and valueset if the key is needed.
|
||||
//
|
||||
// Returns:
|
||||
// - error: An error, if any, encountered during the parsing and mapping process.
|
||||
func (dtts *DigitalTwinTimestampState) parseCarState(key string, value []byte, keyval map[string]interface{}) error {
|
||||
var err error
|
||||
val := string(value)
|
||||
switch key {
|
||||
case state.BMS_PwrBattRmngCpSOC, state.BMS_RmChrgTi_FullChrg, state.BCM_PwrMod, state.PWC_ChrgSts, state.VCU_DCChrgRmngTi, state.BMS_RmChrgTi_TrgtSoC:
|
||||
keyval[key], err = strconv.Atoi(val)
|
||||
case state.ICC_TotMilg_ODO:
|
||||
keyval[key], err = querystring.ConvertStringToInt(val)
|
||||
case state.IBS_BatteryVoltage:
|
||||
keyval[key], err = strconv.ParseFloat(val, 64)
|
||||
|
||||
// updated timestamps
|
||||
case dtts.timestampKey(state.BMS_PwrBattRmngCpSOC), dtts.timestampKey(state.ICC_TotMilg_ODO), dtts.timestampKey(state.VCU_DCChrgRmngTi), dtts.timestampKey(state.BMS_RmChrgTi_TrgtSoC), dtts.timestampKey(state.IBS_BatteryVoltage),
|
||||
dtts.timestampKey(state.BMS_RmChrgTi_FullChrg), dtts.timestampKey(state.BCM_PwrMod), dtts.timestampKey(state.PWC_ChrgSts):
|
||||
keyval[key], err = dtts.timestampVal(value)
|
||||
}
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
Reference in New Issue
Block a user