@@ -77,6 +77,7 @@ export async function parseREVLOG(
7777 const ENCODER_ID = 7 ;
7878
7979 const RECORDS = new Map < string , { id : number ; type : string } > ( ) ;
80+ const FAST_RECORDS = new Map < number , { id : number ; type : string } > ( ) ;
8081 let NEXT_RECORD_ID = 1 ;
8182 let recordsProcessed = false ;
8283
@@ -222,6 +223,63 @@ export async function parseREVLOG(
222223 writeOut ( payload ) ;
223224 } ;
224225
226+ const writeRecordFast = (
227+ recordInfo : { id : number ; type : string } ,
228+ entryValue : string | number | boolean | bigint ,
229+ timestampMs : number
230+ ) => {
231+ let payload : Buffer ;
232+ switch ( recordInfo . type ) {
233+ case 'boolean' :
234+ payload = Buffer . alloc ( 1 ) ;
235+ payload . writeUInt8 ( entryValue ? 1 : 0 , 0 ) ;
236+ break ;
237+ case 'int64' :
238+ payload = Buffer . alloc ( 8 ) ;
239+ try {
240+ payload . writeBigInt64LE (
241+ BigInt (
242+ typeof entryValue === 'number'
243+ ? Math . round ( entryValue )
244+ : entryValue
245+ ) ,
246+ 0
247+ ) ;
248+ } catch {
249+ payload . writeBigInt64LE ( 0n , 0 ) ;
250+ }
251+ break ;
252+ case 'float' :
253+ payload = Buffer . alloc ( 4 ) ;
254+ payload . writeFloatLE ( Number ( entryValue ) , 0 ) ;
255+ break ;
256+ case 'double' :
257+ payload = Buffer . alloc ( 8 ) ;
258+ payload . writeDoubleLE ( Number ( entryValue ) , 0 ) ;
259+ break ;
260+ case 'string' :
261+ payload = Buffer . from ( String ( entryValue ) , 'utf-8' ) ;
262+ break ;
263+ default :
264+ return ;
265+ }
266+
267+ const payloadSize = payload . length ;
268+ const timestampUs = BigInt ( timestampMs ) * 1000n ;
269+ const bitfield =
270+ ( requiredBytes ( recordInfo . id ) - 1 ) |
271+ ( ( requiredBytes ( payloadSize ) - 1 ) << 2 ) |
272+ ( ( requiredTimestampBytes ( timestampUs ) - 1 ) << 4 ) ;
273+
274+ writeOut ( Buffer . from ( [ bitfield ] ) ) ;
275+ writeOut ( writeVariableInt ( recordInfo . id , requiredBytes ( recordInfo . id ) ) ) ;
276+ writeOut ( writeVariableInt ( payloadSize , requiredBytes ( payloadSize ) ) ) ;
277+ writeOut (
278+ writeVariableInt ( timestampUs , requiredTimestampBytes ( timestampUs ) )
279+ ) ;
280+ writeOut ( payload ) ;
281+ } ;
282+
225283 const getWpilogTypeFromSignal = ( signal : Signal ) : string => {
226284 if ( signal . length === 1 ) return 'boolean' ;
227285 const isFloat =
@@ -256,10 +314,7 @@ export async function parseREVLOG(
256314 firmwareFrameId ?: number ;
257315 firmwareMessageName ?: string ;
258316 periodicFrames : Map < number , Message > ;
259- parseFirmwareVersion (
260- decoded : DecodedSignal [ ] ,
261- data ?: Buffer
262- ) : string ;
317+ parseFirmwareVersion ( decoded : DecodedSignal [ ] , data ?: Buffer ) : string ;
263318 }
264319
265320 const sparkDbc = await readDbcFile ( 'spark.public.dbc' ) ;
@@ -445,29 +500,46 @@ export async function parseREVLOG(
445500 device . canDecoder . createFrame ( messageSpec . id , alignedData )
446501 ) ;
447502 if ( decoded ) {
448- for ( const sig of decoded ) {
449- const signalSpec = messageSpec . signals . get ( sig . name ) ;
450- if ( ! signalSpec ) continue ;
503+ let sigIndex = 0 ; // Track the index manually
451504
452- const folder = sig . name . endsWith ( 'FAULT' )
453- ? '/FAULT/'
454- : sig . name . endsWith ( 'WARNING' )
455- ? '/WARNING/'
456- : '/' ;
457- const name = `${ device . prefix } ${ messageId & 0x3f } ${ folder } ${ sig . name } ` ;
505+ for ( const sig of decoded ) {
506+ // Generate our unique math-based key
507+ const fastKey = messageId * 256 + sigIndex ;
508+
509+ let recordInfo = FAST_RECORDS . get ( fastKey ) ;
510+
511+ // SLOW PATH: We have never seen this signal before.
512+ // Do the string math, register it, and cache it.
513+ if ( ! recordInfo ) {
514+ const signalSpec = messageSpec . signals . get ( sig . name ) ;
515+ if ( ! signalSpec ) {
516+ sigIndex ++ ;
517+ continue ;
518+ }
519+
520+ const folder = sig . name . endsWith ( 'FAULT' )
521+ ? '/FAULT/'
522+ : sig . name . endsWith ( 'WARNING' )
523+ ? '/WARNING/'
524+ : '/' ;
525+ const name = `${ device . prefix } ${ messageId & 0x3f } ${ folder } ${ sig . name } ` ;
458526
459- if ( ! RECORDS . has ( name ) ) {
460527 const wpilogType = getWpilogTypeFromSignal ( signalSpec ) ;
461528 const metadata =
462529 signalSpec . description ?? signalSpec . unit ?? '' ;
463- writeControlRecord (
464- NEXT_RECORD_ID ++ ,
465- name ,
466- wpilogType ,
467- metadata
468- ) ;
530+
531+ const newId = NEXT_RECORD_ID ++ ;
532+ writeControlRecord ( newId , name , wpilogType , metadata ) ;
533+
534+ // Save it to the fast cache
535+ recordInfo = { id : newId , type : wpilogType } ;
536+ FAST_RECORDS . set ( fastKey , recordInfo ) ;
469537 }
470- writeRecord ( name , sig . value , msgTsMs ) ;
538+
539+ // FAST PATH: Write immediately using the cached info. No strings attached!
540+ writeRecordFast ( recordInfo , sig . value , msgTsMs ) ;
541+
542+ sigIndex ++ ;
471543 }
472544 }
473545 } catch ( e ) {
0 commit comments