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 } }