Initial cloud-services repo - gateway service + pkg modules
This commit is contained in:
59
pkg/digitaltwin/cache.go
Normal file
59
pkg/digitaltwin/cache.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package digitaltwin
|
||||
|
||||
import (
|
||||
"fiskerinc.com/modules/cache"
|
||||
"fiskerinc.com/modules/logger"
|
||||
)
|
||||
|
||||
// var existsCount ExistsCount
|
||||
|
||||
type DigitalTwinCacheInterface interface {
|
||||
Exists(vin string, prop string, value interface{}) bool
|
||||
}
|
||||
|
||||
type DigitalTwinCache struct {
|
||||
ringMap *cache.RingMap
|
||||
// existsChan chan<- bool
|
||||
}
|
||||
|
||||
func NewDigitalTwinCache(capacity int) DigitalTwinCacheInterface {
|
||||
// existsChannel := make(chan bool, 100)
|
||||
// existsCount = ExistsCount{
|
||||
// Channel: existsChannel,
|
||||
// }
|
||||
// go existsCount.Run()
|
||||
return &DigitalTwinCache{
|
||||
ringMap: cache.NewRingMap(capacity),
|
||||
// existsChan: existsChannel,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DigitalTwinCache) Exists(vin string, prop string, value interface{}) bool {
|
||||
exists := d.ringMap.Exists(vin+prop, value)
|
||||
// If the channel is full, we just fall through
|
||||
// select {
|
||||
// case d.existsChan <- exists:
|
||||
// default:
|
||||
// }
|
||||
return exists
|
||||
}
|
||||
|
||||
type ExistsCount struct {
|
||||
Total int
|
||||
Exists int
|
||||
Channel <-chan bool
|
||||
}
|
||||
|
||||
func (ec *ExistsCount) Run() {
|
||||
for exists := range ec.Channel {
|
||||
ec.Total++
|
||||
if exists {
|
||||
ec.Exists += 1
|
||||
}
|
||||
if ec.Total >= 180000000 {
|
||||
logger.Error().Int("Cached Entries", ec.Exists).Int("Total Entries", ec.Total).Msg("Digital Twin Cache Results")
|
||||
ec.Total = 0
|
||||
ec.Exists = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
42
pkg/digitaltwin/cache_test.go
Normal file
42
pkg/digitaltwin/cache_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package digitaltwin_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"fiskerinc.com/modules/digitaltwin"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDigitalTwinCache(t *testing.T) {
|
||||
vin1 := "11111111111111111"
|
||||
vin2 := "22222222222222222"
|
||||
prop1 := "prop1"
|
||||
prop2 := "prop2"
|
||||
|
||||
cache := digitaltwin.NewDigitalTwinCache(3)
|
||||
|
||||
exists := cache.Exists(vin1, prop1, 1)
|
||||
assert.Equal(t, false, exists)
|
||||
|
||||
exists = cache.Exists(vin2, prop1, 1)
|
||||
assert.Equal(t, false, exists)
|
||||
|
||||
exists = cache.Exists(vin1, prop1, 1)
|
||||
assert.Equal(t, true, exists)
|
||||
|
||||
exists = cache.Exists(vin1, prop1, int64(2))
|
||||
assert.Equal(t, false, exists)
|
||||
|
||||
exists = cache.Exists(vin1, prop2, true)
|
||||
assert.Equal(t, false, exists)
|
||||
|
||||
exists = cache.Exists(vin2, prop2, true)
|
||||
assert.Equal(t, false, exists)
|
||||
|
||||
exists = cache.Exists(vin2, prop1, true)
|
||||
assert.Equal(t, false, exists)
|
||||
|
||||
exists = cache.Exists(vin1, prop1, int64(2))
|
||||
assert.Equal(t, false, exists)
|
||||
}
|
||||
218
pkg/digitaltwin/send.go
Normal file
218
pkg/digitaltwin/send.go
Normal file
@@ -0,0 +1,218 @@
|
||||
package digitaltwin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"fiskerinc.com/modules/cache"
|
||||
"fiskerinc.com/modules/common"
|
||||
"fiskerinc.com/modules/db/queries"
|
||||
"fiskerinc.com/modules/logger"
|
||||
"fiskerinc.com/modules/redis"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func NewSendDigitalTwin(redis redis.ClientPoolInterface, cars queries.CarsInterface) SendDigitalTwin {
|
||||
return SendDigitalTwin{
|
||||
redisClientPool: redis,
|
||||
carsDB: cars,
|
||||
}
|
||||
}
|
||||
|
||||
type SendDigitalTwin struct {
|
||||
redisClientPool redis.ClientPoolInterface
|
||||
carsDB queries.CarsInterface
|
||||
parser *cache.VehicleState
|
||||
}
|
||||
|
||||
func (d *SendDigitalTwin) Close() {
|
||||
d.carsDB = nil
|
||||
}
|
||||
|
||||
func (d *SendDigitalTwin) GetDigitalTwin(vin string) (*common.JSONDigitalTwin, error) {
|
||||
state, err := d.getVehicleState(vin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return CreateDigitalTwinResponse(vin, state), nil
|
||||
}
|
||||
|
||||
func CreateDigitalTwinResponse(vin string, state common.CarState) *common.JSONDigitalTwin {
|
||||
var battery *common.JSONBattery
|
||||
var windows *common.JSONWindows
|
||||
var climateControl *common.JSONClimateControl
|
||||
|
||||
battery = consolidateBattery(state)
|
||||
|
||||
// Consolidate windows
|
||||
if state.Windows != nil && state.MiscWindows != nil && state.Sunroof != nil {
|
||||
windows = &common.JSONWindows{}
|
||||
|
||||
// Main windows
|
||||
windows.LeftFront = state.Windows.LeftFront
|
||||
windows.LeftRear = state.Windows.LeftRear
|
||||
windows.RightFront = state.Windows.RightFront
|
||||
windows.RightRear = state.Windows.RightRear
|
||||
|
||||
// Miscellaneous windows
|
||||
windows.LeftRearQuarter = state.MiscWindows.LeftRearQuarter
|
||||
windows.RightRearQuarter = state.MiscWindows.RightRearQuarter
|
||||
windows.RearWindshield = state.MiscWindows.RearWindshield
|
||||
|
||||
// Sunroof
|
||||
windows.Sunroof = state.Sunroof.Sunroof
|
||||
}
|
||||
|
||||
// Consolidate climate control
|
||||
if state.CabinClimate != nil && state.RearDefrost != nil && state.DriverSeatHeat != nil &&
|
||||
state.PassengerSeatHeat != nil && state.SteeringWheelHeat != nil && state.AmbientTemperature != nil {
|
||||
climateControl = &common.JSONClimateControl{}
|
||||
|
||||
climateControl.CabinTemperature = state.CabinClimate.CabinTemperature
|
||||
climateControl.RearDefrost = state.RearDefrost.On
|
||||
climateControl.DriverSeatHeat = state.DriverSeatHeat.Level
|
||||
climateControl.PassengerSeatHeat = state.PassengerSeatHeat.Level
|
||||
climateControl.SteeringWheelHeat = state.SteeringWheelHeat.On
|
||||
climateControl.AmbientTemperature = state.AmbientTemperature.Temperature
|
||||
climateControl.InternalTemperature = state.CabinClimate.InternalTemperature
|
||||
}
|
||||
|
||||
twinForDrivers := &common.JSONDigitalTwin{
|
||||
VIN: vin,
|
||||
Online: state.Online,
|
||||
OnlineHMI: state.OnlineHMI,
|
||||
VehicleSpeed: state.VehicleSpeed,
|
||||
Gear: state.Gear,
|
||||
Battery: battery,
|
||||
Doors: state.Doors,
|
||||
Location: state.Location,
|
||||
Locks: state.Locks,
|
||||
Windows: windows,
|
||||
ClimateControl: climateControl,
|
||||
TRexVersion: state.TRexVersion,
|
||||
IP: state.IP,
|
||||
VehicleReadyState: state.VehicleReadyState,
|
||||
ExpandedSignals: state.ExpandedSignals,
|
||||
UpdatedAt: state.UpdatedAt,
|
||||
}
|
||||
|
||||
return twinForDrivers
|
||||
}
|
||||
|
||||
func consolidateBattery(state common.CarState) *common.JSONBattery {
|
||||
battery := common.JSONBattery{}
|
||||
if state.StateOfCharge != nil {
|
||||
battery.StateOfCharge = &state.StateOfCharge.Usable
|
||||
}
|
||||
if state.Battery != nil {
|
||||
battery.Percent = &state.Battery.Percent
|
||||
battery.TotalMileageOdometer = &state.Battery.TotalMileageOdometer
|
||||
}
|
||||
if state.MaxRange != nil {
|
||||
battery.MaxMiles = &state.MaxRange.MaxMiles
|
||||
}
|
||||
if state.VCU0x260 != nil {
|
||||
battery.ChargeType = &state.VCU0x260.ChargeType
|
||||
}
|
||||
if state.ChargingMetrics != nil {
|
||||
battery.RemainingChargingTime = &state.ChargingMetrics.RemainingChargingTime
|
||||
}
|
||||
if state.ChargingMetrics != nil {
|
||||
battery.RemainingChargingTimeFull = &state.ChargingMetrics.RemainingChargingTimeFull
|
||||
}
|
||||
if state.CellTemperature != nil {
|
||||
battery.AvgCellTemperature = &state.CellTemperature.AvgBatteryTemp
|
||||
}
|
||||
|
||||
if battery == (common.JSONBattery{}) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &battery
|
||||
}
|
||||
|
||||
func (d *SendDigitalTwin) sendToDriver(twin *common.JSONDigitalTwin, driver string) error {
|
||||
dt, err := json.Marshal(twin)
|
||||
if err != nil {
|
||||
logger.Error().Err(errors.WithStack(err)).Send()
|
||||
}
|
||||
sdt := string(dt)
|
||||
logger.Debug().Msg(sdt)
|
||||
|
||||
logger.Info().Interface("digital twin", twin).Msg("send to driver add to redis")
|
||||
client := d.redisClientPool.GetFromPool()
|
||||
defer client.Close()
|
||||
err = client.SafePublishMessage(
|
||||
common.Mobile.Key(driver),
|
||||
common.Message{
|
||||
Handler: "digital_twin",
|
||||
Data: twin,
|
||||
},
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *SendDigitalTwin) SendToDriver(vin string, driver string) error {
|
||||
ok, err := d.verifyCarToDriver(vin, driver)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !ok {
|
||||
return cache.ErrInvalidCarToDriverAssociation(vin, driver)
|
||||
}
|
||||
|
||||
twin, err := d.GetDigitalTwin(vin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return d.sendToDriver(twin, driver)
|
||||
}
|
||||
|
||||
func (d *SendDigitalTwin) Send(vin string) error {
|
||||
// Get the digital twin
|
||||
redisDigitalTwin, err := d.GetDigitalTwin(vin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
drivers, err := d.retrieveDriverIDs(vin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(drivers) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if redisDigitalTwin != nil {
|
||||
for _, driver := range drivers {
|
||||
|
||||
err = d.sendToDriver(redisDigitalTwin, driver)
|
||||
if err != nil {
|
||||
logger.Error().Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SendDigitalTwin) getVehicleState(vin string) (common.CarState, error) {
|
||||
return d.getParser().Get(vin)
|
||||
}
|
||||
|
||||
func (d *SendDigitalTwin) verifyCarToDriver(vin string, driver string) (bool, error) {
|
||||
return cache.VerifyCarToDriver(d.redisClientPool, d.carsDB, vin, driver)
|
||||
}
|
||||
|
||||
func (d *SendDigitalTwin) retrieveDriverIDs(vin string) ([]string, error) {
|
||||
drivers := cache.NewDriversCache(d.redisClientPool, d.carsDB)
|
||||
|
||||
return drivers.RetrieveDriverIDs(vin)
|
||||
}
|
||||
|
||||
func (d *SendDigitalTwin) getParser() *cache.VehicleState {
|
||||
if d.parser == nil {
|
||||
d.parser = cache.NewVehicleState(d.redisClientPool)
|
||||
}
|
||||
|
||||
return d.parser
|
||||
}
|
||||
139
pkg/digitaltwin/send_test.go
Normal file
139
pkg/digitaltwin/send_test.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package digitaltwin_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"fiskerinc.com/modules/common"
|
||||
"fiskerinc.com/modules/duration"
|
||||
)
|
||||
|
||||
func clone(orig *common.JSONDigitalTwin) (*common.JSONDigitalTwin, error) {
|
||||
origJSON, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clone := common.JSONDigitalTwin{}
|
||||
if err = json.Unmarshal(origJSON, &clone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &clone, nil
|
||||
}
|
||||
|
||||
func cloneAndChangeOnline(orig *common.JSONDigitalTwin) (*common.JSONDigitalTwin, error) {
|
||||
cloned, err := clone(orig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cloned.Online = !cloned.Online
|
||||
return cloned, nil
|
||||
}
|
||||
|
||||
func cloneAndChangeHMIOnline(orig *common.JSONDigitalTwin) (*common.JSONDigitalTwin, error) {
|
||||
cloned, err := clone(orig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cloned.OnlineHMI = !cloned.OnlineHMI
|
||||
return cloned, nil
|
||||
}
|
||||
|
||||
func cloneAndChangeVehicleSpeed(orig *common.JSONDigitalTwin) (*common.JSONDigitalTwin, error) {
|
||||
cloned, err := clone(orig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cloned.VehicleSpeed.Speed = cloned.VehicleSpeed.Speed + 10.0
|
||||
return cloned, nil
|
||||
}
|
||||
|
||||
func cloneAndChangeGear(orig *common.JSONDigitalTwin) (*common.JSONDigitalTwin, error) {
|
||||
cloned, err := clone(orig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cloned.Gear.InPark = !cloned.Gear.InPark
|
||||
return cloned, nil
|
||||
}
|
||||
|
||||
func cloneAndChangeBattery(orig *common.JSONDigitalTwin) (*common.JSONDigitalTwin, error) {
|
||||
cloned, err := clone(orig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
*cloned.Battery.Percent = *cloned.Battery.Percent - 10.0
|
||||
return cloned, nil
|
||||
}
|
||||
|
||||
func cloneAndChangeDoor(orig *common.JSONDigitalTwin) (*common.JSONDigitalTwin, error) {
|
||||
cloned, err := clone(orig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cloned.Doors.LeftFront = !cloned.Doors.LeftFront
|
||||
return cloned, nil
|
||||
}
|
||||
|
||||
func cloneAndChangeLocation(orig *common.JSONDigitalTwin) (*common.JSONDigitalTwin, error) {
|
||||
cloned, err := clone(orig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cloned.Location.Altitude = cloned.Location.Altitude + 100.0
|
||||
return cloned, nil
|
||||
}
|
||||
|
||||
func cloneAndChangeLocks(orig *common.JSONDigitalTwin) (*common.JSONDigitalTwin, error) {
|
||||
cloned, err := clone(orig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cloned.Locks.Driver = !cloned.Locks.Driver
|
||||
return cloned, nil
|
||||
}
|
||||
|
||||
func cloneAndChangeWindows(orig *common.JSONDigitalTwin) (*common.JSONDigitalTwin, error) {
|
||||
cloned, err := clone(orig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cloned.Windows.LeftFront = cloned.Windows.LeftFront + 1
|
||||
return cloned, nil
|
||||
}
|
||||
|
||||
func cloneAndChangeClimateControl(orig *common.JSONDigitalTwin) (*common.JSONDigitalTwin, error) {
|
||||
cloned, err := clone(orig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cloned.ClimateControl.AmbientTemperature = cloned.ClimateControl.AmbientTemperature + 1
|
||||
return cloned, nil
|
||||
}
|
||||
|
||||
func cloneAndChangeTrexVersion(orig *common.JSONDigitalTwin) (*common.JSONDigitalTwin, error) {
|
||||
cloned, err := clone(orig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cloned.TRexVersion = cloned.TRexVersion + "new_version"
|
||||
return cloned, nil
|
||||
}
|
||||
|
||||
func cloneAndChangeIP(orig *common.JSONDigitalTwin) (*common.JSONDigitalTwin, error) {
|
||||
cloned, err := clone(orig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cloned.IP = "172.0.0.99"
|
||||
return cloned, nil
|
||||
}
|
||||
|
||||
func cloneAndUpdateAt(orig *common.JSONDigitalTwin) (*common.JSONDigitalTwin, error) {
|
||||
cloned, err := clone(orig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
*cloned.UpdatedAt = cloned.UpdatedAt.Add(-1 * duration.Minute)
|
||||
return cloned, nil
|
||||
}
|
||||
212
pkg/digitaltwin/sendv2.go
Normal file
212
pkg/digitaltwin/sendv2.go
Normal file
@@ -0,0 +1,212 @@
|
||||
package digitaltwin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
cache "fiskerinc.com/modules/cachev2"
|
||||
"fiskerinc.com/modules/common"
|
||||
"fiskerinc.com/modules/db/queries"
|
||||
"fiskerinc.com/modules/logger"
|
||||
redis "fiskerinc.com/modules/redisv2"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func NewSendDigitalTwinV2(redisClient redis.ClientInterface, cars queries.CarsInterface) SendDigitalTwinV2 {
|
||||
return SendDigitalTwinV2{
|
||||
redisClient: redisClient,
|
||||
carsDB: cars,
|
||||
}
|
||||
}
|
||||
|
||||
type SendDigitalTwinV2 struct {
|
||||
redisClient redis.ClientInterface
|
||||
carsDB queries.CarsInterface
|
||||
parser *cache.VehicleState
|
||||
}
|
||||
|
||||
func (d *SendDigitalTwinV2) Close() {
|
||||
d.carsDB = nil
|
||||
}
|
||||
|
||||
func (d *SendDigitalTwinV2) GetDigitalTwin(vin string) (*common.JSONDigitalTwin, error) {
|
||||
state, err := d.getVehicleState(vin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var battery *common.JSONBattery
|
||||
var windows *common.JSONWindows
|
||||
var climateControl *common.JSONClimateControl
|
||||
|
||||
battery = consolidateBatteryV2(state)
|
||||
|
||||
// Consolidate windows
|
||||
if state.Windows != nil && state.MiscWindows != nil && state.Sunroof != nil {
|
||||
windows = &common.JSONWindows{}
|
||||
|
||||
// Main windows
|
||||
windows.LeftFront = state.Windows.LeftFront
|
||||
windows.LeftRear = state.Windows.LeftRear
|
||||
windows.RightFront = state.Windows.RightFront
|
||||
windows.RightRear = state.Windows.RightRear
|
||||
|
||||
// Miscellaneous windows
|
||||
windows.LeftRearQuarter = state.MiscWindows.LeftRearQuarter
|
||||
windows.RightRearQuarter = state.MiscWindows.RightRearQuarter
|
||||
windows.RearWindshield = state.MiscWindows.RearWindshield
|
||||
|
||||
// Sunroof
|
||||
windows.Sunroof = state.Sunroof.Sunroof
|
||||
}
|
||||
|
||||
// Consolidate climate control
|
||||
if state.CabinClimate != nil && state.RearDefrost != nil && state.DriverSeatHeat != nil &&
|
||||
state.PassengerSeatHeat != nil && state.SteeringWheelHeat != nil && state.AmbientTemperature != nil {
|
||||
climateControl = &common.JSONClimateControl{}
|
||||
|
||||
climateControl.CabinTemperature = state.CabinClimate.CabinTemperature
|
||||
climateControl.RearDefrost = state.RearDefrost.On
|
||||
climateControl.DriverSeatHeat = state.DriverSeatHeat.Level
|
||||
climateControl.PassengerSeatHeat = state.PassengerSeatHeat.Level
|
||||
climateControl.SteeringWheelHeat = state.SteeringWheelHeat.On
|
||||
climateControl.AmbientTemperature = state.AmbientTemperature.Temperature
|
||||
climateControl.InternalTemperature = state.CabinClimate.InternalTemperature
|
||||
}
|
||||
|
||||
twinForDrivers := &common.JSONDigitalTwin{
|
||||
VIN: vin,
|
||||
Online: state.Online,
|
||||
OnlineHMI: state.OnlineHMI,
|
||||
VehicleSpeed: state.VehicleSpeed,
|
||||
Gear: state.Gear,
|
||||
Battery: battery,
|
||||
Doors: state.Doors,
|
||||
Location: state.Location,
|
||||
Locks: state.Locks,
|
||||
Windows: windows,
|
||||
ClimateControl: climateControl,
|
||||
TRexVersion: state.TRexVersion,
|
||||
IP: state.IP,
|
||||
VehicleReadyState: state.VehicleReadyState,
|
||||
ExpandedSignals: state.ExpandedSignals,
|
||||
UpdatedAt: state.UpdatedAt,
|
||||
}
|
||||
|
||||
return twinForDrivers, nil
|
||||
}
|
||||
|
||||
func consolidateBatteryV2(state common.CarState) *common.JSONBattery {
|
||||
battery := common.JSONBattery{}
|
||||
if state.StateOfCharge != nil {
|
||||
battery.StateOfCharge = &state.StateOfCharge.Usable
|
||||
}
|
||||
if state.Battery != nil {
|
||||
battery.Percent = &state.Battery.Percent
|
||||
battery.TotalMileageOdometer = &state.Battery.TotalMileageOdometer
|
||||
}
|
||||
if state.MaxRange != nil {
|
||||
battery.MaxMiles = &state.MaxRange.MaxMiles
|
||||
}
|
||||
if state.VCU0x260 != nil {
|
||||
battery.ChargeType = &state.VCU0x260.ChargeType
|
||||
}
|
||||
if state.ChargingMetrics != nil {
|
||||
battery.RemainingChargingTime = &state.ChargingMetrics.RemainingChargingTime
|
||||
}
|
||||
if state.ChargingMetrics != nil {
|
||||
battery.RemainingChargingTimeFull = &state.ChargingMetrics.RemainingChargingTimeFull
|
||||
}
|
||||
if state.CellTemperature != nil {
|
||||
battery.AvgCellTemperature = &state.CellTemperature.AvgBatteryTemp
|
||||
}
|
||||
|
||||
if battery == (common.JSONBattery{}) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &battery
|
||||
}
|
||||
|
||||
func (d *SendDigitalTwinV2) sendToDriver(twin *common.JSONDigitalTwin, driver string) error {
|
||||
dt, err := json.Marshal(twin)
|
||||
if err != nil {
|
||||
logger.Error().Err(errors.WithStack(err)).Send()
|
||||
}
|
||||
sdt := string(dt)
|
||||
logger.Debug().Msg(sdt)
|
||||
|
||||
logger.Info().Interface("digital twin", twin).Msg("send to driver add to redis")
|
||||
err = d.redisClient.SafePublishMessage(
|
||||
common.Mobile.Key(driver),
|
||||
common.Message{
|
||||
Handler: "digital_twin",
|
||||
Data: twin,
|
||||
},
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *SendDigitalTwinV2) SendToDriver(vin string, driver string) error {
|
||||
ok, err := d.verifyCarToDriver(vin, driver)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !ok {
|
||||
return cache.ErrInvalidCarToDriverAssociation(vin, driver)
|
||||
}
|
||||
|
||||
twin, err := d.GetDigitalTwin(vin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return d.sendToDriver(twin, driver)
|
||||
}
|
||||
|
||||
func (d *SendDigitalTwinV2) Send(vin string) error {
|
||||
// Get the digital twin
|
||||
redisDigitalTwin, err := d.GetDigitalTwin(vin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
drivers, err := d.retrieveDriverIDs(vin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(drivers) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if redisDigitalTwin != nil {
|
||||
for _, driver := range drivers {
|
||||
|
||||
err = d.sendToDriver(redisDigitalTwin, driver)
|
||||
if err != nil {
|
||||
logger.Error().Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SendDigitalTwinV2) getVehicleState(vin string) (common.CarState, error) {
|
||||
return d.getParser().Get(vin)
|
||||
}
|
||||
|
||||
func (d *SendDigitalTwinV2) verifyCarToDriver(vin string, driver string) (bool, error) {
|
||||
return cache.VerifyCarToDriver(d.redisClient, d.carsDB, vin, driver)
|
||||
}
|
||||
|
||||
func (d *SendDigitalTwinV2) retrieveDriverIDs(vin string) ([]string, error) {
|
||||
drivers := cache.NewDriversCache(d.redisClient, d.carsDB)
|
||||
|
||||
return drivers.RetrieveDriverIDs(vin)
|
||||
}
|
||||
|
||||
func (d *SendDigitalTwinV2) getParser() *cache.VehicleState {
|
||||
if d.parser == nil {
|
||||
d.parser = cache.NewVehicleState(d.redisClient)
|
||||
}
|
||||
|
||||
return d.parser
|
||||
}
|
||||
Reference in New Issue
Block a user