Skip to content

Commit 14ac58c

Browse files
committed
Add batch processing for CAN messages
Dashboard and CAN processor now support handling arrays of CAN messages for improved performance and flexibility
1 parent 591eea6 commit 14ac58c

4 files changed

Lines changed: 130 additions & 27 deletions

File tree

pecan/Frontend/pecan-live-dashboard/src/pages/Dashboard.tsx

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,22 +57,35 @@ function Dashboard() {
5757
messageData = event.data;
5858
}
5959

60-
// Use the processor to decode the raw CAN message
60+
// Use the processor to decode the raw CAN message(s)
6161
const decoded = processor.processWebSocketMessage(messageData);
62-
console.log('Decoded message:', decoded);
62+
console.log('Decoded message(s):', decoded);
6363

64-
if (decoded && decoded.signals) {
65-
const canId = decoded.canId.toString();
66-
console.log(`Updating dashboard with CAN ID ${canId}:`, decoded.signals);
67-
68-
// Update or create new message entry
64+
// Handle both single messages and arrays of messages
65+
const messagesToProcess = Array.isArray(decoded) ? decoded : [decoded];
66+
67+
// Process each decoded message
68+
const updates: { [canId: string]: any } = {};
69+
70+
for (const message of messagesToProcess) {
71+
if (message && message.signals) {
72+
const canId = message.canId.toString();
73+
console.log(`Processing CAN ID ${canId}:`, message.signals);
74+
75+
updates[canId] = {
76+
messageName: message.messageName || `CAN_${canId}`,
77+
signals: message.signals,
78+
lastUpdated: Date.now()
79+
};
80+
}
81+
}
82+
83+
// Batch update all messages at once for better performance
84+
if (Object.keys(updates).length > 0) {
85+
console.log(`Updating dashboard with ${Object.keys(updates).length} messages`);
6986
setCanMessages(prev => ({
7087
...prev,
71-
[canId]: {
72-
messageName: decoded.messageName || `CAN_${canId}`,
73-
signals: decoded.signals,
74-
lastUpdated: Date.now()
75-
}
88+
...updates
7689
}));
7790
}
7891
} catch (error) {

pecan/Frontend/pecan-live-dashboard/src/utils/canProcessor.ts

Lines changed: 79 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,20 @@ interface DecodedMessage {
1515
};
1616
}
1717

18+
// Type for batch processing results
19+
type ProcessResult = DecodedMessage | DecodedMessage[] | null;
20+
21+
// Type for input WebSocket messages
22+
interface WebSocketMessage {
23+
time?: number;
24+
timestamp?: number;
25+
canId?: number;
26+
id?: number;
27+
data?: number[];
28+
}
29+
30+
type WebSocketInput = string | WebSocketMessage | WebSocketMessage[];
31+
1832
/**
1933
* Parse the physValue string from Candied (format: "123.45 voltage:V")
2034
* @param physValue - Physical value string from Candied
@@ -301,19 +315,61 @@ export async function createCanProcessor(dbcPath: string): Promise<any> {
301315
return decodeCanMessage(can, parsed.canId, parsed.data, parsed.time);
302316
},
303317

318+
/**
319+
* Process multiple CAN messages in batch
320+
* @param messages - Array of CAN messages
321+
* @returns Array of decoded messages
322+
*/
323+
processBatchMessages: function(messages: WebSocketInput[]): DecodedMessage[] {
324+
const decodedMessages: DecodedMessage[] = [];
325+
326+
for (const message of messages) {
327+
const decoded = this.processWebSocketMessage(message);
328+
if (decoded) {
329+
// If the result is an array, flatten it
330+
if (Array.isArray(decoded)) {
331+
decodedMessages.push(...decoded);
332+
} else {
333+
decodedMessages.push(decoded);
334+
}
335+
}
336+
}
337+
338+
return decodedMessages;
339+
},
340+
304341
/**
305342
* Process WebSocket CAN message
306-
* @param wsMessage - WebSocket message (can be string or object)
307-
* @returns Decoded message or null
343+
* @param wsMessage - WebSocket message (can be string, object, or array of objects)
344+
* @returns Decoded message or array of decoded messages or null
308345
*/
309-
processWebSocketMessage: function(wsMessage: any): DecodedMessage | null {
346+
processWebSocketMessage: function(wsMessage: WebSocketInput): ProcessResult {
310347
// Handle different WebSocket message formats
311348

312349
// If it's a string, try parsing as CSV line
313350
if (typeof wsMessage === 'string') {
314351
return this.processLogLine(wsMessage);
315352
}
316353

354+
// If it's an array of messages, process each one
355+
if (Array.isArray(wsMessage)) {
356+
const decodedMessages: DecodedMessage[] = [];
357+
358+
for (const message of wsMessage) {
359+
const decoded = this.processWebSocketMessage(message);
360+
if (decoded) {
361+
// If the recursive call returns an array, flatten it
362+
if (Array.isArray(decoded)) {
363+
decodedMessages.push(...decoded);
364+
} else {
365+
decodedMessages.push(decoded);
366+
}
367+
}
368+
}
369+
370+
return decodedMessages.length > 0 ? decodedMessages : null;
371+
}
372+
317373
// If it's an object with time, canId/id and data properties
318374
if (typeof wsMessage === 'object') {
319375
const time = wsMessage.time || wsMessage.timestamp || Date.now();
@@ -364,20 +420,31 @@ export async function createCanProcessor(dbcPath: string): Promise<any> {
364420
*
365421
* ws.onmessage = (event) => {
366422
* const decoded = processor.processWebSocketMessage(event.data);
367-
* if (decoded) {
368-
* console.log('Time:', decoded.time);
369-
* console.log('CAN ID:', decoded.canId);
370-
* console.log('Message:', decoded.messageName);
371-
* console.log('Signals:', decoded.signals);
372-
*
373-
* // Next step to tabel or graph
374-
* }
423+
*
424+
* // Handle both single messages and arrays of messages
425+
* const messages = Array.isArray(decoded) ? decoded : [decoded];
426+
*
427+
* messages.forEach(message => {
428+
* if (message) {
429+
* console.log('Time:', message.time);
430+
* console.log('CAN ID:', message.canId);
431+
* console.log('Message:', message.messageName);
432+
* console.log('Signals:', message.signals);
433+
*
434+
* // Next step to table or graph
435+
* }
436+
* });
375437
* };
376438
*
377439
* // Supported WebSocket message formats:
378440
* // 1. CSV string: "2952,CAN,170,4,12,9,0,0,16,64,0"
379441
* ^ relative timestamp will be rejected automatically in the future
380442
* The 2025-2026 DAQ system will have absolute timestamps
381-
* // 2. JSON object: { time: 2952, canId: 170, data: [4,12,9,0,0,16,64,0] }
443+
* // 2. Single JSON object: { time: 2952, canId: 170, data: [4,12,9,0,0,16,64,0] }
382444
* // 3. JSON with timestamp: { timestamp: 1234567890, id: 170, data: [...] }
445+
* // 4. Array of JSON objects: [
446+
* { time: 2952, canId: 170, data: [4,12,9,0,0,16,64,0] },
447+
* { time: 2953, canId: 176, data: [215,1,19,254,51,9,170,14] },
448+
* { time: 2954, canId: 192, data: [216,1,0,0,0,1,252,8] }
449+
* ]
383450
*/

pecan/Frontend/pecan-live-dashboard/src/utils/testing-messages.txt

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,23 @@ curl -X POST http://localhost:8080/send \
9292

9393
curl -X POST http://localhost:8080/send \
9494
-H "Content-Type: application/json" \
95-
-d '{"time":2728,"canId":176,"data":[0,0,0,0,0,0,0,0]}'
95+
-d '{"time":2728,"canId":176,"data":[0,0,0,0,0,0,0,0]}'
96+
97+
# Test batch processing with multiple JSON messages in an array
98+
curl -X POST http://localhost:8080/send \
99+
-H "Content-Type: application/json" \
100+
-d '[
101+
{"time":3000,"canId":176,"data":[215,1,19,254,51,9,170,14]},
102+
{"time":3001,"canId":177,"data":[178,7,250,246,0,0,0,0]},
103+
{"time":3002,"canId":192,"data":[216,1,0,0,0,1,252,8]}
104+
]'
105+
106+
# Test mixed batch with different CAN IDs
107+
curl -X POST http://localhost:8080/send \
108+
-H "Content-Type: application/json" \
109+
-d '[
110+
{"time":3100,"canId":2018,"data":[63,0,83,0,59,0,49,0]},
111+
{"time":3101,"canId":2019,"data":[32,0,32,0,32,0,33,0]},
112+
{"time":3102,"canId":2020,"data":[38,0,57,0,57,0,76,0]},
113+
{"time":3103,"canId":163,"data":[243,205,55,31,16,0,0,0]}
114+
]'

pecan/Frontend/pecan-live-dashboard/ws-server.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ const server = http.createServer((req, res) => {
2424
req.on('end', () => {
2525
try {
2626
const data = JSON.parse(body);
27-
console.log('Received message to broadcast:', data);
27+
const messageCount = Array.isArray(data) ? data.length : 1;
28+
console.log(`Received ${messageCount} message(s) to broadcast:`, data);
2829

2930
// Broadcast to all connected WebSocket clients
3031
wss.clients.forEach(client => {
@@ -34,7 +35,10 @@ const server = http.createServer((req, res) => {
3435
});
3536

3637
res.writeHead(200, { 'Content-Type': 'application/json' });
37-
res.end(JSON.stringify({ success: true, message: 'Message broadcasted' }));
38+
res.end(JSON.stringify({
39+
success: true,
40+
message: `${messageCount} message(s) broadcasted`
41+
}));
3842
} catch (error) {
3943
console.error('Error parsing message:', error);
4044
res.writeHead(400, { 'Content-Type': 'application/json' });

0 commit comments

Comments
 (0)