-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontroller.js
More file actions
149 lines (149 loc) · 3.37 KB
/
Copy pathcontroller.js
File metadata and controls
149 lines (149 loc) · 3.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import { SerialPort } from 'serialport';
import Lockable from './lock.js';
import Streamer from './streamer.js';
import { delay } from './util.js';
// Utility functions
async function getDevicePath(target) {
while (true) {
for (const device of await SerialPort.list()) {
// Only check device if has string property 'path'
if (typeof device?.path !== 'string') continue;
// Check if device matches target
let flag = true;
for (const key in target) {
if (target[key] !== device?.[key]) {
flag = false;
break;
}
}
if (flag) return device.path;
}
await delay(1000);
}
}
// The Controller Device
export default class Controller extends Lockable {
#device;
get device() { return this.#device }
// ----------------------------------------
#cmd = []
// ----------------------------------------
constructor(target) {
super();
this.#init(target);
}
// ----------------------------------------
async #init(target) {
// Clear previously created device instance
this.#device = undefined;
// Lock the device before initialization
const unlock = await this.getLock();
// Wait until device is online
const devicePath = await getDevicePath(target);
// Create device instance
const device = new SerialPort({
path: devicePath,
baudRate: 9600,
autoOpen: false,
});
// Initialize device
while (
await new Promise(res => {
device.open(err => {
if (err) {
console.error(err);
res(false);
} else res(true);
})
}) === false
);
// Register Device Data Handler
device.on('data', chunk => {
this.#streamer.receive(chunk);
process.stdout.write(chunk);
});
// Register Device Error Handler
device.on('error', err => {
console.error(err);
device.close(err => {
if (err) console.error(err);
this.#init(target);
})
})
// Assign new device to controller
this.#device = device;
// Unlock this controller
return unlock();
}
// ----------------------------------------
#streamer = new Streamer('\n');
// Send to device (byte by byte)
async #send(str) {
for (const char of str) {
await new Promise((res, rej) => {
this.#device.write(
char,
'ascii',
err => {
if (err) rej(err);
else res();
}
)
})
}
}
// Execute committed commands
exec() {
return Promise.all([
this.#streamer.waitFor(
/^\>\>\s*Execution FINISHED/gi
),
this.#send('$')
])
}
// Commit commands to the controller
async commit() {
for (const cmd of this.#cmd) {
await Promise.all([
this.#streamer.waitFor(/^\>{2}/gi),
this.#send((cmd + ';').replace(/[\n;]{2,}/, ';'))
])
}
this.#cmd = [];
}
// LED Command Parser
LED(index) {
const self = this, cmd = this.#cmd;
if (index < 1 || index > 8)
throw new Error(`[LED] Index ${index} out of range`);
return {
get ON() {
cmd.push(`LED ${index} 128`)
return self
},
get OFF() {
cmd.push(`LED ${index} 0`)
return self
},
PWM(val) {
if (val < 0)
throw new Error(`[LED] PWM ${val} out of range (0 - 256)`)
cmd.push(`LED ${index} ${Math.round(val)}`)
return self
}
};
}
// WAIT Command Parser
WAIT(time) {
if (time < 0)
throw new Error(`[WAIT] Time period has to be positive`);
let time_ns = Math.round(time * 1000);
this.#cmd.push(`WAIT ${time_ns}`);
return this;
}
// RST Command Parser
RST() {
this.#cmd.push(`RST`);
return this;
}
}