Initial cloud-services repo - gateway service + pkg modules
This commit is contained in:
247
pkg/flashpackversion/flashpack_version_update.go
Normal file
247
pkg/flashpackversion/flashpack_version_update.go
Normal file
@@ -0,0 +1,247 @@
|
||||
package flashpackversion
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"fiskerinc.com/modules/common"
|
||||
"fiskerinc.com/modules/db/queries"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
// Any time we insert new CarECUs, we want to see if the flashpack number for the car has changed
|
||||
// If so, we save it on the car, send it to SAP, and update the Version Log.
|
||||
func InsertCarECUsAndUpdateFlashpackVersion(cars queries.CarsInterface, cvl queries.CarVersionsLogInterface, vin string, ecus []common.CarECU) error {
|
||||
// Convert the ecu names to cloud compatible ecus
|
||||
for _, e := range ecus {
|
||||
e.TransformECUName()
|
||||
}
|
||||
|
||||
// Insert the new CarECUs
|
||||
err := cars.InsertCarECUs(ecus)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var previousFlashpackNumber, currentFlashpackNumber string
|
||||
|
||||
// Find the previous flashpack version of the car
|
||||
car, err := cars.SelectByVIN(vin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
previousFlashpackNumber = car.Flashpack
|
||||
|
||||
// Find the current flashpack number of the car
|
||||
currentFlashpackNumber, err = FindCurrentFlashpackVersionForCar(cars, *car)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If the flashpack has changed, update the car and SAP
|
||||
return updateFlashpackNumber(cars, cvl, vin, currentFlashpackNumber, previousFlashpackNumber)
|
||||
}
|
||||
|
||||
// Given the latest ECUs for a car, find out what the flashpack number for the car is
|
||||
func FindCurrentFlashpackVersionForCar(cars queries.CarsInterface, car common.Car) (currentFlashpackNumber string, err error) {
|
||||
currentFlashpackNumber = ""
|
||||
|
||||
// Get a map of key-value pairs, latest updated ECUs for the car, ECU names as keys and ECU Supplier SW Versions as values
|
||||
latestCarECUsToVersionsMap, err := getLatestCarECUsMap(cars, car.VIN)
|
||||
if err != nil {
|
||||
return currentFlashpackNumber, err
|
||||
}
|
||||
|
||||
// Get a map of all flashpack version mappings, and an array of the flashpack numbers sorted in descending numerical order
|
||||
flashpackMappings, flashpackNumbers, err := getAllFlashpackMappingsByModelTrimMap(cars, car.Model, car.Trim)
|
||||
if err != nil {
|
||||
return currentFlashpackNumber, err
|
||||
}
|
||||
|
||||
// Here is the logic to calculate the current flashpack number for the car
|
||||
// More than one version for an ECU may map to the same flashpack number
|
||||
// A car may be several versions ahead on one ECU, and several versions behind on another ECU
|
||||
// Hence the following ugly code
|
||||
|
||||
// For each flashpack number in descending order
|
||||
var ecusUpdated []string
|
||||
for _, flashpackNumber := range flashpackNumbers {
|
||||
|
||||
var flashpackNumberComplete = true
|
||||
|
||||
outer: // For each ECU version mapping for the flashpack number
|
||||
for _, flashpackMapping := range flashpackMappings[flashpackNumber] {
|
||||
var ecuUpdated = false
|
||||
|
||||
inner: // Determine whether this ECU is updated on the car to this flashpack number or a higher one
|
||||
for _, e := range ecusUpdated {
|
||||
if e == flashpackMapping.ECU {
|
||||
ecuUpdated = true
|
||||
break inner
|
||||
}
|
||||
}
|
||||
|
||||
if !ecuUpdated {
|
||||
ecuVersion, ok := latestCarECUsToVersionsMap[flashpackMapping.ECU]
|
||||
|
||||
// If the ECU has not been updated at all, or it is not at this version, then the flashpack number is incomplete and the car has not achieved it
|
||||
if !ok || ecuVersion != flashpackMapping.SupplierSWVersion {
|
||||
flashpackNumberComplete = false
|
||||
break outer
|
||||
} else {
|
||||
// We have proved that this ECU is updated on the car to this flashpack number
|
||||
ecusUpdated = append(ecusUpdated, flashpackMapping.ECU)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if flashpackNumberComplete && flashpackNumber > currentFlashpackNumber {
|
||||
currentFlashpackNumber = flashpackNumber
|
||||
}
|
||||
}
|
||||
|
||||
return currentFlashpackNumber, err
|
||||
}
|
||||
|
||||
func FindCarECUsToUpdateForNextFlashpackNumber(cars queries.CarsInterface, car common.Car, nextFlashpackNumber string) ([]common.CarECUVersion, error) {
|
||||
ecusToUpdateForNextFlashpackNumber := []common.CarECUVersion{}
|
||||
|
||||
// Get a map of key-value pairs, latest updated ECUs for the car, ECU names as keys and ECU Supplier SW Versions as values
|
||||
latestCarECUsToVersionsMap, err := getLatestCarECUsMap(cars, car.VIN)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get a map of all flashpack version mappings, and an array of the flashpack numbers sorted in descending numerical order
|
||||
allFlashpackMappings, allFlashpackNumbers, err := getAllFlashpackMappingsByModelTrimMap(cars, car.Model, car.Trim)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get the mappings specifically for the next flashpack version
|
||||
nextFlashpackVersionMappings := allFlashpackMappings[nextFlashpackNumber]
|
||||
|
||||
// Here is the logic to find which ECUs on the car need to be updated in order to achieve nextFlashpackNumber
|
||||
// A car may be several versions ahead on one ECU, and several versions behind on another ECU
|
||||
// Hence the following ugly code
|
||||
|
||||
// For each mapping for the next flashpack version
|
||||
for _, nextFlashpackVersionMapping := range nextFlashpackVersionMappings {
|
||||
ecuVersion, ok := latestCarECUsToVersionsMap[nextFlashpackVersionMapping.ECU]
|
||||
|
||||
if !ok {
|
||||
// If the ECU has not been updated at all on this car, we know it needs to be updated to achieve the next flashpack version
|
||||
ecusToUpdateForNextFlashpackNumber = append(ecusToUpdateForNextFlashpackNumber, common.CarECUVersion{
|
||||
CarECUName: nextFlashpackVersionMapping.ECU,
|
||||
CarECUVersion: nextFlashpackVersionMapping.SupplierSWVersion,
|
||||
})
|
||||
} else {
|
||||
// If the ECU has been updated, we need to determine whether its version applies to this flashpack number or a higher one
|
||||
// That means we need to check all the mappings for the next flashpack number and all higher ones as well
|
||||
|
||||
found := false
|
||||
|
||||
outer: // For each flashpack number in descending order
|
||||
for _, flashpackNumber := range allFlashpackNumbers {
|
||||
|
||||
inner: // For each ECU mapping for this flashpack number
|
||||
for _, carECU := range allFlashpackMappings[flashpackNumber] {
|
||||
if ecuVersion == carECU.SupplierSWVersion {
|
||||
found = true
|
||||
break inner
|
||||
}
|
||||
}
|
||||
|
||||
// Don't check flashpack numbers lower than the next flashpack number
|
||||
if flashpackNumber == nextFlashpackNumber {
|
||||
break outer
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
// We have proved that the ECU has not been updated for the next flashpack number
|
||||
ecusToUpdateForNextFlashpackNumber = append(ecusToUpdateForNextFlashpackNumber, common.CarECUVersion{
|
||||
CarECUName: nextFlashpackVersionMapping.ECU,
|
||||
CarECUVersion: nextFlashpackVersionMapping.SupplierSWVersion,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ecusToUpdateForNextFlashpackNumber, err
|
||||
}
|
||||
|
||||
// Save the flashpack number in the database, display it in the version log, and send it to SAP
|
||||
func updateFlashpackNumber(cars queries.CarsInterface, cvl queries.CarVersionsLogInterface, vin, currentFlashpack string, previousFlashpack string) error {
|
||||
if currentFlashpack != previousFlashpack {
|
||||
_, err := cars.UpdateCarFlashpackVersion(vin, currentFlashpack)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
_, err = cvl.LogVersionChange(&common.CarVersionLogs{
|
||||
VIN: vin,
|
||||
VersionSource: common.FlashpackVersionSource,
|
||||
Version: currentFlashpack,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLatestCarECUsMap(cars queries.CarsInterface, vin string) (map[string]string, error) {
|
||||
allLatestCarECUs, err := cars.GetCarECUs(common.CarECUFilter{VIN: vin, Unique: true}, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Put into a map by ECU name
|
||||
var latestCarECUsToVersionsMap = make(map[string]string)
|
||||
for _, e := range allLatestCarECUs {
|
||||
latestCarECUsToVersionsMap[e.ECU] = e.SupplierSWVersion
|
||||
}
|
||||
|
||||
// Sometimes the TBOX version is applied to MCU, or the PDU version is applied to OBC, etc.
|
||||
for ecuName, ecuReplacementName := range common.FlashpackCalculationECUReplacement {
|
||||
_, hasECUName := latestCarECUsToVersionsMap[ecuName]
|
||||
_, hasECUReplacementName := latestCarECUsToVersionsMap[ecuReplacementName]
|
||||
if hasECUName && !hasECUReplacementName {
|
||||
latestCarECUsToVersionsMap[ecuReplacementName] = latestCarECUsToVersionsMap[ecuName]
|
||||
}
|
||||
}
|
||||
|
||||
return latestCarECUsToVersionsMap, nil
|
||||
}
|
||||
|
||||
func getAllFlashpackMappingsByModelTrimMap(cars queries.CarsInterface, model string, trim string) (map[string][]common.CarECU, []string, error) {
|
||||
allFlashpackMappingsByModelTrim, err := cars.GetCarFlashpackVersionMappingsByModelTrim(model, trim, &queries.PageQueryOptions{Order: "flashpack DESC"})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Map flashpack numbers to an array of ECU names and versions
|
||||
var flashpackMappings = make(map[string][]common.CarECU)
|
||||
for _, f := range allFlashpackMappingsByModelTrim {
|
||||
flashpackMappings[f.Flashpack] =
|
||||
append(flashpackMappings[f.Flashpack],
|
||||
common.CarECU{
|
||||
ECU: f.CarECUName,
|
||||
SupplierSWVersion: f.CarECUVersion,
|
||||
})
|
||||
}
|
||||
|
||||
// Sort the flashpack numbers in descending order
|
||||
var flashpackNumbers = maps.Keys(flashpackMappings)
|
||||
sort.Slice(flashpackNumbers, func(i, j int) bool {
|
||||
iFp, _ := strconv.ParseFloat(flashpackNumbers[i], 64) // guaranteed numeric
|
||||
jFp, _ := strconv.ParseFloat(flashpackNumbers[j], 64) // guaranteed numeric
|
||||
|
||||
return iFp > jFp
|
||||
})
|
||||
|
||||
return flashpackMappings, flashpackNumbers, nil
|
||||
}
|
||||
Reference in New Issue
Block a user