181 lines
No EOL
4.6 KiB
TypeScript
181 lines
No EOL
4.6 KiB
TypeScript
import { SerialPort } from 'serialport';
|
|
import { ReadlineParser } from '@serialport/parser-readline';
|
|
import { EventEmitter } from 'events';
|
|
|
|
export interface PinState {
|
|
pin: number;
|
|
state: number;
|
|
name: string;
|
|
timestamp: Date;
|
|
}
|
|
|
|
export interface PinChange {
|
|
pin: number;
|
|
previousState: number;
|
|
currentState: number;
|
|
timestamp: Date;
|
|
}
|
|
|
|
export class BedHardware extends EventEmitter {
|
|
private serialPort: SerialPort | null = null;
|
|
private parser: ReadlineParser | null = null;
|
|
private pinStates: Map<number, PinState> = new Map();
|
|
private isConnected: boolean = false;
|
|
|
|
constructor(private portPath: string, private baudRate: number = 9600) {
|
|
super();
|
|
}
|
|
|
|
async connect(): Promise<void> {
|
|
try {
|
|
this.serialPort = new SerialPort({
|
|
path: this.portPath,
|
|
baudRate: this.baudRate,
|
|
autoOpen: false
|
|
});
|
|
|
|
this.parser = new ReadlineParser({ delimiter: '\n' });
|
|
this.serialPort.pipe(this.parser);
|
|
|
|
// Setup event handlers
|
|
this.serialPort.on('open', () => {
|
|
this.isConnected = true;
|
|
this.emit('connected');
|
|
console.log('Serial port opened');
|
|
});
|
|
|
|
this.serialPort.on('error', (error) => {
|
|
this.emit('error', error);
|
|
console.error('Serial port error:', error);
|
|
});
|
|
|
|
this.serialPort.on('close', () => {
|
|
this.isConnected = false;
|
|
this.emit('disconnected');
|
|
console.log('Serial port closed');
|
|
});
|
|
|
|
this.parser.on('data', (data: string) => {
|
|
this.handleSerialData(data.trim());
|
|
});
|
|
|
|
// Open the port
|
|
await new Promise<void>((resolve, reject) => {
|
|
this.serialPort!.open((error) => {
|
|
if (error) {
|
|
reject(error);
|
|
} else {
|
|
resolve();
|
|
}
|
|
});
|
|
});
|
|
|
|
} catch (error) {
|
|
throw new Error(`Failed to connect to ${this.portPath}: ${error}`);
|
|
}
|
|
}
|
|
|
|
async disconnect(): Promise<void> {
|
|
if (this.serialPort && this.serialPort.isOpen) {
|
|
await new Promise<void>((resolve) => {
|
|
this.serialPort!.close(() => {
|
|
resolve();
|
|
});
|
|
});
|
|
}
|
|
this.serialPort = null;
|
|
this.parser = null;
|
|
this.isConnected = false;
|
|
}
|
|
|
|
private handleSerialData(data: string): void {
|
|
const parts = data.split(':');
|
|
|
|
if (parts[0] === 'INIT') {
|
|
if (parts[1] === 'START') {
|
|
this.emit('initialized');
|
|
console.log('Arduino initialization started');
|
|
} else if (parts.length >= 3) {
|
|
// INIT:PIN:STATE format
|
|
const pin = parseInt(parts[1]);
|
|
const state = parseInt(parts[2]);
|
|
|
|
if (!isNaN(pin) && !isNaN(state)) {
|
|
const pinState: PinState = {
|
|
pin,
|
|
state,
|
|
name: `PIN${pin}`,
|
|
timestamp: new Date()
|
|
};
|
|
|
|
this.pinStates.set(pin, pinState);
|
|
this.emit('pinInitialized', pinState);
|
|
}
|
|
}
|
|
} else if (parts[0] === 'CHANGE' && parts.length >= 4) {
|
|
// CHANGE:PIN:PREVIOUS_STATE:CURRENT_STATE format
|
|
const pin = parseInt(parts[1]);
|
|
const previousState = parseInt(parts[2]);
|
|
const currentState = parseInt(parts[3]);
|
|
|
|
if (!isNaN(pin) && !isNaN(previousState) && !isNaN(currentState)) {
|
|
const pinChange: PinChange = {
|
|
pin,
|
|
previousState,
|
|
currentState,
|
|
timestamp: new Date()
|
|
};
|
|
|
|
// Update stored pin state
|
|
const pinState: PinState = {
|
|
pin,
|
|
state: currentState,
|
|
name: `PIN${pin}`,
|
|
timestamp: new Date()
|
|
};
|
|
|
|
this.pinStates.set(pin, pinState);
|
|
|
|
this.emit('pinChanged', pinChange);
|
|
this.emit(`pin${pin}Changed`, pinChange);
|
|
}
|
|
}
|
|
}
|
|
|
|
getPinState(pin: number): PinState | undefined {
|
|
return this.pinStates.get(pin);
|
|
}
|
|
|
|
getAllPinStates(): PinState[] {
|
|
return Array.from(this.pinStates.values());
|
|
}
|
|
|
|
isPortConnected(): boolean {
|
|
return this.isConnected && this.serialPort?.isOpen === true;
|
|
}
|
|
|
|
// Static method to list available serial ports
|
|
static async listPorts(): Promise<string[]> {
|
|
const ports = await SerialPort.list();
|
|
return ports.map(port => port.path);
|
|
}
|
|
}
|
|
|
|
// Example usage:
|
|
/*
|
|
const bedHardware = new BedHardware('/dev/ttyUSB0', 9600);
|
|
|
|
bedHardware.on('connected', () => {
|
|
console.log('Connected to bed hardware');
|
|
});
|
|
|
|
bedHardware.on('pinChanged', (change: PinChange) => {
|
|
console.log(`Pin ${change.pin} changed from ${change.previousState} to ${change.currentState}`);
|
|
});
|
|
|
|
bedHardware.on('error', (error) => {
|
|
console.error('Hardware error:', error);
|
|
});
|
|
|
|
await bedHardware.connect();
|
|
*/ |