Initial cloud-services repo - gateway service + pkg modules
This commit is contained in:
235
pkg/immobilizer/immobilizerditto/immoDitto.go
Normal file
235
pkg/immobilizer/immobilizerditto/immoDitto.go
Normal file
@@ -0,0 +1,235 @@
|
||||
package immobilizerditto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"fiskerinc.com/modules/common"
|
||||
"fiskerinc.com/modules/dbc/state"
|
||||
"fiskerinc.com/modules/immobilizer/immobilizershared"
|
||||
"fiskerinc.com/modules/logger"
|
||||
"fiskerinc.com/modules/redisv2"
|
||||
)
|
||||
|
||||
// I should put these parked values as a constant somewhere TODO
|
||||
|
||||
func (id *ImmobilizeDitto) VCUGearSig(vin string, newState int) {
|
||||
//0 "Undefined_initial_value" 1 "gear_P" 2 "gear_N" 3 "R_gear" 4 "D_gear" 5 "Reserved" 6 "gear_E" 7 "gear_S" 8 "Reserved" 9 "Reserved" 10 "Reserved" 11 "Reserved" 12 "Reserved" 13 "Reserved" 14 "Reserved" 15 "Reserved" ;
|
||||
id.WatchListSync.RLock()
|
||||
track, ok := id.WatchList[vin]
|
||||
id.WatchListSync.RUnlock()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
switch track.State {
|
||||
case immobilizershared.STATE_IMMOBILIZED:
|
||||
// If we are immobilized, but we are out of park, thats an issue
|
||||
if newState == 1 || newState == 2 {
|
||||
// Car is parked, and possible already immobilized, but we are going to enforce in case we were wrong
|
||||
// id.sendLockCommand(vin)
|
||||
} else if newState > 2 {
|
||||
logger.Error().Str("VIN", vin).Int("New Gear", newState).Str("MSG", "Marked as Immobilized, but received driving gear").Msg("VCUGearSig")
|
||||
go alertAmericanLeaseMovingVehicle(vin)
|
||||
}
|
||||
case immobilizershared.STATE_IMMOBILIZING:
|
||||
if newState == 1 || newState == 2 {
|
||||
// Car is parked, send that lock command. We know the car is awake, so no need to send a wake up sms
|
||||
id.sendLockCommand(vin)
|
||||
}
|
||||
case immobilizershared.STATE_MOBILIZING:
|
||||
if newState > 2 {
|
||||
// unsure if we should accept this as done
|
||||
immobilizershared.UpdateCarMobilized(vin, id.RedisClient)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (id *ImmobilizeDitto) sendLockCommand(vin string) {
|
||||
msg := common.RemoteCommandSource{}
|
||||
msg.Command = "doors_lock"
|
||||
|
||||
// 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,
|
||||
})
|
||||
res := id.RedisClient.RPush(context.Background(), redisv2.QueueKey(common.TRex.Key(vin)), data)
|
||||
err := res.Err()
|
||||
if err != nil {
|
||||
logger.Err(err).Msg("Failed to send lock command")
|
||||
}
|
||||
}
|
||||
|
||||
func alertAmericanLeaseMovingVehicle(vin string) {
|
||||
url := "https://mobile.americanlease.net/api/v1/fisker/command/failure"
|
||||
token := "Bearer f3d795d3-5325-42d7-8dd8-ccb416c52ae2"
|
||||
|
||||
type AmericanLeaseBody struct {
|
||||
VIN string `json:"vin"`
|
||||
UTCMobileAt time.Time `json:"utcMobileAt"` //"utcMobileAt":"2025-03-07T09:00:00",
|
||||
UUID string `json:"uuid"` //"uuid": "db6e407d-791b-4d00-a742-da856a42c4ba"
|
||||
}
|
||||
|
||||
body := AmericanLeaseBody{
|
||||
VIN: vin,
|
||||
UTCMobileAt: time.Now().UTC(),
|
||||
UUID: "00000000-0000-0000-0000-000000000000",
|
||||
}
|
||||
|
||||
b, _ := json.Marshal(body)
|
||||
|
||||
// Create the HTTP request
|
||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(b))
|
||||
if err != nil {
|
||||
logger.Err(err).Msg("failed to make request to american lease")
|
||||
return
|
||||
}
|
||||
|
||||
// Set headers
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", token)
|
||||
|
||||
// Execute the request
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
logger.Err(err).Msg("failed to do request to american lease")
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 300 {
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
logger.Error().Str("response body", string(respBody)).Str("response header", resp.Status).Msg("failed to contact american lease about moving car")
|
||||
}
|
||||
}
|
||||
|
||||
// So we got the VCU Immo sts, most likely a new value compared to what was had before
|
||||
func (id *ImmobilizeDitto) VCUIMMOSts(vin string, newState string) {
|
||||
// Possible values for newState
|
||||
// "Immo_Active",
|
||||
// "Immo_Inactive",
|
||||
// "Reserved",
|
||||
// "Invalid",
|
||||
// Check if we are tracking this vehicle
|
||||
id.WatchListSync.RLock()
|
||||
track, ok := id.WatchList[vin]
|
||||
id.WatchListSync.RUnlock()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debug().Str("New State", newState).Str("Tracked", track.State.String()).Str("VIN", vin).Msg("VCUIMMOSts signal")
|
||||
switch track.State {
|
||||
case immobilizershared.STATE_IMMOBILIZED:
|
||||
// Car is currently marked as immobilized
|
||||
if newState == "Immo_Inactive" {
|
||||
// This is an issue, we have the car as being immobilized, but it just got moving. Now this could possible be a timing issue between a different ditto
|
||||
// If this actually happens, we need to set the vehicle to Immobilizing
|
||||
logger.Error().Str("VIN", vin).Str("MSG", "Marked as Immobilized, but received Immo_Inactive").Msg("Ditto Immobilizer")
|
||||
}
|
||||
// if newState == "Immo_Active" {
|
||||
// // This is can be ignored
|
||||
// }
|
||||
case immobilizershared.STATE_IMMOBILIZING:
|
||||
// We are trying to immobilize the vehicle
|
||||
if newState == "Immo_Active" {
|
||||
immobilizershared.UpdateCarImmobilized(vin, id.RedisClient)
|
||||
// Send lock command some more
|
||||
id.sendLockCommand(vin)
|
||||
}
|
||||
case immobilizershared.STATE_MOBILIZING:
|
||||
// Hey great, we know that the car is now active
|
||||
if newState == "Immo_Inactive" {
|
||||
immobilizershared.UpdateCarMobilized(vin, id.RedisClient)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (id *ImmobilizeDitto) PKC_KeyStsMod(vin string, newState string) {
|
||||
id.WatchListSync.RLock()
|
||||
track, ok := id.WatchList[vin]
|
||||
id.WatchListSync.RUnlock()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debug().Str("New State", newState).Str("Tracked", track.State.String()).Str("VIN", vin).Msg("PKC_KeyStsMode signal")
|
||||
switch track.State {
|
||||
case immobilizershared.STATE_IMMOBILIZED:
|
||||
// Car is currently marked as immobilized
|
||||
if newState == state.PKC_KeyStsMod_disabled {
|
||||
// we thought the car was immobilized, but we just got a signal saying its been mobilized
|
||||
logger.Error().Str("VIN", vin).Str("MSG", "Marked as Immobilized, but received PKC_KeyStsMod: disabled").Msg("Ditto Immobilizer")
|
||||
}
|
||||
case immobilizershared.STATE_IMMOBILIZING:
|
||||
// We were trying to immobilize, and we got confirmation that it is
|
||||
if newState == state.PKC_KeyStsMod_enabled {
|
||||
immobilizershared.UpdateCarImmobilized(vin, id.RedisClient)
|
||||
}
|
||||
case immobilizershared.STATE_MOBILIZING:
|
||||
// trying to get car moving, and got confirmation it is
|
||||
if newState == state.PKC_KeyStsMod_disabled {
|
||||
immobilizershared.UpdateCarMobilized(vin, id.RedisClient)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ongoing process, needs to be run in da background
|
||||
func (id *ImmobilizeDitto) ListenToRedisChanges() {
|
||||
subscription := id.RedisClient.Subscribe(context.Background(), immobilizershared.IMMOBILIZER_PUBSUB_CHANNEL)
|
||||
subChannel := subscription.Channel()
|
||||
for msg := range subChannel {
|
||||
rim := immobilizershared.RedisImmobilizerMsg{}
|
||||
err := json.Unmarshal([]byte(msg.Payload), &rim)
|
||||
if err != nil {
|
||||
logger.Err(err).Str("Payload", msg.Payload).Msg("ListenToRedisChanges: Failed to parse payload")
|
||||
continue
|
||||
}
|
||||
id.WatchListSync.Lock()
|
||||
if rim.State == immobilizershared.STATE_MOBILIZED {
|
||||
delete(id.WatchList, rim.VIN)
|
||||
} else {
|
||||
id.WatchList[rim.VIN] = immobilizershared.ImmobilizeTrack{State: rim.State}
|
||||
}
|
||||
id.WatchListSync.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// Read in redis to get out initial state
|
||||
func (id *ImmobilizeDitto) SeedLocalData() {
|
||||
res, err := immobilizershared.ReadVehicleStatuses(id.RedisClient)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
id.WatchListSync.Lock()
|
||||
id.WatchList = res
|
||||
id.WatchListSync.Unlock()
|
||||
}
|
||||
|
||||
// Don't want to become de-synced, so will seed once an hour
|
||||
func (id *ImmobilizeDitto) SeedHourly() {
|
||||
id.SeedLocalData()
|
||||
time.AfterFunc(time.Hour, id.SeedHourly)
|
||||
}
|
||||
|
||||
func InitImmobilizerDitto(connection *redisv2.Connection) (id *ImmobilizeDitto) {
|
||||
id = &ImmobilizeDitto{}
|
||||
id.RedisClient = connection
|
||||
go id.SeedHourly()
|
||||
go id.ListenToRedisChanges()
|
||||
return id
|
||||
}
|
||||
|
||||
// Wether a car is to start mobilizing or immobilizing is a decision by the server, not from a cars messaging
|
||||
type ImmobilizeDitto struct {
|
||||
RedisClient *redisv2.Connection
|
||||
WatchList map[string]immobilizershared.ImmobilizeTrack
|
||||
WatchListSync sync.RWMutex
|
||||
}
|
||||
Reference in New Issue
Block a user