Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ jobs:
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y rpm libarchive-tools

# Download ONLY the appropriate backend for this platform
- name: Download Linux backend
Expand Down Expand Up @@ -188,8 +187,6 @@ jobs:
electron-app/dist/*.exe
electron-app/dist/*.AppImage
electron-app/dist/*.deb
electron-app/dist/*.rpm
electron-app/dist/*.pacman
Comment thread
maximka76667 marked this conversation as resolved.
electron-app/dist/*.dmg
electron-app/dist/*.zip
electron-app/dist/*.yml
Expand Down
25 changes: 13 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Hyperloop Control Station H11
![Testing View](https://raw.githubusercontent.com/Hyperloop-UPV/webpage/5c1c827d82d380689856ee61af43da30da22e0fc/src/assets/backgrounds/testing-view.png)

![Testing View](https://raw.githubusercontent.com/Hyperloop-UPV/webpage/5c1c827d82d380689856ee61af43da30da22e0fc/src/assets/backgrounds/testing-view.png)

## Monorepo usage

Expand All @@ -20,17 +21,17 @@ Before starting, ensure you have the following installed:

Our `pnpm-workspace.yaml` defines the following workspaces:

| Workspace | Language | Description |
| :----------------------------- | :------- | :--------------------------------------------- |
| `testing-view` | TS/React | Web interface for telemetry testing |
| `competition-view` | TS/React | UI for the competition |
| `backend` | Go | Data ingestion and pod communication server |
| `packet-sender` | Rust | Utility for simulating vehicle packets |
| `electron-app` | JS | The main Control Station desktop application |
| `@workspace/ui` | TS/React | Shared UI component library (frontend-kit) |
| `@workspace/core` | TS | Shared business logic and types (frontend-kit) |
| `@workspace/eslint-config` | ESLint | Common ESLint configuration (frontend-kit) |
| `@workspace/typescript-config` | TS | Common TypeScript configuration (frontend-kit) |
| Workspace | Language | Description |
| :----------------------------- | :------- | :---------------------------------------------------- |
| `testing-view` | TS/React | Web interface for telemetry testing |
| `competition-view` | TS/React | UI for the competition |
| `backend` | Go | Data ingestion and pod communication server |
| `packet-sender` | Rust | Utility for simulating vehicle packets |
| `hyperloop-control-station` | JS | The main Control Station electron desktop application |
| `@workspace/ui` | TS/React | Shared UI component library (frontend-kit) |
| `@workspace/core` | TS | Shared business logic and types (frontend-kit) |
| `@workspace/eslint-config` | ESLint | Common ESLint configuration (frontend-kit) |
| `@workspace/typescript-config` | TS | Common TypeScript configuration (frontend-kit) |

---

Expand Down
3 changes: 2 additions & 1 deletion electron-app/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,5 @@ coverage

# Config and config backups
config.toml
config.toml.backup-*
config.toml.backup-*
version.toml
8 changes: 2 additions & 6 deletions electron-app/BUILD.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
# Hyperloop Control Station Build System

The project uses a unified, modular build script (`electron-app/build.mjs`) to handle building the backend (Go), packet sender (Rust), and frontends (React/Vite) for the Electron application.
The project uses a unified, modular build script (`electron-app/build.mjs`) to handle building the backend (Go), and frontends (React/Vite) for the Electron application.

## Prerequisites

- **Node.js** & **pnpm**
- **Go** (1.21+)
- **Rust/Cargo** (for Packet Sender)

## Basic Usage

Run the build script from the `electron-app` directory (or via npm scripts).

```sh
# Build EVERYTHING (Backend, Packet Sender, Frontends)
# Build EVERYTHING (Backend, Frontends)
pnpm build

# OR
Expand All @@ -34,9 +33,6 @@ node build.mjs --backend

# Build only the Testing View
node build.mjs --testing-view

# Build only the Packet Sender
node build.mjs --packet-sender
```

## Platform Targeting
Expand Down
15 changes: 13 additions & 2 deletions electron-app/build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -222,13 +222,21 @@ scriptArgs.forEach((arg) => {
});

const specificTargets = Object.keys(CONFIG).filter((key) =>
scriptArgs.includes(`--${key}`)
scriptArgs.includes(`--${key}`),
);
const targetsToBuild =
specificTargets.length > 0 ? specificTargets : Object.keys(CONFIG);

// --- Main Execution ---

console.log(`
______ __ ______ _____ ____________ __
___ / / /____ ________________________ /___________________ __ / / /__ __ \\_ | / /
__ /_/ /__ / / /__ __ \\ _ \\_ ___/_ /_ __ \\ __ \\__ __ \\ _ / / /__ /_/ /_ | / /
_ __ / _ /_/ /__ /_/ / __/ / _ / / /_/ / /_/ /_ /_/ / / /_/ / _ ____/__ |/ /
/_/ /_/ _\\__, / _ .___/\\___//_/ /_/ \\____/\\____/_ .___/ \\____/ /_/ _____/
/____/ /_/ /_/ `);

logger.header("Hyperloop Control Station Build");

(async () => {
Expand All @@ -254,7 +262,10 @@ logger.header("Hyperloop Control Station Build");

if (frontendBuilt && !process.env.CI) {
logger.info("Finalizing Electron...");
run("pnpm --filter electron-app install --frozen-lockfile", __dirname);
run(
"pnpm --filter hyperloop-control-station install --frozen-lockfile",
__dirname,
);
}

if (allSuccess) {
Expand Down
13 changes: 5 additions & 8 deletions electron-app/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ if (process.platform === "linux") {
// Setup IPC handlers for renderer process communication
setupIpcHandlers();

app.setName("hyperloop-control-station");

// App lifecycle: wait for Electron to be ready
app.whenReady().then(async () => {
// Get the screen width and height
Expand All @@ -59,7 +57,6 @@ app.whenReady().then(async () => {
logger.electron.header("Backend process spawned");
} catch (error) {
// Start backend already shows these errors
return;
}

// Create main application window
Expand Down Expand Up @@ -118,11 +115,11 @@ app.on("window-all-closed", () => {
});

// Cleanup before app quits
app.on("before-quit", () => {
// Stop backend process gracefully
stopBackend();
// Stop packet sender process gracefully
stopPacketSender();
app.on("before-quit", (e) => {
e.preventDefault();
Promise.all([stopBackend(), stopPacketSender()])
.catch((error) => logger.electron.error("Error during shutdown:", error))
.finally(() => app.exit());
});

// Handle uncaught exceptions globally
Expand Down
8 changes: 3 additions & 5 deletions electron-app/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "electron-app",
"name": "hyperloop-control-station",
"version": "1.0.0",
"description": "Hyperloop UPV Control Station",
"main": "main.js",
Expand Down Expand Up @@ -57,7 +57,7 @@
"owner": "Hyperloop-UPV",
"repo": "software"
},
"productName": "Hyperloop-Control-Station",
"productName": "Hyperloop-Ctrl",
"directories": {
"output": "dist"
},
Expand Down Expand Up @@ -107,9 +107,7 @@
"linux": {
"target": [
"AppImage",
"deb",
"rpm",
"pacman"
"deb"
],
"icon": "icons/512x512.png",
"category": "Utility",
Expand Down
8 changes: 6 additions & 2 deletions electron-app/preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ contextBridge.exposeInMainWorld("electronAPI", {
// Open folder selection dialog
selectFolder: () => ipcRenderer.invoke("select-folder"),
// Receive log message from backend
onLog: (callback) =>
ipcRenderer.on("log", (_event, value) => callback(value)),
onLog: (callback) => {
const listener = (_event, value) => callback(value);
ipcRenderer.removeAllListeners("log");
ipcRenderer.on("log", listener);
return () => ipcRenderer.removeListener("log", listener);
},
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expect, beforeEach, vi } from "vitest";
import fs from "fs";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { ConfigManager } from "../configManager.js";

// Mock fs module
Expand All @@ -8,15 +8,24 @@ vi.mock("fs");
describe("ConfigManager - Initialization", () => {
const templatePath = "/path/to/template.toml";
const userConfigPath = "/path/to/user.toml";
const versionFilePath = "/path/to/version.toml";
const appVersion = "1.0.0";
const appVersionReturnValue = `version = "${appVersion}"`;

beforeEach(() => {
vi.clearAllMocks();
});

it("should create config manager instance", () => {
fs.existsSync.mockReturnValue(true);
fs.readFileSync.mockReturnValue(appVersionReturnValue);

const manager = new ConfigManager(userConfigPath, templatePath);
const manager = new ConfigManager(
userConfigPath,
templatePath,
versionFilePath,
appVersion,
);

expect(manager.userConfigPath).toBe(userConfigPath);
expect(manager.templatePath).toBe(templatePath);
Expand All @@ -28,8 +37,9 @@ describe("ConfigManager - Initialization", () => {
return true;
});
fs.mkdirSync.mockImplementation(() => {});
fs.readFileSync.mockReturnValue(appVersionReturnValue);

new ConfigManager(userConfigPath, templatePath);
new ConfigManager(userConfigPath, templatePath, versionFilePath, appVersion);

expect(fs.mkdirSync).toHaveBeenCalledWith("/path/to", {
recursive: true,
Expand All @@ -39,17 +49,17 @@ describe("ConfigManager - Initialization", () => {
it("should copy template if user config does not exist", () => {
fs.existsSync.mockImplementation((path) => {
if (path === userConfigPath) return false;
if (path === templatePath) return true;
return true;
});
fs.copyFileSync.mockImplementation(() => {});
fs.writeFileSync.mockImplementation(() => {});
const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => {});

new ConfigManager(userConfigPath, templatePath);
new ConfigManager(userConfigPath, templatePath, versionFilePath, appVersion);

expect(fs.copyFileSync).toHaveBeenCalledWith(templatePath, userConfigPath);
expect(consoleSpy).toHaveBeenCalledWith(
expect.stringContaining("Created config from template")
expect.stringContaining("Created config from template"),
);

consoleSpy.mockRestore();
Expand All @@ -59,7 +69,7 @@ describe("ConfigManager - Initialization", () => {
fs.existsSync.mockReturnValue(false);

expect(() => {
new ConfigManager(userConfigPath, templatePath);
new ConfigManager(userConfigPath, templatePath, versionFilePath, appVersion);
}).toThrow("Template not found");
});
});
Loading
Loading