From 3a6ae09cac15e0de509606c94ee91cbeb9355b03 Mon Sep 17 00:00:00 2001 From: FelixTing Date: Tue, 7 Nov 2023 16:57:45 +0800 Subject: [PATCH] PS-547 Add reuseable function for parsing DBC file to Device and DeviceProfile Signed-off-by: FelixTing --- central/dbc/compile.go | 212 ++++++++++++++++++++++++++++++++++ central/dbc/constants.go | 24 ++++ central/dbc/dbc_sample.dbc | 60 ++++++++++ central/dbc/transform.go | 148 ++++++++++++++++++++++++ central/dbc/transform_test.go | 124 ++++++++++++++++++++ go.mod | 4 + go.sum | 24 +++- 7 files changed, 595 insertions(+), 1 deletion(-) create mode 100644 central/dbc/compile.go create mode 100644 central/dbc/constants.go create mode 100644 central/dbc/dbc_sample.dbc create mode 100644 central/dbc/transform.go create mode 100644 central/dbc/transform_test.go diff --git a/central/dbc/compile.go b/central/dbc/compile.go new file mode 100644 index 00000000..c5257e42 --- /dev/null +++ b/central/dbc/compile.go @@ -0,0 +1,212 @@ +// +// This compile.go was copied from https://github.com/einride/can-go because it is not exported. +// +// SPDX-License-Identifier: Apache-2.0 + +package dbc + +import ( + "fmt" + "sort" + "time" + + "go.einride.tech/can/pkg/dbc" + "go.einride.tech/can/pkg/descriptor" +) + +type CompileResult struct { + Database *descriptor.Database + Warnings []error +} + +func Compile(sourceFile string, data []byte) (result *CompileResult, err error) { + p := dbc.NewParser(sourceFile, data) + if err := p.Parse(); err != nil { + return nil, fmt.Errorf("failed to parse DBC source file: %w", err) + } + defs := p.Defs() + c := &compiler{ + db: &descriptor.Database{}, + defs: defs, + } + c.collectDescriptors() + c.addMetadata() + c.sortDescriptors() + return &CompileResult{Database: c.db, Warnings: c.warnings}, nil +} + +type compileError struct { + def dbc.Def + reason string +} + +func (e *compileError) Error() string { + return fmt.Sprintf("failed to Compile: %v (%v)", e.reason, e.def) +} + +type compiler struct { + db *descriptor.Database + defs []dbc.Def + warnings []error +} + +func (c *compiler) addWarning(warning error) { + c.warnings = append(c.warnings, warning) +} + +func (c *compiler) collectDescriptors() { + for _, def := range c.defs { + switch def := def.(type) { + case *dbc.VersionDef: + c.db.Version = def.Version + case *dbc.MessageDef: + if def.MessageID == dbc.IndependentSignalsMessageID { + continue // don't Compile + } + message := &descriptor.Message{ + Name: string(def.Name), + ID: def.MessageID.ToCAN(), + IsExtended: def.MessageID.IsExtended(), + Length: uint8(def.Size), + SenderNode: string(def.Transmitter), + } + for _, signalDef := range def.Signals { + signal := &descriptor.Signal{ + Name: string(signalDef.Name), + IsBigEndian: signalDef.IsBigEndian, + IsSigned: signalDef.IsSigned, + IsMultiplexer: signalDef.IsMultiplexerSwitch, + IsMultiplexed: signalDef.IsMultiplexed, + MultiplexerValue: uint(signalDef.MultiplexerSwitch), + Start: uint8(signalDef.StartBit), + Length: uint8(signalDef.Size), + Scale: signalDef.Factor, + Offset: signalDef.Offset, + Min: signalDef.Minimum, + Max: signalDef.Maximum, + Unit: signalDef.Unit, + } + for _, receiver := range signalDef.Receivers { + signal.ReceiverNodes = append(signal.ReceiverNodes, string(receiver)) + } + message.Signals = append(message.Signals, signal) + } + c.db.Messages = append(c.db.Messages, message) + case *dbc.NodesDef: + for _, node := range def.NodeNames { + c.db.Nodes = append(c.db.Nodes, &descriptor.Node{Name: string(node)}) + } + } + } +} + +func (c *compiler) addMetadata() { + for _, def := range c.defs { + switch def := def.(type) { + case *dbc.CommentDef: + switch def.ObjectType { + case dbc.ObjectTypeMessage: + if def.MessageID == dbc.IndependentSignalsMessageID { + continue // don't Compile + } + message, ok := c.db.Message(def.MessageID.ToCAN()) + if !ok { + c.addWarning(&compileError{def: def, reason: "no declared message"}) + continue + } + message.Description = def.Comment + case dbc.ObjectTypeSignal: + if def.MessageID == dbc.IndependentSignalsMessageID { + continue // don't Compile + } + signal, ok := c.db.Signal(def.MessageID.ToCAN(), string(def.SignalName)) + if !ok { + c.addWarning(&compileError{def: def, reason: "no declared signal"}) + continue + } + signal.Description = def.Comment + case dbc.ObjectTypeNetworkNode: + node, ok := c.db.Node(string(def.NodeName)) + if !ok { + c.addWarning(&compileError{def: def, reason: "no declared node"}) + continue + } + node.Description = def.Comment + } + case *dbc.ValueDescriptionsDef: + if def.MessageID == dbc.IndependentSignalsMessageID { + continue // don't Compile + } + if def.ObjectType != dbc.ObjectTypeSignal { + continue // don't Compile + } + signal, ok := c.db.Signal(def.MessageID.ToCAN(), string(def.SignalName)) + if !ok { + c.addWarning(&compileError{def: def, reason: "no declared signal"}) + continue + } + for _, valueDescription := range def.ValueDescriptions { + signal.ValueDescriptions = append(signal.ValueDescriptions, &descriptor.ValueDescription{ + Description: valueDescription.Description, + Value: int64(valueDescription.Value), + }) + } + case *dbc.AttributeValueForObjectDef: + switch def.ObjectType { + case dbc.ObjectTypeMessage: + msg, ok := c.db.Message(def.MessageID.ToCAN()) + if !ok { + c.addWarning(&compileError{def: def, reason: "no declared message"}) + continue + } + switch def.AttributeName { + case "GenMsgSendType": + if err := msg.SendType.UnmarshalString(def.StringValue); err != nil { + c.addWarning(&compileError{def: def, reason: err.Error()}) + continue + } + case "GenMsgCycleTime": + msg.CycleTime = time.Duration(def.IntValue) * time.Millisecond + case "GenMsgDelayTime": + msg.DelayTime = time.Duration(def.IntValue) * time.Millisecond + } + case dbc.ObjectTypeSignal: + sig, ok := c.db.Signal(def.MessageID.ToCAN(), string(def.SignalName)) + if !ok { + c.addWarning(&compileError{def: def, reason: "no declared signal"}) + } + if def.AttributeName == "GenSigStartValue" { + sig.DefaultValue = int(def.IntValue) + } + } + } + } +} + +func (c *compiler) sortDescriptors() { + // Sort nodes by name + sort.Slice(c.db.Nodes, func(i, j int) bool { + return c.db.Nodes[i].Name < c.db.Nodes[j].Name + }) + // Sort messages by ID + sort.Slice(c.db.Messages, func(i, j int) bool { + return c.db.Messages[i].ID < c.db.Messages[j].ID + }) + for _, m := range c.db.Messages { + m := m + // Sort signals by start (and multiplexer value) + sort.Slice(m.Signals, func(j, k int) bool { + if m.Signals[j].MultiplexerValue < m.Signals[k].MultiplexerValue { + return true + } + return m.Signals[j].Start < m.Signals[k].Start + }) + // Sort value descriptions by value + for _, s := range m.Signals { + s := s + sort.Slice(s.ValueDescriptions, func(k, l int) bool { + return s.ValueDescriptions[k].Value < s.ValueDescriptions[l].Value + }) + } + } +} diff --git a/central/dbc/constants.go b/central/dbc/constants.go new file mode 100644 index 00000000..e413b339 --- /dev/null +++ b/central/dbc/constants.go @@ -0,0 +1,24 @@ +// +// Copyright (C) 2023 IOTech Ltd +// +// SPDX-License-Identifier: Apache-2.0 + +package dbc + +const ( + Canbus = "CANbus" + J1939 = "J1939" + Network = "network" + Standard = "standard" + ID = "ID" + DataSize = "dataSize" + Sender = "sender" + + BitStart = "bitStart" + BitLen = "bitLen" + LittleEndian = "littleEndian" + ReceiverNames = "receiverNames" + MuxSignal = "muxSignal" + MuxNum = "muxNum" + IsSigned = "isSigned" +) diff --git a/central/dbc/dbc_sample.dbc b/central/dbc/dbc_sample.dbc new file mode 100644 index 00000000..0414c35e --- /dev/null +++ b/central/dbc/dbc_sample.dbc @@ -0,0 +1,60 @@ +VERSION "" + + +NS_ : + NS_DESC_ + CM_ + BA_DEF_ + BA_ + VAL_ + CAT_DEF_ + CAT_ + FILTER + BA_DEF_DEF_ + EV_DATA_ + ENVVAR_DATA_ + SGTYPE_ + SGTYPE_VAL_ + BA_DEF_SGTYPE_ + BA_SGTYPE_ + SIG_TYPE_REF_ + VAL_TABLE_ + SIG_GROUP_ + SIG_VALTYPE_ + SIGTYPE_VALTYPE_ + BO_TX_BU_ + BA_DEF_REL_ + BA_REL_ + BA_DEF_DEF_REL_ + BU_SG_REL_ + BU_EV_REL_ + BU_BO_REL_ + SG_MUL_VAL_ + +BS_: + +BU_: + +BO_ 2364539902 EEC2: 8 Vector__XXX + SG_ Accelerator_Pedal_1_Low_Idle_Swi : 0|2@1+ (1,0) [0|3] "bit" Vector__XXX + + + +CM_ BO_ 2364539902 "Electronic Engine Controller 2"; +CM_ SG_ 2364539902 Accelerator_Pedal_1_Low_Idle_Swi "Switch signal which indicates the state of the accelerator pedal 1 low idle switch. The low idle switch is defined in SAE Recommended Practice J1843."; + + +BA_DEF_ SG_ "SPN" INT 0 524287; +BA_DEF_ SG_ "SystemSignalLongSymbol" STRING ; + +BA_DEF_DEF_ "SPN" 0; +BA_DEF_DEF_ "SystemSignalLongSymbol" ""; + +BA_ "SPN" SG_ 2364539902 Accelerator_Pedal_1_Low_Idle_Swi 558; +BA_ "SystemSignalLongSymbol" SG_ 2364539902 Accelerator_Pedal_1_Low_Idle_Swi "A1IS"; + + +VAL_ 2364539902 Accelerator_Pedal_1_Low_Idle_Swi 0 "Accelerator pedal 1 not in low idle condition" 1 "Accelerator pedal 1 in low idle condition" 2 "Error" 3 "Not available" ; + + + diff --git a/central/dbc/transform.go b/central/dbc/transform.go new file mode 100644 index 00000000..bc42f5d2 --- /dev/null +++ b/central/dbc/transform.go @@ -0,0 +1,148 @@ +// +// Copyright (C) 2023 IOTech Ltd +// +// SPDX-License-Identifier: Apache-2.0 + +package dbc + +import ( + "io" + "math" + "strconv" + + "github.com/edgexfoundry/go-mod-core-contracts/v2/common" + "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos" + "github.com/edgexfoundry/go-mod-core-contracts/v2/models" + + "go.einride.tech/can/pkg/descriptor" +) + +func valueType(s *descriptor.Signal) string { + _, offsetFrac := math.Modf(s.Offset) + _, scaleFrac := math.Modf(s.Scale) + if offsetFrac != 0 || scaleFrac != 0 { + return common.ValueTypeFloat64 + } + if s.IsSigned { + return common.ValueTypeInt64 + } else { + return common.ValueTypeUint64 + } +} + +func ConvertDBCtoProfile(file io.Reader) (profileDTOs []dtos.DeviceProfile, err error, validateErrors map[string]error) { + data, err := io.ReadAll(file) + if err != nil { + return + } + + compileResult, err := Compile("", data) + if err != nil { + return + } + + validateErrors = make(map[string]error, len(compileResult.Database.Messages)) + + for _, m := range compileResult.Database.Messages { + var deviceResources []dtos.DeviceResource + var deviceCommands []dtos.DeviceCommand + var profileDto dtos.DeviceProfile + for _, s := range m.Signals { + deviceResource := dtos.DeviceResource{ + Name: s.Name, + Description: s.Description, + Properties: dtos.ResourceProperties{ + ValueType: valueType(s), + ReadWrite: common.ReadWrite_R, + Units: s.Unit, + Minimum: strconv.FormatFloat(s.Min, 'f', -1, 64), + Maximum: strconv.FormatFloat(s.Max, 'f', -1, 64), + Scale: strconv.FormatFloat(s.Scale, 'f', -1, 64), + Offset: strconv.FormatFloat(s.Offset, 'f', -1, 64), + DefaultValue: strconv.FormatInt(int64(s.DefaultValue), 10), + }, + Attributes: map[string]interface{}{ + BitStart: s.Start, + BitLen: s.Length, + LittleEndian: !s.IsBigEndian, + ReceiverNames: s.ReceiverNodes, + MuxSignal: s.IsMultiplexer, + IsSigned: s.IsSigned, + }, + } + if s.IsMultiplexed { + deviceResource.Attributes[MuxNum] = s.MultiplexerValue + } + if len(s.ValueDescriptions) > 0 { + var deviceCommand dtos.DeviceCommand + deviceCommand.Name = s.Name + deviceCommand.ReadWrite = common.ReadWrite_R + mappings := make(map[string]string, len(s.ValueDescriptions)) + for _, valueDescription := range s.ValueDescriptions { + mappings[strconv.FormatInt(valueDescription.Value, 10)] = valueDescription.Description + } + deviceCommand.ResourceOperations = []dtos.ResourceOperation{ + { + DeviceResource: s.Name, + DefaultValue: strconv.FormatInt(int64(s.DefaultValue), 10), + Mappings: mappings, + }, + } + deviceCommands = append(deviceCommands, deviceCommand) + } + deviceResources = append(deviceResources, deviceResource) + } + profileDto.Name = m.Name + profileDto.Description = m.Description + profileDto.DeviceResources = deviceResources + profileDto.DeviceCommands = deviceCommands + + if validateErr := common.Validate(profileDto); validateErr != nil { + validateErrors[profileDto.Name] = validateErr + } else { + profileDTOs = append(profileDTOs, profileDto) + } + } + return +} + +func ConvertDBCtoDevice(file io.Reader, networkName, serviceName string) (deviceDTOs []dtos.Device, err error, validateErrors map[string]error) { + data, err := io.ReadAll(file) + if err != nil { + return + } + + compileResult, err := Compile("", data) + if err != nil { + return + } + + validateErrors = make(map[string]error, len(compileResult.Database.Messages)) + + for _, m := range compileResult.Database.Messages { + deviceDTO := dtos.Device{ + Name: m.Name, + Description: m.Description, + AdminState: models.Unlocked, + OperatingState: models.Up, + ProfileName: m.Name, + ServiceName: serviceName, + Protocols: map[string]dtos.ProtocolProperties{ + Canbus: { + Network: networkName, + Standard: J1939, + ID: strconv.Itoa(int(m.ID)), + DataSize: strconv.Itoa(int(m.Length)), + Sender: m.SenderNode, + }, + }, + } + validateErr := common.Validate(deviceDTO) + if validateErr != nil { + validateErrors[deviceDTO.Name] = validateErr + } else { + deviceDTOs = append(deviceDTOs, deviceDTO) + } + } + return +} diff --git a/central/dbc/transform_test.go b/central/dbc/transform_test.go new file mode 100644 index 00000000..c8b82967 --- /dev/null +++ b/central/dbc/transform_test.go @@ -0,0 +1,124 @@ +// +// Copyright (C) 2023 IOTech Ltd +// +// SPDX-License-Identifier: Apache-2.0 + +package dbc + +import ( + "os" + "reflect" + "testing" + + "github.com/edgexfoundry/go-mod-core-contracts/v2/common" + "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos" + "github.com/edgexfoundry/go-mod-core-contracts/v2/models" + + "github.com/stretchr/testify/require" +) + +func TestConvertDBCtoDevice(t *testing.T) { + networkName := "vcan0" + serviceName := "device-can" + + ioReader, err := os.Open("dbc_sample.dbc") + defer func() { + err := ioReader.Close() + require.NoError(t, err) + }() + require.NoError(t, err) + + deviceDTOs, err, _ := ConvertDBCtoDevice(ioReader, networkName, serviceName) + require.NoError(t, err) + require.NotEmpty(t, deviceDTOs) + + expectedDeviceDTO := dtos.Device{ + Name: "EEC2", + Description: "Electronic Engine Controller 2", + AdminState: models.Unlocked, + OperatingState: models.Up, + ProfileName: "EEC2", + ServiceName: serviceName, + Protocols: map[string]dtos.ProtocolProperties{ + Canbus: { + Network: networkName, + Standard: J1939, + ID: "217056254", + DataSize: "8", + Sender: "Vector__XXX", + }, + }, + } + require.EqualValues(t, expectedDeviceDTO, deviceDTOs[0], "Generated Device DTO doesn't match the expected value.") +} + +func TestConvertDBCtoProfile(t *testing.T) { + ioReader, err := os.Open("dbc_sample.dbc") + defer func() { + err := ioReader.Close() + require.NoError(t, err) + }() + require.NoError(t, err) + + profileDTOs, err, _ := ConvertDBCtoProfile(ioReader) + if err != nil { + t.Errorf("Expected no error, but got: %v", err) + } + + if len(profileDTOs) == 0 { + t.Errorf("Expected 1 DeviceProfile, but got 0") + } + + expectedProfileDTO := dtos.DeviceProfile{ + DeviceProfileBasicInfo: dtos.DeviceProfileBasicInfo{ + Name: "EEC2", + Description: "Electronic Engine Controller 2", + }, + DeviceResources: []dtos.DeviceResource{ + { + Name: "Accelerator_Pedal_1_Low_Idle_Swi", + Description: "Switch signal which indicates the state of the accelerator pedal 1 low idle switch. The low idle switch is defined in SAE Recommended Practice J1843.", + Properties: dtos.ResourceProperties{ + ValueType: common.ValueTypeUint64, + ReadWrite: common.ReadWrite_R, + Units: "bit", + Minimum: "0", + Maximum: "3", + Scale: "1", + Offset: "0", + DefaultValue: "0", + }, + Attributes: map[string]interface{}{ + BitStart: uint8(0), + BitLen: uint8(2), + LittleEndian: true, + ReceiverNames: []string{"Vector__XXX"}, + MuxSignal: false, + IsSigned: false, + }, + }, + }, + DeviceCommands: []dtos.DeviceCommand{ + { + Name: "Accelerator_Pedal_1_Low_Idle_Swi", + ReadWrite: common.ReadWrite_R, + ResourceOperations: []dtos.ResourceOperation{ + { + DeviceResource: "Accelerator_Pedal_1_Low_Idle_Swi", + DefaultValue: "0", + Mappings: map[string]string{ + "0": "Accelerator pedal 1 not in low idle condition", + "1": "Accelerator pedal 1 in low idle condition", + "2": "Error", + "3": "Not available", + }, + }, + }, + }, + }, + } + + if !reflect.DeepEqual(profileDTOs[0], expectedProfileDTO) { + t.Errorf("Generated DeviceProfile DTO doesn't match the expected value.") + } +} diff --git a/go.mod b/go.mod index 5628a752..47daf549 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/pelletier/go-toml/v2 v2.0.6 github.com/stretchr/testify v1.8.1 github.com/xuri/excelize/v2 v2.8.0 + go.einride.tech/can v0.7.0 golang.org/x/exp v0.0.0-20231006140011-7918f672742d golang.org/x/text v0.13.0 google.golang.org/protobuf v1.28.1 @@ -21,6 +22,7 @@ require ( github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/golang/mock v1.6.0 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -31,6 +33,8 @@ require ( github.com/xuri/efp v0.0.0-20230802181842-ad255f2331ca // indirect github.com/xuri/nfp v0.0.0-20230819163627-dc951e3ffe1a // indirect golang.org/x/crypto v0.14.0 // indirect + golang.org/x/mod v0.13.0 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/sys v0.13.0 // indirect + golang.org/x/tools v0.14.0 // indirect ) diff --git a/go.sum b/go.sum index 9163351c..ef245072 100644 --- a/go.sum +++ b/go.sum @@ -16,9 +16,11 @@ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/j github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/validator/v10 v10.10.1 h1:uA0+amWMiglNZKZ9FJRKUAe9U3RX91eVn1JYXMWt7ig= github.com/go-playground/validator/v10 v10.10.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -64,8 +66,12 @@ github.com/xuri/excelize/v2 v2.8.0 h1:Vd4Qy809fupgp1v7X+nCS/MioeQmYVVzi495UCTqB7 github.com/xuri/excelize/v2 v2.8.0/go.mod h1:6iA2edBTKxKbZAa7X5bDhcCg51xdOn1Ar5sfoXRGrQg= github.com/xuri/nfp v0.0.0-20230819163627-dc951e3ffe1a h1:Mw2VNrNNNjDtw68VsEj2+st+oCSn4Uz7vZw6TbhcV1o= github.com/xuri/nfp v0.0.0-20230819163627-dc951e3ffe1a/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.einride.tech/can v0.7.0 h1:HcpgY32r+/nk5WpFiuk9PwFYaQNkLfqgILnOdrkRbaQ= +go.einride.tech/can v0.7.0/go.mod h1:cPDw0qQMSAsD/NcDqChkhT6Tc8pr5v0fP3HyjLKpYnM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= @@ -75,10 +81,15 @@ golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/image v0.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo= golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= @@ -87,11 +98,16 @@ golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -117,10 +133,15 @@ golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= @@ -133,3 +154,4 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=