22This file contains the logic for parsing different types of log files on the main electron process.
33*/
44
5+ import { WebContents } from "electron"
56import fs from "fs"
67import readline from "readline"
78
8- import createRecentLogsManager from "../settings /recentLogManager"
9+ import createRecentLogsManager from "./utils /recentLogManager"
910
1011import {
11- clearUnitCache ,
1212 buildDefaultMessageFilters ,
13- calculateMeanValues ,
1413 calcGPSOffset ,
14+ calculateMeanValues ,
15+ clearUnitCache ,
1516 convertTimeUStoUTC ,
1617 expandBATMessages ,
1718 expandESCMessages ,
19+ getUnit ,
1820 processFlightModes ,
1921 sortObjectByKeys ,
20- getUnit ,
21- } from "./utils/fla-utils"
22+ } from "./utils/flaUtils"
23+
24+ // Type definitions
25+ interface FormatMessage {
26+ length : number
27+ name : string
28+ type : number
29+ format : string
30+ fields : string [ ]
31+ units ?: string
32+ multiplier ?: string
33+ }
34+
35+ interface MessageObject {
36+ name : string
37+ type ?: number
38+ TimeUS ?: number
39+ [ key : string ] : string | number | undefined
40+ }
41+
42+ interface Messages {
43+ [ messageName : string ] :
44+ | MessageObject [ ]
45+ | { [ key : string ] : FormatMessage }
46+ | { [ key : string ] : string }
47+ | string
48+ | null
49+ }
50+
51+ interface ParseResult {
52+ success : boolean
53+ error ?: string
54+ summary ?: LogSummary
55+ }
56+
57+ interface LogSummary {
58+ formatMessages : { [ key : string ] : FormatMessage }
59+ utcAvailable : boolean
60+ logEvents : MessageObject [ ]
61+ flightModeMessages : MessageObject [ ]
62+ logType : string
63+ messageFilters : Record < string , unknown >
64+ messageMeans : Record <
65+ string ,
66+ { mean : string ; max : string ; min : string }
67+ > | null
68+ aircraftType : string | null
69+ }
70+
71+ interface Dataset {
72+ label : string
73+ yAxisID : string
74+ x : Float64Array
75+ y : Float32Array
76+ }
77+
78+ type LogType = "dataflash" | "fgcs_telemetry" | "mp_telemetry" | null
2279
2380const UPDATE_THROTTLE_MS = 100 // Update every 100ms
2481const recentLogsManager = createRecentLogsManager ( )
25- let logData = null
26- let defaultMessageFilters = { }
27-
28- async function parseDataflashLogFile ( rl , fileStream , fileSize , webContents ) {
82+ let logData : Messages | null = null
83+ let defaultMessageFilters : Record < string , unknown > = { }
84+
85+ async function parseDataflashLogFile (
86+ rl : readline . Interface ,
87+ fileStream : fs . ReadStream ,
88+ fileSize : number ,
89+ webContents : WebContents ,
90+ ) : Promise < Messages > {
2991 // https://ardupilot.org/copter/docs/logmessages.html
3092 // https://github.com/ArduPilot/ardupilot/tree/master/libraries/AP_Logger
3193
3294 return new Promise ( ( resolve , reject ) => {
3395 const stringTypes = new Set ( [ "n" , "N" , "Z" , "M" ] )
34- let aircraftType = null
96+ let aircraftType : string | null = null
3597 let lastUpdateTime = 0
3698
37- const formatMessages = { }
38- const messages = { }
39- const units = { }
99+ const formatMessages : { [ key : string ] : FormatMessage } = { }
100+ const messages : Messages = { }
101+ const units : { [ key : string ] : string } = { }
40102
41- rl . on ( "line" , ( line ) => {
103+ rl . on ( "line" , ( line : string ) => {
42104 // Skip empty lines early
43105 if ( ! line || line . length < 3 ) return
44106
@@ -116,7 +178,7 @@ async function parseDataflashLogFile(rl, fileStream, fileSize, webContents) {
116178 messages [ messageName ] = [ ]
117179 }
118180
119- const messageObj = {
181+ const messageObj : MessageObject = {
120182 name : messageName ,
121183 type : formatMessage . type ,
122184 }
@@ -144,7 +206,7 @@ async function parseDataflashLogFile(rl, fileStream, fileSize, webContents) {
144206 }
145207 }
146208
147- messages [ messageName ] . push ( messageObj )
209+ ; ( messages [ messageName ] as MessageObject [ ] ) . push ( messageObj )
148210 }
149211 }
150212
@@ -168,26 +230,26 @@ async function parseDataflashLogFile(rl, fileStream, fileSize, webContents) {
168230 resolve ( messages )
169231 } )
170232
171- rl . on ( "error" , ( err ) => {
233+ rl . on ( "error" , ( err : Error ) => {
172234 console . error ( "Error reading log file:" , err )
173235 reject ( err )
174236 } )
175237 } )
176238}
177239
178240async function parseFgcsTelemetryLogFile (
179- rl ,
180- fileStream ,
181- fileSize ,
182- webContents ,
183- ) {
184- const formatMessages = { }
185- const messages = { }
241+ rl : readline . Interface ,
242+ fileStream : fs . ReadStream ,
243+ fileSize : number ,
244+ webContents : WebContents ,
245+ ) : Promise < Messages > {
246+ const formatMessages : { [ key : string ] : FormatMessage } = { }
247+ const messages : Messages = { }
186248
187249 let lastUpdateTime = 0
188250
189251 return new Promise ( ( resolve , reject ) => {
190- rl . on ( "line" , ( line ) => {
252+ rl . on ( "line" , ( line : string ) => {
191253 if ( ! line || line . length < 5 || line . includes ( "==" ) ) {
192254 return
193255 }
@@ -210,7 +272,7 @@ async function parseFgcsTelemetryLogFile(
210272
211273 // Get field names and cache format
212274 if ( ! formatMessages [ messageName ] ) {
213- const fields = [ ]
275+ const fields : string [ ] = [ ]
214276 for ( let i = 0 ; i < messageData . length ; i ++ ) {
215277 const keyVal = messageData [ i ] ?. trim ( )
216278 if ( keyVal ) {
@@ -220,10 +282,16 @@ async function parseFgcsTelemetryLogFile(
220282 }
221283 }
222284 }
223- formatMessages [ messageName ] = { fields }
285+ formatMessages [ messageName ] = {
286+ fields,
287+ length : 0 ,
288+ name : messageName ,
289+ type : 0 ,
290+ format : "" ,
291+ }
224292 }
225293
226- const messageObj = {
294+ const messageObj : MessageObject = {
227295 TimeUS : Math . round ( timestamp * 1000 ) , // Use round instead of parseInt for better precision
228296 name : messageName ,
229297 }
@@ -247,7 +315,7 @@ async function parseFgcsTelemetryLogFile(
247315 messages [ messageName ] = [ ]
248316 }
249317
250- messages [ messageName ] . push ( messageObj )
318+ ; ( messages [ messageName ] as MessageObject [ ] ) . push ( messageObj )
251319
252320 const now = Date . now ( )
253321 if ( now - lastUpdateTime > UPDATE_THROTTLE_MS ) {
@@ -267,18 +335,18 @@ async function parseFgcsTelemetryLogFile(
267335 resolve ( messages )
268336 } )
269337
270- rl . on ( "error" , ( err ) => {
338+ rl . on ( "error" , ( err : Error ) => {
271339 console . error ( "Error reading log file:" , err )
272340 reject ( err )
273341 } )
274342 } )
275343}
276344
277- function determineLogFileType ( filePath , firstLine ) {
345+ function determineLogFileType ( filePath : string , firstLine : string ) : LogType {
278346 const reFileExtension = / (?: \. ( [ ^ . ] + ) ) ? $ / // https://stackoverflow.com/a/680982
279- const ext = reFileExtension . exec ( filePath ) [ 1 ]
347+ const ext = reFileExtension . exec ( filePath ) ?. [ 1 ]
280348
281- const extensionToTypeMap = {
349+ const extensionToTypeMap : { [ key : string ] : LogType } = {
282350 log : "dataflash" ,
283351 ftlog : "fgcs_telemetry" ,
284352 // exclude txt from map as txt's are ambiguous
@@ -304,21 +372,21 @@ function determineLogFileType(filePath, firstLine) {
304372}
305373
306374// New function to get recent files
307- export function getRecentFiles ( ) {
375+ export function getRecentFiles ( ) : string [ ] {
308376 return recentLogsManager . getRecentLogs ( )
309377}
310378
311379// New function to clear recent files
312- export function clearRecentFiles ( ) {
380+ export function clearRecentFiles ( ) : void {
313381 recentLogsManager . clearRecentLogs ( )
314382}
315383
316- async function getFirstLine ( pathToFile ) {
384+ async function getFirstLine ( pathToFile : string ) : Promise < string > {
317385 // https://stackoverflow.com/a/60193465/23139916
318386 const readable = fs . createReadStream ( pathToFile )
319387 const reader = readline . createInterface ( { input : readable } )
320- const line = await new Promise ( ( resolve ) => {
321- reader . on ( "line" , ( line ) => {
388+ const line = await new Promise < string > ( ( resolve ) => {
389+ reader . on ( "line" , ( line : string ) => {
322390 reader . close ( )
323391 resolve ( line )
324392 } )
@@ -328,10 +396,14 @@ async function getFirstLine(pathToFile) {
328396}
329397
330398// function to process and save the log file data
331- function processAndSaveLogData ( loadedLogMessages , logType ) {
399+ function processAndSaveLogData (
400+ loadedLogMessages : Messages ,
401+ logType : string ,
402+ ) : LogSummary {
332403 clearUnitCache ( ) // Clear cache when loading new file
333- const aircraftType = loadedLogMessages . aircraftType
334- delete loadedLogMessages . aircraftType
404+ const aircraftType =
405+ ( loadedLogMessages [ "aircraftType" ] as string | null ) || null
406+ delete loadedLogMessages [ "aircraftType" ]
335407
336408 const initialFilters = buildDefaultMessageFilters ( loadedLogMessages )
337409
@@ -351,7 +423,7 @@ function processAndSaveLogData(loadedLogMessages, logType) {
351423
352424 // Convert TimeUS to TimeUTC if GPS data is available
353425 let finalMessages = { ...expandedMessages }
354- let gpsOffset = null
426+ let gpsOffset : number | null = null
355427 let utcAvailable = false
356428 if ( finalMessages . GPS && logType === "dataflash" ) {
357429 gpsOffset = calcGPSOffset ( finalMessages )
@@ -375,7 +447,7 @@ function processAndSaveLogData(loadedLogMessages, logType) {
375447 return {
376448 formatMessages : finalFormats ,
377449 utcAvailable,
378- logEvents : finalMessages [ "EV" ] || [ ] ,
450+ logEvents : ( finalMessages [ "EV" ] as MessageObject [ ] ) || [ ] ,
379451 flightModeMessages,
380452 logType,
381453 messageFilters : defaultMessageFilters ,
@@ -384,7 +456,10 @@ function processAndSaveLogData(loadedLogMessages, logType) {
384456 }
385457}
386458
387- export default async function openFile ( event , filePath ) {
459+ export default async function openFile (
460+ event : { sender : WebContents } ,
461+ filePath : string | null ,
462+ ) : Promise < ParseResult > {
388463 if ( filePath == null ) {
389464 return { success : false , error : "No file path provided" }
390465 }
@@ -410,7 +485,7 @@ export default async function openFile(event, filePath) {
410485 crlfDelay : Infinity ,
411486 } )
412487
413- let messages = null
488+ let messages : Messages | null = null
414489
415490 if ( logType === "dataflash" ) {
416491 messages = await parseDataflashLogFile (
@@ -446,14 +521,19 @@ export default async function openFile(event, filePath) {
446521 } else {
447522 return { success : false , error : "Failed to parse log file" }
448523 }
449- } catch ( err ) {
524+ } catch ( err : unknown ) {
450525 console . error ( "Error parsing log file:" , err )
451- return { success : false , error : err . message || "Unknown parsing error" }
526+ const errorMessage =
527+ err instanceof Error ? err . message : "Unknown parsing error"
528+ return { success : false , error : errorMessage }
452529 }
453530}
454531
455532// on-demand retrieval of messages
456- export async function getMessages ( _event , requestedMessages ) {
533+ export async function getMessages (
534+ _event : unknown ,
535+ requestedMessages : string [ ] ,
536+ ) : Promise < Dataset [ ] | { success : false ; error : string } > {
457537 // each requestedMessage should be of the form `${requestedMessageName}/${requestedFieldName}`
458538 // like ['ARM/ArmState', 'ARSP/Airspeed']
459539
@@ -472,9 +552,10 @@ export async function getMessages(_event, requestedMessages) {
472552 return [ ]
473553 }
474554
475- const formatMessages = logData . format || { }
476- const units = logData . units || { }
477- const datasets = [ ]
555+ const formatMessages =
556+ ( logData . format as { [ key : string ] : FormatMessage } ) || { }
557+ const units = ( logData . units as { [ key : string ] : string } ) || { }
558+ const datasets : Dataset [ ] = [ ]
478559
479560 // Loop through the list of requested messages and transform each of them
480561 for (
@@ -494,7 +575,7 @@ export async function getMessages(_event, requestedMessages) {
494575 const fmt = formatMessages [ categoryName ]
495576 const hasField = fmt ?. fields . includes ( fieldName )
496577
497- const series = logData [ categoryName ]
578+ const series = logData [ categoryName ] as MessageObject [ ]
498579
499580 if ( ! hasField || ! Array . isArray ( series ) || series . length === 0 ) {
500581 // Skip unknown or unavailable labels
0 commit comments