Files
cloud-services/pkg/common/updatemanifest_ecu.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
}
}