352 lines
10 KiB
Go
352 lines
10 KiB
Go
package common
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"sort"
|
|
"strconv"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel"
|
|
"github.com/fiskerinc/cloud-services/pkg/vindecoder"
|
|
)
|
|
|
|
// Any changes need to be made to modules_go/db/queries/updatemanifests.go ECURollback() on getRollbacksOld
|
|
type UpdateManifestECU struct {
|
|
ID int64 `json:"id,omitempty" pg:",pk"`
|
|
UpdateManifestID int64 `json:"manifest_id,omitempty" pg:",unique:updatemanifestid_ecu_partnumber" validate:"required,gte=0"`
|
|
ECU string `json:"name,omitempty" pg:",unique:updatemanifestid_ecu_partnumber" validate:"required,min=2,max=10"`
|
|
Version string `json:"version" validate:"required,max=255"`
|
|
CurrentVersion string `json:"current_version,omitempty" pg:"-"`
|
|
HWVersion string `json:"hw_version,omitempty" pg:"-"`
|
|
HWVersions []string `json:"hw_versions,omitempty" pg:"hw_versions,array"`
|
|
ConfigurationMask string `json:"configuration_mask,omitempty" validate:"max=5000"`
|
|
Configuration string `json:"configuration,omitempty" validate:"max=5000"`
|
|
SelfDownload bool `json:"self_download,omitempty"`
|
|
Mode string `json:"mode,omitempty" validate:"required,max=4"`
|
|
Files []*UpdateManifestFile `json:"files,omitempty" pg:"rel:has-many"`
|
|
Rollback []*UpdateManifestECU `json:"rollback,omitempty" pg:"-"`
|
|
ECCKeys *ECCKeys `json:"ecc_keys,omitempty" pg:"-"`
|
|
InstallPriority int `json:"install_priority,omitempty" pg:"install_priority"`
|
|
dbbasemodel.DBModelBase
|
|
}
|
|
|
|
// Scrub cleans data for sending to car
|
|
// clearHWVersion: Determines wether or not the hw versions field should be cleared, by default should be true. Need this so the front end can see
|
|
// since scrub is called
|
|
func (ume *UpdateManifestECU) Scrub(ecuType Device, clearHWVersion bool) {
|
|
// As a final catch all incase the transform was not called before sending the manifest
|
|
ume.TransformECUName()
|
|
|
|
ume.ID = 0
|
|
ume.Mode = ""
|
|
ume.UpdateManifestID = 0
|
|
ume.CreatedAt = nil
|
|
ume.UpdatedAt = nil
|
|
|
|
if clearHWVersion {
|
|
if ume.HWVersion == "" && len(ume.HWVersions) > 0 {
|
|
ume.HWVersion = ume.HWVersions[0]
|
|
}
|
|
ume.HWVersions = nil
|
|
}
|
|
|
|
if ecuType == TRex && ume.SelfDownload {
|
|
ume.Files = make([]*UpdateManifestFile, 0)
|
|
}
|
|
|
|
ume.scrubKeys(ecuType)
|
|
|
|
ume.SortFiles()
|
|
ume.ImageSignatureChecksumCheck()
|
|
|
|
for _, file := range ume.Files {
|
|
file.Scrub()
|
|
}
|
|
|
|
for _, ecu := range ume.Rollback {
|
|
ecu.RollbackScrub(ecuType)
|
|
}
|
|
|
|
ume.InstallPriority = 0
|
|
}
|
|
|
|
func (ume *UpdateManifestECU) scrubKeys(ecu Device) {
|
|
if ume.ECCKeys == nil || ecu == HMI || (ecu == TRex && ume.SelfDownload) {
|
|
ume.ECCKeys = nil
|
|
return
|
|
}
|
|
|
|
keys := ume.ECCKeys
|
|
keys.ScrubForManifest()
|
|
}
|
|
|
|
func (ume *UpdateManifestECU) RollbackScrub(ecuType Device) {
|
|
ume.ECU = ""
|
|
ume.Mode = ""
|
|
ume.HWVersion = ""
|
|
ume.HWVersions = []string{}
|
|
ume.ECCKeys = nil
|
|
ume.Scrub(ecuType, true)
|
|
}
|
|
|
|
func (ume *UpdateManifestECU) SortFiles() {
|
|
sort.Slice(ume.Files, func(i int, j int) bool {
|
|
order1 := ume.Files[i].FileOrder
|
|
order2 := ume.Files[j].FileOrder
|
|
|
|
if order1 == order2 {
|
|
return ume.sortFileByType(i, j)
|
|
}
|
|
|
|
return order1 < order2
|
|
})
|
|
}
|
|
|
|
func (ume *UpdateManifestECU) sortFileByType(i int, j int) bool {
|
|
order1 := fileSortOrder[ume.Files[i].FileType]
|
|
order2 := fileSortOrder[ume.Files[j].FileType]
|
|
|
|
return order1 < order2
|
|
}
|
|
|
|
// Given a set of files, figure out subset which are s19/hex files that are split. Order them, and remove the checksum
|
|
// Then have the signature only on the last file
|
|
func (ume *UpdateManifestECU) ImageSignatureChecksumCheck() (err error) {
|
|
// Ideally the remove parsed and remove original will have already been called
|
|
type FileIndexStruct struct {
|
|
FileName string
|
|
// Parsed *bool // Cant use a pointer as it index's on pointer value
|
|
HasParsed bool // True if the file has true or false for parsed
|
|
Parsed bool // True if HasParsed and Parsed
|
|
}
|
|
// First need to find the file names
|
|
filesMap := make(map[FileIndexStruct][]*UpdateManifestFile, 0)
|
|
|
|
// Prepare our files for sorting by putting them into a map
|
|
for _, file := range ume.Files {
|
|
if file.Signature != "" {
|
|
file.Checksum = ""
|
|
}
|
|
fis := FileIndexStruct{}
|
|
fis.FileName = file.Filename
|
|
if file.Parsed == nil {
|
|
fis.HasParsed = false
|
|
fis.Parsed = false
|
|
} else if !*file.Parsed {
|
|
fis.HasParsed = true
|
|
fis.Parsed = false
|
|
} else {
|
|
fis.HasParsed = true
|
|
fis.Parsed = true
|
|
// Need to parse the file name now, so we can match them up. It's okay if we have the parsed s19's with the bin at the end as they all will
|
|
regexExp := regexp.MustCompile(`(\S*.(?:s19|hex))_(\d+).bin`)
|
|
// Returns [ fullFileName.19_5.bin fullFileName.19 19]
|
|
matches := regexExp.FindStringSubmatch(file.Filename)
|
|
// Like a parsed file should always follow this file name format, but maybe someones files is in whacky form and has extra characters in it
|
|
if len(matches) != 3 {
|
|
return errors.New("Length of array was not 3")
|
|
}
|
|
fis.FileName = matches[1]
|
|
file.FileOrder, err = strconv.Atoi(matches[2])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
// Add the file to its array
|
|
filesArray, ok := filesMap[fis]
|
|
if !ok {
|
|
filesArray = make([]*UpdateManifestFile, 0)
|
|
}
|
|
filesArray = append(filesArray, file)
|
|
filesMap[fis] = filesArray
|
|
}
|
|
|
|
// So this is going to be an array or array of files, so that we can run the sort by file type thing
|
|
filesArrayArray := make([][]*UpdateManifestFile, 0)
|
|
// Now we will sort our files, I really just care about the parsed ones right now.
|
|
for index, filesArray := range filesMap {
|
|
// If we are parsed files, then we need to order them, and then remove checksum field for just a single signature
|
|
if index.HasParsed && index.Parsed {
|
|
//Sort them in 0,1,2,3... order
|
|
sort.Slice(filesArray, func(i, j int) bool {
|
|
// This function is less f[i] < f[j]
|
|
return filesArray[i].FileOrder < filesArray[j].FileOrder
|
|
})
|
|
|
|
// Check first if there is a signature, then remove all the checksums if there is
|
|
// If there is checksums and no signature, we can ignore
|
|
|
|
if filesArray[0].Signature != "" {
|
|
// If there is a signature, only the last one gets it
|
|
for x := 0; x < len(filesArray)-1; x++ {
|
|
// Remove the checksum and signature from all but the last element
|
|
filesArray[x].Checksum = ""
|
|
filesArray[x].Signature = ""
|
|
}
|
|
filesArray[len(filesArray)-1].Checksum = ""
|
|
}
|
|
|
|
}
|
|
|
|
// Now we add our sorted files back in
|
|
// ume.Files = append(ume.Files, filesArray...)
|
|
// Going to maintain our sorted files file type
|
|
filesArrayArray = append(filesArrayArray, filesArray)
|
|
}
|
|
|
|
sort.Slice(filesArrayArray, func(i, j int) bool {
|
|
order1 := fileSortOrder[filesArrayArray[i][0].FileType]
|
|
order2 := fileSortOrder[filesArrayArray[j][0].FileType]
|
|
|
|
return order1 < order2
|
|
})
|
|
|
|
ume.Files = make([]*UpdateManifestFile, 0)
|
|
for _, filesArray := range filesArrayArray {
|
|
ume.Files = append(ume.Files, filesArray...)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (ume *UpdateManifestECU) String() string {
|
|
return fmt.Sprintf("UpdateManifestECU<%s %s %s>", ume.ECU, ume.Version, ume.HWVersions)
|
|
}
|
|
|
|
func (ume *UpdateManifestECU) GetFileIDs() []string {
|
|
ids := make([]string, len(ume.Files))
|
|
|
|
for i := range ume.Files {
|
|
ids[i] = ume.Files[i].FileID
|
|
}
|
|
|
|
return ids
|
|
}
|
|
|
|
// 3 Cases for parsed: nil: original not s19. false: original s19, true: parsed s19
|
|
// Removes files from the list that are the parsed reference to the uploaded files
|
|
// For aftersales
|
|
func (ume *UpdateManifestECU) RemoveParsedS19HexFiles() {
|
|
i := 0
|
|
for _, file := range ume.Files {
|
|
// This if is for keeping the file, if false its removed
|
|
if file.Parsed == nil || !*file.Parsed {
|
|
ume.Files[i] = file
|
|
i++
|
|
}
|
|
}
|
|
ume.Files = ume.Files[:i]
|
|
}
|
|
|
|
// Removes files from the list that are the original reference to the uploaded file
|
|
// For attendent
|
|
func (ume *UpdateManifestECU) RemoveOriginalS19HexFiles() {
|
|
i := 0
|
|
for _, file := range ume.Files {
|
|
if file.Parsed == nil || *file.Parsed {
|
|
ume.Files[i] = file
|
|
i++
|
|
}
|
|
}
|
|
ume.Files = ume.Files[:i]
|
|
}
|
|
|
|
// Removes files from the list that are not s19 or hex file related. So files that have null for parsed
|
|
func (ume *UpdateManifestECU) RemovedNonS19HexFiles() {
|
|
i := 0
|
|
for _, file := range ume.Files {
|
|
if file.Parsed == nil {
|
|
ume.Files[i] = file
|
|
i++
|
|
}
|
|
}
|
|
ume.Files = ume.Files[:i]
|
|
}
|
|
|
|
// For aftersales
|
|
func (ume *UpdateManifestECU) RemoveParsedS19HexFilesRollbacks() {
|
|
for _, ecu := range ume.Rollback {
|
|
ecu.RemoveParsedS19HexFiles()
|
|
}
|
|
}
|
|
|
|
// For attendent
|
|
func (ume *UpdateManifestECU) RemoveOriginalS19HexFilesRollbacks() {
|
|
for _, ecu := range ume.Rollback {
|
|
ecu.RemoveOriginalS19HexFiles()
|
|
}
|
|
|
|
}
|
|
|
|
func (ume *UpdateManifestECU) ToUpdateConfigManifestECU() (ucme UpdateConfigManifestECU) {
|
|
ucme = UpdateConfigManifestECU{
|
|
ECU: ume.ECU,
|
|
Configuration: ume.Configuration,
|
|
}
|
|
return
|
|
}
|
|
|
|
// In case we need to rename a ecu with files and data before sending it to a car.
|
|
// Also rename its roll backs. This really does need to happen before GetCDS is fired off
|
|
// I do not want to transform the ECU on import. These renames could change or could hopefully go away
|
|
func (ume *UpdateManifestECU) TransformECUName() {
|
|
replacement, ok := ECUReplacement()[ume.ECU]
|
|
if ok {
|
|
ume.ECU = replacement
|
|
}
|
|
|
|
if ume.ECCKeys != nil {
|
|
ume.ECCKeys.TransformECUName()
|
|
}
|
|
|
|
if ume.Rollback != nil {
|
|
for _, rollbackECU := range ume.Rollback {
|
|
rollbackECU.TransformECUName()
|
|
}
|
|
}
|
|
}
|
|
|
|
// Given a vin, extract its trim level
|
|
func (um *UpdateManifestECU) FilterECUFiles(filter ECUFileFilter) {
|
|
offset := 0
|
|
for _, file := range um.Files {
|
|
if file.IsFileCompatible(filter) {
|
|
um.Files[offset] = file
|
|
offset++
|
|
}
|
|
}
|
|
um.Files = um.Files[:offset]
|
|
}
|
|
|
|
type ECUFileFilter struct {
|
|
Trim CompatibleTrim
|
|
DriveSide CompatibleDriveSide
|
|
}
|
|
|
|
func vinTrimToFileTrim(vinTrim string) (fileTrim CompatibleTrim, ok bool) {
|
|
ok = true
|
|
switch vinTrim {
|
|
case vindecoder.TRIM_SPORT:
|
|
return SPORT, ok
|
|
case vindecoder.TRIM_ULTRA:
|
|
return ULTRA, ok
|
|
case vindecoder.TRIM_EXTREME:
|
|
return EXTREME, ok
|
|
case vindecoder.TRIM_OCEAN_ONE:
|
|
return EXTREME, ok
|
|
}
|
|
ok = false
|
|
return
|
|
}
|
|
|
|
func vodDriveSideToFileDriveSide(side string) (compatibleDriveSide CompatibleDriveSide, ok bool) {
|
|
switch side {
|
|
case "RIGHT":
|
|
return RIGHT_HAND_DRIVE, true
|
|
case "LEFT":
|
|
return LEFT_HAND_DRIVE, true
|
|
default:
|
|
return "INVALID", false
|
|
}
|
|
}
|