Skip to content

Commit 3e64d94

Browse files
authored
Merge pull request #300 from HyperloopUPV-H8/blcu-improvements
BLCU improvements
2 parents 7cf9d03 + 6fa18a7 commit 3e64d94

8 files changed

Lines changed: 468 additions & 52 deletions

File tree

backend/cmd/main.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"github.com/HyperloopUPV-H8/h9-backend/internal/utils"
3333
vehicle_models "github.com/HyperloopUPV-H8/h9-backend/internal/vehicle/models"
3434
"github.com/HyperloopUPV-H8/h9-backend/pkg/abstraction"
35+
"github.com/HyperloopUPV-H8/h9-backend/pkg/boards"
3536
"github.com/HyperloopUPV-H8/h9-backend/pkg/broker"
3637
blcu_topics "github.com/HyperloopUPV-H8/h9-backend/pkg/broker/topics/blcu"
3738
connection_topic "github.com/HyperloopUPV-H8/h9-backend/pkg/broker/topics/connection"
@@ -219,6 +220,16 @@ func main() {
219220
vehicle.SetIdToBoardName(idToBoard)
220221
vehicle.SetTransport(transp)
221222

223+
// <--- BLCU Board --->
224+
// Register BLCU board for handling bootloader operations
225+
if blcuIP, exists := adj.Info.Addresses[BLCU]; exists {
226+
blcuBoard := boards.New(blcuIP)
227+
vehicle.AddBoard(blcuBoard)
228+
trace.Info().Str("ip", blcuIP).Msg("BLCU board registered")
229+
} else {
230+
trace.Warn().Msg("BLCU address not found in ADJ")
231+
}
232+
222233
// <--- transport --->
223234
// Load and set packet decoder and encoder
224235
decoder, encoder := getTransportDecEnc(adj.Info, podData)

backend/pkg/boards/blcu.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ const (
1515
BlcuName = "BLCU"
1616
BlcuId = abstraction.BoardId(1)
1717

18-
AckId = "1"
18+
AckId = abstraction.BoardEvent("ACK")
19+
DownloadEventId = abstraction.BoardEvent("DOWNLOAD")
20+
UploadEventId = abstraction.BoardEvent("UPLOAD")
1921

2022
BlcuDownloadOrderId = 1
2123
BlcuUploadOrderId = 2
@@ -39,21 +41,21 @@ func (boards *BLCU) Id() abstraction.BoardId {
3941

4042
func (boards *BLCU) Notify(boardNotification abstraction.BoardNotification) {
4143
switch notification := boardNotification.(type) {
42-
case AckNotification:
44+
case *AckNotification:
4345
boards.ackChan <- struct{}{}
4446

45-
case DownloadEvent:
46-
err := boards.download(notification)
47+
case *DownloadEvent:
48+
err := boards.download(*notification)
4749
if err != nil {
4850
fmt.Println(ErrDownloadFailure{
4951
Timestamp: time.Now(),
5052
Inner: err,
5153
}.Error())
5254
}
53-
case UploadEvent:
54-
err := boards.upload(notification)
55+
case *UploadEvent:
56+
err := boards.upload(*notification)
5557
if err != nil {
56-
fmt.Println(ErrDownloadFailure{
58+
fmt.Println(ErrUploadFailure{
5759
Timestamp: time.Now(),
5860
Inner: err,
5961
}.Error())
Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
package boards_test
2+
3+
import (
4+
"encoding/json"
5+
"testing"
6+
"time"
7+
8+
"github.com/HyperloopUPV-H8/h9-backend/pkg/abstraction"
9+
"github.com/HyperloopUPV-H8/h9-backend/pkg/boards"
10+
"github.com/HyperloopUPV-H8/h9-backend/pkg/broker"
11+
blcu_topic "github.com/HyperloopUPV-H8/h9-backend/pkg/broker/topics/blcu"
12+
"github.com/HyperloopUPV-H8/h9-backend/pkg/vehicle"
13+
"github.com/HyperloopUPV-H8/h9-backend/pkg/websocket"
14+
"github.com/rs/zerolog"
15+
)
16+
17+
// MockTransport implements abstraction.Transport for testing
18+
type MockTransport struct {
19+
sentMessages []abstraction.TransportMessage
20+
}
21+
22+
func (m *MockTransport) SendMessage(msg abstraction.TransportMessage) error {
23+
m.sentMessages = append(m.sentMessages, msg)
24+
return nil
25+
}
26+
27+
func (m *MockTransport) HandleClient(config interface{}, target string) error {
28+
return nil
29+
}
30+
31+
func (m *MockTransport) HandleServer(config interface{}, addr string) error {
32+
return nil
33+
}
34+
35+
func (m *MockTransport) HandleSniffer(sniffer interface{}) error {
36+
return nil
37+
}
38+
39+
func (m *MockTransport) SetAPI(api abstraction.TransportAPI) {}
40+
41+
func (m *MockTransport) SetIdTarget(id abstraction.PacketId, target abstraction.TransportTarget) {}
42+
43+
func (m *MockTransport) SetTargetIp(ip string, target abstraction.TransportTarget) {}
44+
45+
func (m *MockTransport) SetpropagateFault(propagate bool) {}
46+
47+
func (m *MockTransport) WithDecoder(decoder interface{}) abstraction.Transport {
48+
return m
49+
}
50+
51+
func (m *MockTransport) WithEncoder(encoder interface{}) abstraction.Transport {
52+
return m
53+
}
54+
55+
// MockLogger implements abstraction.Logger for testing
56+
type MockLogger struct{}
57+
58+
func (m *MockLogger) Start() error {
59+
return nil
60+
}
61+
62+
func (m *MockLogger) Stop() error {
63+
return nil
64+
}
65+
66+
func (m *MockLogger) PushRecord(record abstraction.LoggerRecord) error {
67+
return nil
68+
}
69+
70+
func (m *MockLogger) PullRecord(request abstraction.LoggerRequest) (abstraction.LoggerRecord, error) {
71+
return nil, nil
72+
}
73+
74+
// TestBLCUDownloadOrder tests the BLCU download order flow
75+
func TestBLCUDownloadOrder(t *testing.T) {
76+
// Setup
77+
logger := zerolog.New(nil).Level(zerolog.Disabled)
78+
79+
// Create vehicle
80+
v := vehicle.New(logger)
81+
82+
// Create and setup broker
83+
b := broker.New(logger)
84+
connections := make(chan *websocket.Client)
85+
pool := websocket.NewPool(connections, logger)
86+
b.SetPool(pool)
87+
88+
// Register BLCU topics
89+
blcu_topic.RegisterTopics(b, pool)
90+
91+
// Set broker and transport
92+
v.SetBroker(b)
93+
mockTransport := &MockTransport{}
94+
v.SetTransport(mockTransport)
95+
mockLogger := &MockLogger{}
96+
v.SetLogger(mockLogger)
97+
98+
// Create BLCU board
99+
blcuBoard := boards.New("192.168.0.10") // Example IP
100+
101+
// This is the missing step - register the BLCU board with the vehicle
102+
v.AddBoard(blcuBoard)
103+
104+
// Note: In a real scenario, we would capture responses through the broker
105+
106+
// Test download request
107+
t.Run("Download Request", func(t *testing.T) {
108+
downloadRequest := &blcu_topic.DownloadRequest{
109+
Board: "VCU",
110+
}
111+
112+
// Send download request through UserPush
113+
err := v.UserPush(downloadRequest)
114+
if err != nil {
115+
t.Fatalf("UserPush failed: %v", err)
116+
}
117+
118+
// Simulate ACK from board
119+
blcuBoard.Notify(boards.AckNotification{
120+
ID: boards.AckId,
121+
})
122+
123+
// Check if the download order was sent to the board
124+
if len(mockTransport.sentMessages) == 0 {
125+
t.Fatal("No message sent to transport")
126+
}
127+
128+
// Verify the packet sent contains the correct order ID
129+
// In a real test, we would decode the packet and verify its contents
130+
})
131+
}
132+
133+
// TestBLCUUploadOrder tests the BLCU upload order flow
134+
func TestBLCUUploadOrder(t *testing.T) {
135+
// Setup
136+
logger := zerolog.New(nil).Level(zerolog.Disabled)
137+
138+
// Create vehicle
139+
v := vehicle.New(logger)
140+
141+
// Create and setup broker
142+
b := broker.New(logger)
143+
connections := make(chan *websocket.Client)
144+
pool := websocket.NewPool(connections, logger)
145+
b.SetPool(pool)
146+
147+
// Register BLCU topics
148+
blcu_topic.RegisterTopics(b, pool)
149+
150+
// Set broker and transport
151+
v.SetBroker(b)
152+
mockTransport := &MockTransport{}
153+
v.SetTransport(mockTransport)
154+
mockLogger := &MockLogger{}
155+
v.SetLogger(mockLogger)
156+
157+
// Create BLCU board
158+
blcuBoard := boards.New("192.168.0.10") // Example IP
159+
160+
// Register the BLCU board with the vehicle
161+
v.AddBoard(blcuBoard)
162+
163+
// Test upload request
164+
t.Run("Upload Request", func(t *testing.T) {
165+
// Using the internal request type that has Data field
166+
uploadRequest := &blcu_topic.UploadRequestInternal{
167+
Board: "VCU",
168+
Data: []byte("test firmware data"),
169+
}
170+
171+
// Send upload request through UserPush
172+
err := v.UserPush(uploadRequest)
173+
if err != nil {
174+
t.Fatalf("UserPush failed: %v", err)
175+
}
176+
177+
// Simulate ACK from board
178+
blcuBoard.Notify(boards.AckNotification{
179+
ID: boards.AckId,
180+
})
181+
182+
// Check if the upload order was sent to the board
183+
if len(mockTransport.sentMessages) == 0 {
184+
t.Fatal("No message sent to transport")
185+
}
186+
})
187+
}
188+
189+
// TestBLCUWebSocketFlow tests the complete WebSocket flow for BLCU orders
190+
func TestBLCUWebSocketFlow(t *testing.T) {
191+
// Setup
192+
logger := zerolog.New(nil).Level(zerolog.Disabled)
193+
194+
// Create vehicle
195+
v := vehicle.New(logger)
196+
197+
// Create and setup broker
198+
b := broker.New(logger)
199+
connections := make(chan *websocket.Client)
200+
pool := websocket.NewPool(connections, logger)
201+
b.SetPool(pool)
202+
203+
// Register BLCU topics
204+
blcu_topic.RegisterTopics(b, pool)
205+
206+
// Set broker
207+
v.SetBroker(b)
208+
mockTransport := &MockTransport{}
209+
v.SetTransport(mockTransport)
210+
mockLogger := &MockLogger{}
211+
v.SetLogger(mockLogger)
212+
213+
// Create BLCU board
214+
blcuBoard := boards.New("192.168.0.10")
215+
v.AddBoard(blcuBoard)
216+
217+
// Simulate WebSocket client message
218+
t.Run("WebSocket Download Message", func(t *testing.T) {
219+
// Get download topic handler from registered topics
220+
downloadHandler := &blcu_topic.Download{}
221+
downloadHandler.SetAPI(b)
222+
downloadHandler.SetPool(pool)
223+
224+
// Create WebSocket message
225+
downloadReq := blcu_topic.DownloadRequest{
226+
Board: "VCU",
227+
}
228+
payload, _ := json.Marshal(downloadReq)
229+
230+
wsMessage := &websocket.Message{
231+
Topic: blcu_topic.DownloadName,
232+
Payload: payload,
233+
}
234+
235+
// Simulate client message
236+
// Create a valid UUID for ClientId
237+
clientUUID := [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
238+
clientId := websocket.ClientId(clientUUID)
239+
downloadHandler.ClientMessage(clientId, wsMessage)
240+
241+
// Give some time for async operations
242+
time.Sleep(100 * time.Millisecond)
243+
244+
// Verify order was sent
245+
if len(mockTransport.sentMessages) == 0 {
246+
t.Error("No message sent to transport after WebSocket message")
247+
}
248+
})
249+
}
250+
251+
// TestBLCURegistrationIssue demonstrates the issue when BLCU is not registered
252+
func TestBLCURegistrationIssue(t *testing.T) {
253+
// Setup WITHOUT registering BLCU board
254+
logger := zerolog.New(nil).Level(zerolog.Disabled)
255+
256+
v := vehicle.New(logger)
257+
b := broker.New(logger)
258+
connections := make(chan *websocket.Client)
259+
pool := websocket.NewPool(connections, logger)
260+
b.SetPool(pool)
261+
blcu_topic.RegisterTopics(b, pool)
262+
v.SetBroker(b)
263+
264+
// Try to send download request without BLCU board registered
265+
t.Run("Download Without Registration", func(t *testing.T) {
266+
defer func() {
267+
if r := recover(); r == nil {
268+
// If no panic, check if the request was handled
269+
// In the current implementation, this will fail silently
270+
t.Log("Request handled without BLCU registration - this is the bug!")
271+
}
272+
}()
273+
274+
downloadRequest := &blcu_topic.DownloadRequest{
275+
Board: "VCU",
276+
}
277+
278+
// This will fail because boards[boards.BlcuId] is nil
279+
err := v.UserPush(downloadRequest)
280+
if err == nil {
281+
t.Log("UserPush succeeded but BLCU board notification will fail")
282+
}
283+
})
284+
}

0 commit comments

Comments
 (0)