Skip to content

Commit

Permalink
feat(spie): create data delimited event
Browse files Browse the repository at this point in the history
  • Loading branch information
robsonos committed Dec 24, 2024
1 parent 83da0b3 commit 7cc4bba
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 28 deletions.
14 changes: 14 additions & 0 deletions apps/spie-ui/src/app/services/serial-port.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,18 @@ export class SerialPortService {
serialPortEvent.type === 'data' || serialPortEvent.type === 'clear'
)
);

dataDelimitedEvent$: Observable<DataEvent> = toObservable(this.isOpen).pipe(
switchMap(() =>
merge(
this.electronService.serialPort.onEvent(),
this.clearDataSubject.pipe(map(() => ({ type: 'clear' } as DataEvent)))
)
),
filter(
(serialPortEvent) =>
serialPortEvent.type === 'data-delimited' ||
serialPortEvent.type === 'clear'
)
);
}
64 changes: 37 additions & 27 deletions apps/spie/src/app/events/serial-port.events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ import type {
SerialPortEventType,
} from '@spie/types';
import { ipcMain } from 'electron';
import { InterByteTimeoutParser, SerialPort } from 'serialport';
import { ReadlineParser, SerialPort } from 'serialport';

export default class SerialPortEvents {
private static serialPort: SerialPort | null = null;
private static parser: InterByteTimeoutParser | null = null;
private static eventListeners = new Map<
SerialPortEventType,
(...args: any[]) => void
Expand All @@ -18,6 +16,9 @@ export default class SerialPortEvents {
SerialPortEventType,
(...args: any[]) => void
>();
private static serialPort: SerialPort | null = null;
private static parser: ReadlineParser | null = null;
private static parserCallback: (data: string) => void | null = null;
private static encoding: Encoding = 'ascii';
private static areListenersRegistered = false;
private static openOptions: OpenOptions | null = null;
Expand All @@ -33,11 +34,7 @@ export default class SerialPortEvents {
serialPortEventType: SerialPortEventType,
callback: (...args: any[]) => void
) => {
if (
!SerialPortEvents.serialPort ||
!SerialPortEvents.parser ||
!SerialPortEvents.serialPort.isOpen
) {
if (!SerialPortEvents.serialPort || !SerialPortEvents.serialPort.isOpen) {
if (!SerialPortEvents.listenerQueue.has(serialPortEventType)) {
// console.log('SerialPortEvents.addEventListener queue', serialPortEventType, callback);
// Port is not open, queue the callback
Expand All @@ -49,13 +46,17 @@ export default class SerialPortEvents {
if (!SerialPortEvents.eventListeners.has(serialPortEventType)) {
// console.log('SerialPortEvents.addEventListener attach', serialPortEventType, callback);
// Port is open, attach callback immediately
if (serialPortEventType === 'data') {
SerialPortEvents.parser.on(serialPortEventType, callback);
} else {
SerialPortEvents.serialPort.on(serialPortEventType, callback);
}
SerialPortEvents.serialPort.on(serialPortEventType, callback);
SerialPortEvents.eventListeners.set(serialPortEventType, callback);
}

if (
SerialPortEvents.parser &&
SerialPortEvents.parser.listenerCount('data') === 0 &&
SerialPortEvents.parserCallback
) {
SerialPortEvents.parser.on('data', SerialPortEvents.parserCallback);
}
};

addEventListener('error', (error: Error) => {
Expand All @@ -73,7 +74,7 @@ export default class SerialPortEvents {
event.sender.send('serial-port-event', notification);
});

addEventListener('data', (chunk: any) => {
addEventListener('data', (chunk: Buffer) => {
const data: string =
SerialPortEvents.encoding === 'hex'
? chunk.toString('hex').toUpperCase().match(/.{2}/g).join(' ')
Expand All @@ -88,6 +89,11 @@ export default class SerialPortEvents {
event.sender.send('serial-port-event', notification);
});

SerialPortEvents.parserCallback = (data: string) => {
const notification: SerialPortEvent = { type: 'data-delimited', data };
event.sender.send('serial-port-event', notification);
};

SerialPortEvents.areListenersRegistered = true;

return Promise.resolve();
Expand All @@ -99,14 +105,15 @@ export default class SerialPortEvents {
}

SerialPortEvents.eventListeners.forEach((callback, serialPortEventType) => {
// console.log('SerialPortEvents.removeEventListener', serialPortEventType, callback);
if (serialPortEventType === 'data') {
SerialPortEvents.parser.off(serialPortEventType, callback);
} else {
SerialPortEvents.serialPort.off(serialPortEventType, callback);
}
// console.log('SerialPortEvents.serialPort.off', serialPortEventType, callback);
SerialPortEvents.serialPort.off(serialPortEventType, callback);
});

if (SerialPortEvents.parser) {
// console.log('SerialPortEvents.parser.off', serialPortEventType, callback);
SerialPortEvents.parser.off('data', SerialPortEvents.parserCallback);
}

SerialPortEvents.eventListeners.clear();
SerialPortEvents.areListenersRegistered = false;

Expand Down Expand Up @@ -139,25 +146,28 @@ export default class SerialPortEvents {
}
);

// INFO: InterByteTimeoutParser should become a toggleable parameter if the added "delay" becomes a problem.
SerialPortEvents.parser = SerialPortEvents.serialPort.pipe(
new InterByteTimeoutParser({ interval: 5 })
new ReadlineParser({ includeDelimiter: true }) // TODO: remove includeDelimiter
);

// Process queued listeners after opening
SerialPortEvents.listenerQueue.forEach(
(callback, serialPortEventType) => {
// console.log(`Processing queued listener for serialPortEventType: ${serialPortEventType}`);
if (serialPortEventType === 'data') {
SerialPortEvents.parser.on(serialPortEventType, callback);
} else {
SerialPortEvents.serialPort.on(serialPortEventType, callback);
}
SerialPortEvents.serialPort.on(serialPortEventType, callback);
SerialPortEvents.eventListeners.set(serialPortEventType, callback);
}
);
SerialPortEvents.listenerQueue.clear();

if (
SerialPortEvents.parser &&
SerialPortEvents.parser.listenerCount('data') === 0 &&
SerialPortEvents.parserCallback
) {
SerialPortEvents.parser.on('data', SerialPortEvents.parserCallback);
}

SerialPortEvents.serialPort.open((error) => {
if (error) {
return reject(error);
Expand Down
6 changes: 5 additions & 1 deletion libs/types/src/lib/electron.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,13 @@ export type SerialPortEvent =
| { type: 'open' }
| { type: 'close' }
| { type: 'data'; data: string }
| { type: 'data-delimited'; data: string }
| { type: 'drain' };

export type DataEvent = { type: 'data'; data: string } | { type: 'clear' };
export type DataEvent =
| { type: 'data'; data: string }
| { type: 'data-delimited'; data: string }
| { type: 'clear' };

export interface SerialPortAPI {
list: () => Promise<PortInfo[]>;
Expand Down

0 comments on commit 7cc4bba

Please sign in to comment.