Skip to content

Commit

Permalink
Merge pull request #174 from the-orange-alliance/jan-app
Browse files Browse the repository at this point in the history
Jan app
  • Loading branch information
kyle-flynn authored Sep 27, 2024
2 parents f2c16e0 + 3a67ef0 commit 40f36aa
Show file tree
Hide file tree
Showing 6 changed files with 442 additions and 198 deletions.
41 changes: 36 additions & 5 deletions back-end/realtime/src/rooms/FCS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import {
FieldControlUpdatePacket,
FeedingTheFutureFCS,
Match as MatchObj,
MatchSocketEvent
MatchSocketEvent,
FieldControlStatus
} from '@toa-lib/models';
import Room from './Room.js';
import Match from './Match.js';
Expand All @@ -27,6 +28,7 @@ export default class FCS extends Room {
private previousMatchDetails: FeedingTheFuture.MatchDetails =
FeedingTheFuture.defaultMatchDetails;
private packetManager: FeedingTheFutureFCS.PacketManager;
private status: FieldControlStatus = { wleds: {} };

public constructor(server: Server, matchRoom: Match) {
super(server, 'fcs');
Expand All @@ -40,10 +42,25 @@ export default class FCS extends Room {
// Connect to wled websocket servers if there are wleds
Object.entries(this.packetManager.getInitPacket().wleds).forEach((wled) => {
const isProd = environment.get().nodeEnv === 'production';
const path = isProd ? `${__dirname}/worker/index.js` : join(__dirname, "../../build/util/WLEDWorker/worker.js");
const path = isProd
? `${__dirname}/worker/index.js`
: join(__dirname, '../../build/util/WLEDWorker/worker.js');
logger.verbose(`Creating WLED worker for ${wled[0]} at ${path}`);
this.wledControllers[wled[0]] = new Worker(path, { workerData: wled[1] });
// this.wledControllers[wled[0]] = new WledController(wled[1]);

// Set up wled statuses
this.status.wleds[wled[0]] = {
connected: false,
stickyLostConnection: false
};
this.wledControllers[wled[0]].on('message', (message) => {
if (message.type === 'status') {
if (!message.data.connected)
this.status.wleds[wled[0]].stickyLostConnection = true;
this.status.wleds[wled[0]].connected = message.data.connected;
this.broadcast().emit('fcs:status', this.status);
}
});
});

matchRoom.localEmitter.on(
Expand Down Expand Up @@ -107,14 +124,25 @@ export default class FCS extends Room {
Object.entries(this.packetManager.getInitPacket().wleds).forEach(
(wled) => {
// this.wledControllers[wled[0]].initialize(wled[1]);
this.wledControllers[wled[0]].postMessage({ type: "initialize", data: wled[1] });
this.wledControllers[wled[0]].postMessage({
type: 'initialize',
data: wled[1]
});
}
);
}
);

socket.on('fcs:digitalInputs', this.packetManager.handleDigitalInputs);

socket.on('fcs:clearStatus', () => {
Object.entries(this.status.wleds).forEach((wled) => {
this.status.wleds[wled[0]].stickyLostConnection =
!this.status.wleds[wled[0]].connected;
});
socket.emit('fcs:status', this.status);
});

socket.emit('fcs:update', this.latestFcsStatus);
}

Expand All @@ -124,7 +152,10 @@ export default class FCS extends Room {
// Handle wleds
Object.entries(update.wleds).forEach((wled) => {
// this.wledControllers[wled[0]].update(wled[1]);
this.wledControllers[wled[0]].postMessage({ type: "update", data: wled[1] });
this.wledControllers[wled[0]].postMessage({
type: 'update',
data: wled[1]
});
});

// Update this.latestFcsStatus AFTER sending out the new update
Expand Down
17 changes: 15 additions & 2 deletions back-end/realtime/src/util/WLEDWorker/WLEDController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
buildWledSetColorPacket
} from '../WLEDHelper.js';
import WebSocket from 'ws';
import { EventEmitter } from 'node:events';

export class WledController {
private static heartbeatPeriodMs = 500;
Expand All @@ -22,12 +23,15 @@ export class WledController {

private lastTimestamp: number | null;

public readonly eventEmitter: EventEmitter;

constructor(initPacket: WledInitParameters) {
this.initPacket = initPacket;
this.keepAlive = null;
this.heartbeat = null;
this.socket = null;
this.lastTimestamp = null;
this.eventEmitter = new EventEmitter();
}

public initialize(initPacket?: WledInitParameters): void {
Expand All @@ -50,6 +54,7 @@ export class WledController {
if (!this.socket) return;
if (this.socket.readyState === 0) return;
this.connected = true;
this.emitStatus();
logger.info(`${this.getName()} === connected ===`);
try {
this.socket?.send(buildWledInitializationPacket(this.initPacket));
Expand All @@ -68,14 +73,15 @@ export class WledController {
this.socket.onclose = () => {
logger.error(`${this.getName()} disconnected`);
this.connected = false;
this.emitStatus();
// If the keepalive loop is running, clear it
if (this.keepAlive) {
clearInterval(this.keepAlive);
this.keepAlive = null;
}
// Attempt to reconnect once the socket has closed
setTimeout(() => this.initialize(), WledController.reconnectPeriodMs);
}
};

this.socket.onerror = (e: WebSocket.ErrorEvent) => {
logger.error(`${this.getName()} failed to connect: ${e.error}`);
Expand Down Expand Up @@ -106,14 +112,15 @@ export class WledController {
private startKeepalive(): void {
logger.info(`${this.getName()} starting keepalive`);
this.keepAlive = setInterval(() => {
if (!this.connected) return
if (!this.connected) return;
if (!this.lastTimestamp) return;
if (
Date.now() - this.lastTimestamp >=
WledController.keepAliveTimeoutMs
) {
logger.error(`${this.getName()} keepalive timeout`);
this.connected = false;
this.emitStatus();
this.socket?.terminate();

if (this.keepAlive) {
Expand Down Expand Up @@ -155,4 +162,10 @@ export class WledController {
const parts = name.split('-');
return name.replace(parts[0] + '-', '');
}

private emitStatus(): void {
this.eventEmitter.emit('status', {
connected: this.connected
});
}
}
40 changes: 23 additions & 17 deletions back-end/realtime/src/util/WLEDWorker/worker.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,34 @@
import { WledInitParameters, WledUpdateParameters } from "@toa-lib/models";
import { workerData, parentPort } from 'worker_threads'
import { WledController } from "./WLEDController.js";
import { WledInitParameters, WledUpdateParameters } from '@toa-lib/models';
import { workerData, parentPort } from 'worker_threads';
import { WledController } from './WLEDController.js';

interface UpdateMessage {
data: WledUpdateParameters
type: "update"
data: WledUpdateParameters;
type: 'update';
}

interface InitializeMessage {
type: "initialize"
data: WledInitParameters
type: 'initialize';
data: WledInitParameters;
}

type Message = UpdateMessage | InitializeMessage
type Message = UpdateMessage | InitializeMessage;

const wled = new WledController(workerData)
wled.initialize()
const wled = new WledController(workerData);
wled.initialize();

wled.eventEmitter.on('status', (status) => {
if (parentPort) {
parentPort?.postMessage({ type: 'status', data: status });
}
});

if (parentPort) {
parentPort.on('message', (message: Message) => {
if (message.type === "update") {
wled.update(message.data)
} else if (message.type === "initialize") {
wled.initialize(message.data)
}
});
parentPort.on('message', (message: Message) => {
if (message.type === 'update') {
wled.update(message.data);
} else if (message.type === 'initialize') {
wled.initialize(message.data);
}
});
}
2 changes: 1 addition & 1 deletion front-end/src/app-routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ const AppRoutes: AppRoute[] = [
element: AudienceDisplayManager
},
{
name: 'Event Monitor',
name: 'Jan App',
path: '/event-monitor',
group: 0,
element: EventMonitor
Expand Down
Loading

0 comments on commit 40f36aa

Please sign in to comment.