248 lines
8.9 KiB
Go
248 lines
8.9 KiB
Go
package flashpackversion
|
|
|
|
import (
|
|
"sort"
|
|
"strconv"
|
|
|
|
"github.com/fiskerinc/cloud-services/pkg/common"
|
|
"github.com/fiskerinc/cloud-services/pkg/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
|
|
}
|