diff --git a/backend/cmd/main.go b/backend/cmd/main.go index 8f9875fa6..0c7b54882 100644 --- a/backend/cmd/main.go +++ b/backend/cmd/main.go @@ -67,7 +67,7 @@ import ( const ( BACKEND = "backend" - BLCU = "blcu" + BLCU = "BLCU" TcpClient = "TCP_CLIENT" TcpServer = "TCP_SERVER" UDP = "UDP" @@ -88,7 +88,7 @@ var currentVersion string func main() { // update() // FIXME: Updater disabled due to cross-platform and reliability issues - + traceFile := initTrace(*traceLevel, *traceFile) defer traceFile.Close() @@ -223,16 +223,34 @@ func main() { // <--- BLCU Board ---> // Register BLCU board for handling bootloader operations if blcuIP, exists := adj.Info.Addresses[BLCU]; exists { - tftpConfig := boards.TFTPConfig{ - BlockSize: config.TFTP.BlockSize, - Retries: config.TFTP.Retries, - TimeoutMs: config.TFTP.TimeoutMs, - BackoffFactor: config.TFTP.BackoffFactor, - EnableProgress: config.TFTP.EnableProgress, + blcuId, idExists := adj.Info.BoardIds["BLCU"] + if !idExists { + trace.Error().Msg("BLCU IP found in ADJ but board ID missing") + } else { + // Get configurable order IDs or use defaults + downloadOrderId := config.Blcu.DownloadOrderId + uploadOrderId := config.Blcu.UploadOrderId + if downloadOrderId == 0 { + downloadOrderId = boards.DefaultBlcuDownloadOrderId + } + if uploadOrderId == 0 { + uploadOrderId = boards.DefaultBlcuUploadOrderId + } + + tftpConfig := boards.TFTPConfig{ + BlockSize: config.TFTP.BlockSize, + Retries: config.TFTP.Retries, + TimeoutMs: config.TFTP.TimeoutMs, + BackoffFactor: config.TFTP.BackoffFactor, + EnableProgress: config.TFTP.EnableProgress, + } + blcuBoard := boards.NewWithConfig(blcuIP, tftpConfig, abstraction.BoardId(blcuId), downloadOrderId, uploadOrderId) + vehicle.AddBoard(blcuBoard) + vehicle.SetBlcuId(abstraction.BoardId(blcuId)) + trace.Info().Str("ip", blcuIP).Int("id", int(blcuId)).Uint16("download_order_id", downloadOrderId).Uint16("upload_order_id", uploadOrderId).Msg("BLCU board registered") } - blcuBoard := boards.NewWithTFTPConfig(blcuIP, tftpConfig) - vehicle.AddBoard(blcuBoard) - trace.Info().Str("ip", blcuIP).Msg("BLCU board registered") + } else { + trace.Warn().Msg("BLCU not found in ADJ configuration - bootloader operations unavailable") } // <--- transport ---> @@ -254,15 +272,15 @@ func main() { downloadOrderId := config.Blcu.DownloadOrderId uploadOrderId := config.Blcu.UploadOrderId if downloadOrderId == 0 { - downloadOrderId = boards.BlcuDownloadOrderId + downloadOrderId = boards.DefaultBlcuDownloadOrderId } if uploadOrderId == 0 { - uploadOrderId = boards.BlcuUploadOrderId + uploadOrderId = boards.DefaultBlcuUploadOrderId } - + transp.SetIdTarget(abstraction.PacketId(downloadOrderId), abstraction.TransportTarget("BLCU")) transp.SetIdTarget(abstraction.PacketId(uploadOrderId), abstraction.TransportTarget("BLCU")) - + // Use BLCU address from config, ADJ, or default blcuIP := config.Blcu.IP if blcuIP == "" { @@ -289,23 +307,23 @@ func main() { } // Create TCP client config with custom parameters from config clientConfig := tcp.NewClientConfig(backendTcpClientAddr) - + // Apply custom timeout if specified if config.TCP.ConnectionTimeout > 0 { clientConfig.Timeout = time.Duration(config.TCP.ConnectionTimeout) * time.Millisecond } - + // Apply custom keep-alive if specified if config.TCP.KeepAlive > 0 { clientConfig.KeepAlive = time.Duration(config.TCP.KeepAlive) * time.Millisecond } - + // Apply custom backoff parameters if config.TCP.BackoffMinMs > 0 || config.TCP.BackoffMaxMs > 0 || config.TCP.BackoffMultiplier > 0 { minBackoff := 100 * time.Millisecond // default - maxBackoff := 5 * time.Second // default - multiplier := 1.5 // default - + maxBackoff := 5 * time.Second // default + multiplier := 1.5 // default + if config.TCP.BackoffMinMs > 0 { minBackoff = time.Duration(config.TCP.BackoffMinMs) * time.Millisecond } @@ -315,13 +333,13 @@ func main() { if config.TCP.BackoffMultiplier > 0 { multiplier = config.TCP.BackoffMultiplier } - + clientConfig.ConnectionBackoffFunction = tcp.NewExponentialBackoff(minBackoff, multiplier, maxBackoff) } - + // Apply max retries (0 or negative means infinite) clientConfig.MaxConnectionRetries = config.TCP.MaxRetries - + go transp.HandleClient(clientConfig, fmt.Sprintf("%s:%d", adj.Info.Addresses[board.Name], adj.Info.Ports[TcpServer])) i++ } diff --git a/backend/pkg/boards/blcu.go b/backend/pkg/boards/blcu.go index 641cf83d5..9c4534404 100644 --- a/backend/pkg/boards/blcu.go +++ b/backend/pkg/boards/blcu.go @@ -11,17 +11,16 @@ import ( dataPacket "github.com/HyperloopUPV-H8/h9-backend/pkg/transport/packet/data" ) -// TODO! Get from ADE const ( BlcuName = "BLCU" - BlcuId = abstraction.BoardId(1) AckId = abstraction.BoardEvent("ACK") DownloadEventId = abstraction.BoardEvent("DOWNLOAD") UploadEventId = abstraction.BoardEvent("UPLOAD") - BlcuDownloadOrderId = 701 - BlcuUploadOrderId = 700 + // Default order IDs - can be overridden via config.toml + DefaultBlcuDownloadOrderId = 701 + DefaultBlcuUploadOrderId = 700 ) type TFTPConfig struct { @@ -33,12 +32,16 @@ type TFTPConfig struct { } type BLCU struct { - api abstraction.BoardAPI - ackChan chan struct{} - ip string - tftpConfig TFTPConfig + api abstraction.BoardAPI + ackChan chan struct{} + ip string + tftpConfig TFTPConfig + id abstraction.BoardId + downloadOrderId uint16 + uploadOrderId uint16 } +// Deprecated: Use NewWithConfig with proper board ID and order IDs from configuration func New(ip string) *BLCU { return NewWithTFTPConfig(ip, TFTPConfig{ BlockSize: 131072, // 128kB @@ -46,18 +49,33 @@ func New(ip string) *BLCU { TimeoutMs: 5000, BackoffFactor: 2, EnableProgress: true, - }) + }, 0) // Board ID 0 indicates missing configuration } -func NewWithTFTPConfig(ip string, tftpConfig TFTPConfig) *BLCU { +// Deprecated: Use NewWithConfig for proper order ID configuration +func NewWithTFTPConfig(ip string, tftpConfig TFTPConfig, id abstraction.BoardId) *BLCU { return &BLCU{ - ackChan: make(chan struct{}), - ip: ip, - tftpConfig: tftpConfig, + ackChan: make(chan struct{}), + ip: ip, + tftpConfig: tftpConfig, + id: id, + downloadOrderId: DefaultBlcuDownloadOrderId, + uploadOrderId: DefaultBlcuUploadOrderId, } } -func (boards *BLCU) Id() abstraction.BoardId { - return BlcuId + +func NewWithConfig(ip string, tftpConfig TFTPConfig, id abstraction.BoardId, downloadOrderId, uploadOrderId uint16) *BLCU { + return &BLCU{ + ackChan: make(chan struct{}), + ip: ip, + tftpConfig: tftpConfig, + id: id, + downloadOrderId: downloadOrderId, + uploadOrderId: uploadOrderId, + } +} +func (board *BLCU) Id() abstraction.BoardId { + return board.id } func (boards *BLCU) Notify(boardNotification abstraction.BoardNotification) { @@ -96,7 +114,7 @@ func (boards *BLCU) SetAPI(api abstraction.BoardAPI) { func (boards *BLCU) download(notification DownloadEvent) error { // Notify the BLCU ping := dataPacket.NewPacketWithValues( - abstraction.PacketId(BlcuDownloadOrderId), + abstraction.PacketId(boards.downloadOrderId), map[dataPacket.ValueName]dataPacket.Value{ BlcuName: dataPacket.NewEnumValue(dataPacket.EnumVariant(notification.Board)), }, @@ -169,7 +187,7 @@ func (boards *BLCU) download(notification DownloadEvent) error { } func (boards *BLCU) upload(notification UploadEvent) error { - ping := dataPacket.NewPacketWithValues(abstraction.PacketId(BlcuUploadOrderId), + ping := dataPacket.NewPacketWithValues(abstraction.PacketId(boards.uploadOrderId), map[dataPacket.ValueName]dataPacket.Value{ BlcuName: dataPacket.NewEnumValue(dataPacket.EnumVariant(notification.Board)), }, diff --git a/backend/pkg/boards/blcu_simple_test.go b/backend/pkg/boards/blcu_simple_test.go index 2fcf4ddae..7ff5633e8 100644 --- a/backend/pkg/boards/blcu_simple_test.go +++ b/backend/pkg/boards/blcu_simple_test.go @@ -3,24 +3,76 @@ package boards_test import ( "testing" + "github.com/HyperloopUPV-H8/h9-backend/pkg/abstraction" "github.com/HyperloopUPV-H8/h9-backend/pkg/boards" blcu_topic "github.com/HyperloopUPV-H8/h9-backend/pkg/broker/topics/blcu" "github.com/HyperloopUPV-H8/h9-backend/pkg/vehicle" "github.com/rs/zerolog" ) -// TestBLCUBoardRegistration tests that BLCU board can be registered +// TestBLCUBoardRegistration tests that BLCU board can be registered with different configurations func TestBLCUBoardRegistration(t *testing.T) { logger := zerolog.New(nil).Level(zerolog.Disabled) v := vehicle.New(logger) - // Create and register BLCU board + // Test deprecated constructor (should use board ID 0) blcuBoard := boards.New("192.168.0.10") v.AddBoard(blcuBoard) + // Verify board is registered with ID 0 (missing configuration) + if blcuBoard.Id() != 0 { + t.Errorf("Expected board ID 0 for deprecated constructor, got %d", blcuBoard.Id()) + } +} + +// TestBLCUWithCustomConfiguration tests BLCU with custom board ID and order IDs +func TestBLCUWithCustomConfiguration(t *testing.T) { + logger := zerolog.New(nil).Level(zerolog.Disabled) + v := vehicle.New(logger) + + // Test new constructor with custom configuration + tftpConfig := boards.TFTPConfig{ + BlockSize: 131072, + Retries: 3, + TimeoutMs: 5000, + BackoffFactor: 2, + EnableProgress: true, + } + + customBoardId := abstraction.BoardId(7) + customDownloadOrderId := uint16(801) + customUploadOrderId := uint16(802) + + blcuBoard := boards.NewWithConfig("192.168.0.10", tftpConfig, customBoardId, customDownloadOrderId, customUploadOrderId) + v.AddBoard(blcuBoard) + + // Verify board is registered with custom ID + if blcuBoard.Id() != customBoardId { + t.Errorf("Expected board ID %d, got %d", customBoardId, blcuBoard.Id()) + } +} + +// TestBLCUWithDefaultConfiguration tests BLCU with default order IDs +func TestBLCUWithDefaultConfiguration(t *testing.T) { + logger := zerolog.New(nil).Level(zerolog.Disabled) + v := vehicle.New(logger) + + // Test deprecated constructor (should use default order IDs) + tftpConfig := boards.TFTPConfig{ + BlockSize: 131072, + Retries: 3, + TimeoutMs: 5000, + BackoffFactor: 2, + EnableProgress: true, + } + + boardId := abstraction.BoardId(7) + blcuBoard := boards.NewWithTFTPConfig("192.168.0.10", tftpConfig, boardId) + v.AddBoard(blcuBoard) + // Verify board is registered - if blcuBoard.Id() != boards.BlcuId { - t.Errorf("Expected board ID %d, got %d", boards.BlcuId, blcuBoard.Id()) + if blcuBoard.Id() != boardId { + t.Errorf("Expected board ID %d, got %d", boardId, blcuBoard.Id()) } } diff --git a/backend/pkg/vehicle/constructor.go b/backend/pkg/vehicle/constructor.go index 93c2944fd..e71cd407b 100644 --- a/backend/pkg/vehicle/constructor.go +++ b/backend/pkg/vehicle/constructor.go @@ -75,3 +75,8 @@ func (vehicle *Vehicle) SetIpToBoardId(ipToBoardId map[string]abstraction.BoardI vehicle.ipToBoardId = ipToBoardId vehicle.trace.Info().Msg("set ip to board id") } + +func (vehicle *Vehicle) SetBlcuId(id abstraction.BoardId) { + vehicle.BlcuId = id + vehicle.trace.Info().Uint16("blcu_id", uint16(id)).Msg("set blcu id") +} diff --git a/backend/pkg/vehicle/notification.go b/backend/pkg/vehicle/notification.go index 164b4147a..72fde59c3 100644 --- a/backend/pkg/vehicle/notification.go +++ b/backend/pkg/vehicle/notification.go @@ -131,7 +131,7 @@ func (vehicle *Vehicle) handlePacketNotification(notification transport.PacketNo return errors.Join(fmt.Errorf("remove state orders (state orders from %s to %s)", notification.From, notification.To), err) } case *blcu_packet.Ack: - vehicle.boards[boards.BlcuId].Notify(abstraction.BoardNotification( + vehicle.boards[vehicle.BlcuId].Notify(abstraction.BoardNotification( &boards.AckNotification{ ID: boards.AckId, }, diff --git a/backend/pkg/vehicle/vehicle.go b/backend/pkg/vehicle/vehicle.go index fe1405861..b067bef3c 100644 --- a/backend/pkg/vehicle/vehicle.go +++ b/backend/pkg/vehicle/vehicle.go @@ -33,6 +33,7 @@ type Vehicle struct { updateFactory *update_factory.UpdateFactory idToBoardName map[uint16]string ipToBoardId map[string]abstraction.BoardId + BlcuId abstraction.BoardId trace zerolog.Logger } @@ -92,11 +93,11 @@ func (vehicle *Vehicle) UserPush(push abstraction.BrokerPush) error { case "blcu/downloadRequest": download := push.(*blcu_topic.DownloadRequest) - if board, exists := vehicle.boards[boards.BlcuId]; exists { + if board, exists := vehicle.boards[vehicle.BlcuId]; exists { board.Notify(abstraction.BoardNotification( &boards.DownloadEvent{ BoardEvent: boards.DownloadEventId, - BoardID: boards.BlcuId, + BoardID: vehicle.BlcuId, Board: download.Board, }, )) @@ -124,7 +125,7 @@ func (vehicle *Vehicle) UserPush(push abstraction.BrokerPush) error { return nil } - if board, exists := vehicle.boards[boards.BlcuId]; exists { + if board, exists := vehicle.boards[vehicle.BlcuId]; exists { board.Notify(abstraction.BoardNotification(uploadEvent)) } else { fmt.Fprintf(os.Stderr, "BLCU board not registered\n")