Skip to content

Commit 84ed6b2

Browse files
merge thingy
2 parents f2f911d + c50baaf commit 84ed6b2

49 files changed

Lines changed: 1360 additions & 357 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

gcs/electron/fla.ts

Lines changed: 100 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import type {
2828
LogType,
2929
MessageObject,
3030
Messages,
31+
ParamObject,
3132
ParseResult,
3233
RecentLog,
3334
} from "./types/flaTypes"
@@ -45,7 +46,7 @@ import {
4546
} from "./utils/flaUtils"
4647

4748
const UPDATE_THROTTLE_MS = 100 // Update every 100ms
48-
const recentLogsManager = createRecentLogsManager()
49+
const recentLogsManager = createRecentLogsManager(20)
4950
let logData: Messages | null = null
5051
let defaultMessageFilters: Record<string, unknown> = {}
5152

@@ -128,6 +129,7 @@ async function parseDataflashLogFile(
128129
// File data
129130
} else if (messageName === "MSG") {
130131
// MSG data
132+
// TODO: Use "VER" message for this data
131133
const text = splitLineData[2]?.trim()
132134

133135
if (aircraftType === null && text) {
@@ -138,43 +140,39 @@ async function parseDataflashLogFile(
138140
aircraftType = "copter"
139141
}
140142
}
141-
} else {
142-
// Message data
143-
const formatMessage = formatMessages[messageName]
144-
if (formatMessage) {
145-
if (!messages[messageName]) {
146-
messages[messageName] = []
147-
}
143+
}
148144

149-
const messageObj: MessageObject = {
150-
name: messageName,
151-
}
145+
// Message data
146+
const formatMessage = formatMessages[messageName]
147+
if (formatMessage) {
148+
if (!messages[messageName]) {
149+
messages[messageName] = []
150+
}
152151

153-
const fields = formatMessage.fields
154-
const format = formatMessage.format
155-
const fieldsLength = fields.length
156-
157-
for (
158-
let i = 0;
159-
i < fieldsLength && i < splitLineData.length - 1;
160-
i++
161-
) {
162-
const field = fields[i]
163-
const formatType = format[i]
164-
const value = splitLineData[i + 1]?.trim()
165-
166-
if (value !== undefined && value !== "") {
167-
if (stringTypes.has(formatType)) {
168-
messageObj[field] = value
169-
} else {
170-
const numValue = parseFloat(value)
171-
messageObj[field] = isNaN(numValue) ? 0 : numValue
172-
}
152+
const messageObj: MessageObject = {
153+
name: messageName,
154+
}
155+
156+
const fields = formatMessage.fields
157+
const format = formatMessage.format
158+
const fieldsLength = fields.length
159+
160+
for (let i = 0; i < fieldsLength && i < splitLineData.length - 1; i++) {
161+
const field = fields[i]
162+
const formatType = format[i]
163+
const value = splitLineData[i + 1]?.trim()
164+
165+
if (value !== undefined && value !== "") {
166+
if (stringTypes.has(formatType)) {
167+
messageObj[field] = value
168+
} else {
169+
const numValue = parseFloat(value)
170+
messageObj[field] = isNaN(numValue) ? 0 : numValue
173171
}
174172
}
175-
176-
;(messages[messageName] as MessageObject[]).push(messageObj)
177173
}
174+
175+
;(messages[messageName] as MessageObject[]).push(messageObj)
178176
}
179177

180178
const now = Date.now()
@@ -375,6 +373,17 @@ function parseDataflashBinFile(
375373
aircraftType: getAircraftTypeFromMavType(parser.getMavType()),
376374
...transformedMessages,
377375
}
376+
377+
if (
378+
Array.isArray(parsedData?.PARM) &&
379+
parsedData.PARM.some(
380+
(param: MessageObject) =>
381+
param.Name === "Q_ENABLE" && param.Value === 1,
382+
)
383+
) {
384+
parsedData.aircraftType = "quadplane"
385+
}
386+
378387
webContents.send("fla:log-parse-progress", {
379388
percent: 100,
380389
})
@@ -424,11 +433,6 @@ export function getRecentFiles(): RecentLog[] {
424433
return recentLogsManager.getRecentLogs()
425434
}
426435

427-
// New function to clear recent files
428-
export function clearRecentFiles(): void {
429-
recentLogsManager.clearRecentLogs()
430-
}
431-
432436
async function getFirstLine(pathToFile: string): Promise<string> {
433437
// https://stackoverflow.com/a/60193465/23139916
434438
const readable = fs.createReadStream(pathToFile)
@@ -684,8 +688,12 @@ export async function getMessages(
684688

685689
for (let j = 0; j < len; j++) {
686690
const point = series[j]
691+
// Use UtcTimeUS if available, otherwise fall back to TimeUS
692+
const timeValue =
693+
point.UtcTimeUS !== undefined ? point.UtcTimeUS : point.TimeUS
694+
687695
// making sure all the entries are numbers
688-
x[j] = typeof point.TimeUS === "number" ? point.TimeUS : 0
696+
x[j] = typeof timeValue === "number" ? timeValue : 0
689697
y[j] = typeof point[fieldName] === "number" ? point[fieldName] : 0
690698
}
691699

@@ -699,3 +707,56 @@ export async function getMessages(
699707

700708
return datasets
701709
}
710+
711+
export async function getMessageDataForTable(
712+
_event: unknown,
713+
requestedMessage: string,
714+
): Promise<MessageObject[] | { success: false; error: string }> {
715+
if (!logData) {
716+
console.error("getMessageDataForTable: logData is null or undefined.")
717+
return { success: false, error: "Log data not loaded." }
718+
}
719+
720+
if (!requestedMessage.trim()) {
721+
return { success: false, error: "Invalid message type." }
722+
}
723+
724+
const series = logData[requestedMessage] as MessageObject[]
725+
if (!Array.isArray(series) || series.length === 0) {
726+
return { success: false, error: "No data available for this message type." }
727+
}
728+
729+
return series
730+
}
731+
732+
export async function saveParamsToFile(
733+
_event: unknown,
734+
filePath: string,
735+
params: Array<ParamObject>,
736+
): Promise<{ success: boolean; message: string }> {
737+
try {
738+
// Sort params alphabetically by name
739+
const sortedParams = [...params].sort((a, b) =>
740+
a.name.localeCompare(b.name),
741+
)
742+
743+
// Write params to file in the format: PARAM_NAME,value
744+
const content = sortedParams
745+
.map((param) => `${param.name.toUpperCase()},${param.value}`)
746+
.join("\n")
747+
748+
await fs.promises.writeFile(filePath, content + "\n", "utf-8")
749+
750+
console.log(`Parameters saved to ${filePath}`)
751+
return {
752+
success: true,
753+
message: `Parameters saved successfully to ${filePath}`,
754+
}
755+
} catch (error) {
756+
console.error("Failed to save params to file:", error)
757+
return {
758+
success: false,
759+
message: `Failed to save parameters: ${error instanceof Error ? error.message : String(error)}`,
760+
}
761+
}
762+
}

gcs/electron/main.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ import fs from "node:fs"
1616
import path from "node:path"
1717
import packageInfo from "../package.json"
1818

19-
import openFile, { clearRecentFiles, getMessages, getRecentFiles } from "./fla"
19+
import openFile, {
20+
getMessageDataForTable,
21+
getMessages,
22+
getRecentFiles,
23+
saveParamsToFile,
24+
} from "./fla"
2025
import registerAboutIPC, {
2126
destroyAboutWindow,
2227
openAboutPopout,
@@ -28,6 +33,9 @@ import registerFFmpegBinaryIPC from "./modules/ffmpegBinary"
2833
import registerFlaParamsIPC, {
2934
destroyFlaParamsWindow,
3035
} from "./modules/flaParamsWindow"
36+
import registerGraphWindowIPC, {
37+
destroyAllGraphWindows,
38+
} from "./modules/graphWindow"
3139
import registerLinkStatsIPC, {
3240
destroyLinkStatsWindow,
3341
openLinkStatsWindow,
@@ -40,9 +48,6 @@ import registerVibeStatusIPC, {
4048
} from "./modules/vibeStatusWindow"
4149
import registerVideoIPC, { destroyVideoWindow } from "./modules/videoWindow"
4250
import { readParamsFile } from "./utils/paramsFile"
43-
import registerGraphWindowIPC, {
44-
destroyAllGraphWindows,
45-
} from "./modules/graphWindow"
4651

4752
// Check if required data files exist
4853
function checkRequiredDataFiles(): {
@@ -606,12 +611,16 @@ app.whenReady().then(() => {
606611
return []
607612
}
608613
})
609-
// Clear recent logs
610-
ipcMain.handle("fla:clear-recent-logs", clearRecentFiles)
611614

612615
// Load Messages on demand
613616
ipcMain.handle("fla:get-messages", getMessages)
614617

618+
// Get message data for table
619+
ipcMain.handle("fla:get-message-data-for-table", getMessageDataForTable)
620+
621+
// Save FLA params to file
622+
ipcMain.handle("fla:save-params-to-file", saveParamsToFile)
623+
615624
// Open native save dialog
616625
ipcMain.handle("app:get-save-file-path", async (event, options) => {
617626
const window = BrowserWindow.fromWebContents(event.sender)

gcs/electron/modules/aboutWindow.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { BrowserWindow, ipcMain, shell } from "electron"
22
import path from "path"
3+
import { getCenteredWindowPosition } from "../utils/windowUtils"
34

45
let aboutPopoutWin: BrowserWindow | null = null
56

67
const VITE_DEV_SERVER_URL = process.env["VITE_DEV_SERVER_URL"]
78

8-
export function openAboutPopout() {
9+
export function openAboutPopout(parentWindow?: BrowserWindow) {
910
if (aboutPopoutWin === null) {
10-
aboutPopoutWin = new BrowserWindow({
11+
const windowOptions: Electron.BrowserWindowConstructorOptions = {
1112
width: 400,
1213
height: 300,
1314
frame: true,
@@ -20,7 +21,20 @@ export function openAboutPopout() {
2021
},
2122
fullscreen: false,
2223
fullscreenable: false,
23-
})
24+
}
25+
26+
// Position window in the center of the parent window
27+
const centeredPosition = getCenteredWindowPosition(
28+
parentWindow,
29+
windowOptions.width!,
30+
windowOptions.height!,
31+
)
32+
if (centeredPosition) {
33+
windowOptions.x = centeredPosition.x
34+
windowOptions.y = centeredPosition.y
35+
}
36+
37+
aboutPopoutWin = new BrowserWindow(windowOptions)
2438
}
2539

2640
if (VITE_DEV_SERVER_URL) {
@@ -57,8 +71,9 @@ export default function registerAboutIPC() {
5771
ipcMain.removeHandler("app:open-about-window")
5872
ipcMain.removeHandler("app:close-about-window")
5973

60-
ipcMain.handle("app:open-about-window", () => {
61-
openAboutPopout()
74+
ipcMain.handle("app:open-about-window", (event) => {
75+
const parentWindow = BrowserWindow.fromWebContents(event.sender)
76+
openAboutPopout(parentWindow || undefined)
6277
})
6378
ipcMain.handle("app:close-about-window", () => closeAboutPopout())
6479
}

gcs/electron/modules/ekfStatusWindow.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { BrowserWindow, ipcMain } from "electron"
22
import path from "path"
3+
import { getCenteredWindowPosition } from "../utils/windowUtils"
34

45
const VITE_DEV_SERVER_URL = process.env["VITE_DEV_SERVER_URL"]
56

67
let ekfStatusWin: BrowserWindow | null = null
78

8-
export function openEkfStatusWindow() {
9+
export function openEkfStatusWindow(parentWindow?: BrowserWindow) {
910
if (ekfStatusWin === null) {
10-
ekfStatusWin = new BrowserWindow({
11+
const windowOptions: Electron.BrowserWindowConstructorOptions = {
1112
width: 700,
1213
height: 400,
1314
frame: true,
@@ -21,7 +22,20 @@ export function openEkfStatusWindow() {
2122
fullscreen: false,
2223
fullscreenable: false,
2324
alwaysOnTop: true,
24-
})
25+
}
26+
27+
// Position window in the center of the parent window
28+
const centeredPosition = getCenteredWindowPosition(
29+
parentWindow,
30+
windowOptions.width!,
31+
windowOptions.height!,
32+
)
33+
if (centeredPosition) {
34+
windowOptions.x = centeredPosition.x
35+
windowOptions.y = centeredPosition.y
36+
}
37+
38+
ekfStatusWin = new BrowserWindow(windowOptions)
2539
}
2640

2741
if (VITE_DEV_SERVER_URL) {
@@ -51,8 +65,9 @@ export default function registerEkfStatusIPC() {
5165
ipcMain.removeHandler("app:close-ekf-status-window")
5266
ipcMain.removeHandler("app:update-ekf-status")
5367

54-
ipcMain.handle("app:open-ekf-status-window", () => {
55-
openEkfStatusWindow()
68+
ipcMain.handle("app:open-ekf-status-window", (event) => {
69+
const parentWindow = BrowserWindow.fromWebContents(event.sender)
70+
openEkfStatusWindow(parentWindow || undefined)
5671
})
5772
ipcMain.handle("app:close-ekf-status-window", () => closeEkfStatusWindow())
5873
ipcMain.handle("app:update-ekf-status", (_, ekfStatusData) => {

0 commit comments

Comments
 (0)