Add depot, attendant, jetfire, optimus, ota services with kustomize overlays
This commit is contained in:
193
services/ota_update_go/background/carImmobilizer.go
Normal file
193
services/ota_update_go/background/carImmobilizer.go
Normal file
@@ -0,0 +1,193 @@
|
||||
package background
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"maps"
|
||||
"otaupdate/services"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fiskerinc/cloud-services/pkg/cache"
|
||||
"github.com/fiskerinc/cloud-services/pkg/carcommand"
|
||||
"github.com/fiskerinc/cloud-services/pkg/common"
|
||||
"github.com/fiskerinc/cloud-services/pkg/logger"
|
||||
"github.com/fiskerinc/cloud-services/pkg/redis"
|
||||
)
|
||||
|
||||
// Anywhere a comment says lock, its probably actually lock/unlock
|
||||
|
||||
// Have a local list we check and run against
|
||||
// and have a redis cache list that we will modify
|
||||
// keep track of time started, last updated time, number of times sent, and if its a lock or unlock
|
||||
|
||||
var (
|
||||
immobilizerOnce sync.Once
|
||||
immobilizer *Immobilizer
|
||||
)
|
||||
|
||||
// Message will stay alive in redis for an hour before it is removed
|
||||
const CAR_CHECK_INTERVAL time.Duration = time.Duration(5 * time.Minute) // Minutes between checking car status and sending command
|
||||
const REDIS_UPDATE_INTERVAL time.Duration = time.Duration(time.Hour * 2) // Minutes between updating redis with the current cache list. // need to keep track of last updated incase our service goes down
|
||||
const REDIS_EXPIRATION_DURATION time.Duration = time.Duration(24 * time.Hour) // how old a time should be before we pick it up for us to process
|
||||
|
||||
const CAR_LOCK_ATTEMPT_COUNT = 15 // how many times to send the lock command
|
||||
|
||||
type CarTrack struct {
|
||||
TimesModified int `json:"times_modified"`
|
||||
Immobilize bool `json:"immobilize"` // 0: unlock, 1: lock
|
||||
}
|
||||
|
||||
type Immobilizer struct {
|
||||
VINs map[string]*CarTrack // By having a pointer here, should be able to modify timesModified without write lock
|
||||
sync.RWMutex // mutex for handling the reading and writing of the VIN's map
|
||||
}
|
||||
|
||||
func GetImmobilizer() *Immobilizer {
|
||||
immobilizerOnce.Do(func() {
|
||||
if immobilizer != nil {
|
||||
return
|
||||
}
|
||||
logger.Info().Msg("Init Immobilizer instance")
|
||||
immobilizer = InitiateImmobilizer()
|
||||
})
|
||||
return immobilizer
|
||||
}
|
||||
|
||||
func InitiateImmobilizer() *Immobilizer {
|
||||
imm := &Immobilizer{}
|
||||
imm.VINs = make(map[string]*CarTrack)
|
||||
go time.AfterFunc(CAR_CHECK_INTERVAL, imm.CheckCars)
|
||||
// go time.AfterFunc(REDIS_UPDATE_INTERVAL, imm.UpdateRedis)
|
||||
// Have a random offset so multiple ota's starting at same time don't look and claim at the same time
|
||||
// go time.AfterFunc(REDIS_EXPIRATION_DURATION+(time.Duration(rand.IntN(20))*time.Minute), imm.ClaimRedis)
|
||||
return imm
|
||||
}
|
||||
|
||||
|
||||
func (imm *Immobilizer) GetVINList() (vinList []string) {
|
||||
imm.RLock()
|
||||
defer imm.RUnlock()
|
||||
for vin := range imm.VINs {
|
||||
vinList = append(vinList, vin)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (imm *Immobilizer) CheckCars() {
|
||||
clientPool := services.RedisClientPool()
|
||||
twins, _ := cache.GetVINListDigitalTwin(imm.GetVINList(), clientPool)
|
||||
parkedVINs := []string{} // parkedVINs are the cars we are going to send the lock command to
|
||||
for vin, twin := range twins {
|
||||
gear := twin.Gear
|
||||
if gear == nil {
|
||||
continue
|
||||
}
|
||||
if gear.InPark {
|
||||
parkedVINs = append(parkedVINs, vin)
|
||||
}
|
||||
}
|
||||
// Have a list of vins we can now modify
|
||||
// Send the lock/unlock command, send wake up command
|
||||
hopefulImmob := imm.sendRemoteCommands(parkedVINs)
|
||||
// Not on hopeful immob, we do not check if the car ever wakes up from its sms, so its possible if parked deep in a garage it will not
|
||||
// Possible fix: remove redis timeout for car lock commands
|
||||
go imm.RemoveVINs(hopefulImmob)
|
||||
time.AfterFunc(CAR_CHECK_INTERVAL, imm.CheckCars)
|
||||
}
|
||||
|
||||
func (imm *Immobilizer) sendRemoteCommands(parkedVINs []string) (removableVins []string) {
|
||||
// First send wake up message
|
||||
|
||||
// Send lock or unlock command
|
||||
|
||||
// for _, vin := range request.VINs {
|
||||
// Action logger should get added to when the user adds car to the list
|
||||
// go func() {
|
||||
// actionLog := actionlogger.ActionLog{
|
||||
// VIN: vin,
|
||||
// Action: actionlogger.RemoteCommand,
|
||||
// UserIdentifier: httphandlers.GetClientID(r),
|
||||
// CallLocation: "github.com/fiskerinc/cloud-services/services/ota_update_go/handlers/vehicle_command.go",
|
||||
// Description: string(description),
|
||||
// }
|
||||
// err = alDB.Insert(actionLog)
|
||||
// if err != nil {
|
||||
// logger.Err(err).Msg("failed to insert action log inside HandleVehicleCommand")
|
||||
// }
|
||||
// }()
|
||||
// vehicle_command.go has an extremely convoluted way to get remote commands to car. It pushed it to a kafka queue to to be picked up
|
||||
// then kafka does some delivery re-trying stuff in some way, wakes up the car and then puts the message into redis.
|
||||
// we are going straight to the redis section
|
||||
|
||||
// remoteCommands := make([]string, 0, len(parkedVINs))
|
||||
imm.RLock()
|
||||
batch := redis.NewRedisBatchCommands()
|
||||
smsClient := services.GetSMSClient()
|
||||
wake := carcommand.NewCarWakeUp(services.GetDB().GetCars(), smsClient)
|
||||
for _, vin := range parkedVINs {
|
||||
temp := imm.VINs[vin]
|
||||
temp.TimesModified -= 1
|
||||
if temp.TimesModified < 0 {
|
||||
removableVins = append(removableVins, vin)
|
||||
continue
|
||||
}
|
||||
// probably faster to create the list of things to push and batch, but fine for now
|
||||
msg := common.RemoteCommandSource{}
|
||||
if temp.Immobilize {
|
||||
msg.Command = "doors_lock"
|
||||
} else {
|
||||
msg.Command = "doors_unlock"
|
||||
}
|
||||
// SafeQueueMessage auto deletes after one hour, which I do not want with this lock command, rather it sat around indefinitely
|
||||
data, _ := json.Marshal(common.Message{
|
||||
Handler: "remote_command",
|
||||
Data: msg,
|
||||
})
|
||||
batch.Add("RPUSH", redis.QueueKey(common.TRex.Key(vin)), data)
|
||||
|
||||
// try to wake up car
|
||||
wake.WakeUp(vin, false)
|
||||
}
|
||||
imm.RUnlock()
|
||||
|
||||
redisClient := services.RedisClientPool().GetFromPool()
|
||||
defer redisClient.Close()
|
||||
_, err := redisClient.ExecuteBatch(batch)
|
||||
if err != nil {
|
||||
logger.Err(err).Msg("failed to push car immobilizer commands to redis")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (imm *Immobilizer) RemoveVINs(vins []string) {
|
||||
imm.Lock()
|
||||
defer imm.Unlock()
|
||||
for _, v := range vins {
|
||||
delete(imm.VINs, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (imm *Immobilizer) AddVINs(vins []string, immobilize bool) {
|
||||
imm.Lock()
|
||||
defer imm.Unlock()
|
||||
for _, v := range vins {
|
||||
imm.VINs[v] = &CarTrack{TimesModified: CAR_LOCK_ATTEMPT_COUNT, Immobilize: immobilize}
|
||||
}
|
||||
}
|
||||
|
||||
// Just returns the information about the vins we are currently tracking
|
||||
// not sure about memory safety on this
|
||||
func (imm *Immobilizer) GetVINInformation() (vinInfo map[string]*CarTrack) {
|
||||
imm.RLock()
|
||||
defer imm.RUnlock()
|
||||
vinInfo = maps.Clone(imm.VINs)
|
||||
return
|
||||
}
|
||||
|
||||
func (imm *Immobilizer) UpdateRedis() {
|
||||
|
||||
}
|
||||
|
||||
func (imm *Immobilizer) ClaimRedis() {
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user