|
1 | 1 | const ArduinoUSBVendorId = 0x2341;
|
2 | 2 | const UserActionAbortError = 8;
|
3 | 3 |
|
| 4 | +class BytesWaitTransformer { |
| 5 | + constructor(waitBytes) { |
| 6 | + this.waitBytes = waitBytes; |
| 7 | + this.buffer = new Uint8Array(0); |
| 8 | + this.controller = undefined; |
| 9 | + } |
| 10 | + |
| 11 | + async transform(chunk, controller) { |
| 12 | + this.controller = controller; |
| 13 | + |
| 14 | + // Concatenate incoming chunk with existing buffer |
| 15 | + this.buffer = new Uint8Array([...this.buffer, ...chunk]); |
| 16 | + |
| 17 | + while (this.buffer.length >= this.waitBytes) { |
| 18 | + // Extract the required number of bytes |
| 19 | + const bytesToProcess = this.buffer.slice(0, this.waitBytes); |
| 20 | + |
| 21 | + // Remove processed bytes from the buffer |
| 22 | + this.buffer = this.buffer.slice(this.waitBytes); |
| 23 | + |
| 24 | + // Notify the controller that bytes have been processed |
| 25 | + controller.enqueue(bytesToProcess); |
| 26 | + } |
| 27 | + } |
| 28 | + |
| 29 | + flush(controller) { |
| 30 | + if (this.buffer.length > 0) { |
| 31 | + // Handle remaining bytes (if any) when the stream is closed |
| 32 | + const remainingBytes = this.buffer.slice(); |
| 33 | + console.log("Remaining bytes:", remainingBytes); |
| 34 | + |
| 35 | + // Notify the controller that remaining bytes have been processed |
| 36 | + controller.enqueue(remainingBytes); |
| 37 | + } |
| 38 | + } |
| 39 | + } |
| 40 | + |
4 | 41 | /**
|
5 | 42 | * Handles the connection between the browser and the Arduino board via Web Serial.
|
6 | 43 | */
|
@@ -120,61 +157,42 @@ class SerialConnectionHandler {
|
120 | 157 | * If the timeout is reached, the reader will be canceled and the read lock will be released.
|
121 | 158 | */
|
122 | 159 | async readBytes(numBytes, timeout = null) {
|
123 |
| - if (this.currentPort.readable.locked) { |
| 160 | + if(!this.currentPort) return null; |
| 161 | + if(this.currentPort.readable.locked) { |
124 | 162 | console.log('🔒 Stream is already locked. Ignoring request...');
|
125 | 163 | return null;
|
126 | 164 | }
|
127 | 165 |
|
128 |
| - const bytesRead = new Uint8Array(numBytes); |
129 |
| - let bytesReadIdx = 0; |
130 |
| - let keepReading = true; |
131 |
| - |
132 |
| - // As long as the errors are non-fatal, a new ReadableStream is created automatically and hence port.readable is non-null. |
133 |
| - // If a fatal error occurs, such as the serial device being removed, then port.readable becomes null. |
134 |
| - |
135 |
| - while (this.currentPort?.readable && keepReading) { |
136 |
| - const reader = this.currentPort.readable.getReader(); |
137 |
| - this.currentReader = reader; |
138 |
| - let timeoutID = null; |
139 |
| - // let count = 0; |
140 |
| - |
141 |
| - try { |
142 |
| - while (bytesReadIdx < numBytes) { |
143 |
| - if (timeout) { |
144 |
| - timeoutID = setTimeout(() => { |
145 |
| - console.log('⌛️ Timeout occurred while reading.'); |
146 |
| - if (this.currentPort?.readable) reader?.cancel(); |
147 |
| - }, timeout); |
148 |
| - } |
149 |
| - |
150 |
| <
8000
td data-grid-cell-id="diff-bf28ead0c7931c8586c19e4939bdb9b9bd96968c7f6444e58609b3d8b0856ee1-150-165-2" data-line-anchor="diff-bf28ead0c7931c8586c19e4939bdb9b9bd96968c7f6444e58609b3d8b0856ee1L150" data-selected="false" role="gridcell" style="background-color:var(--diffBlob-deletionLine-bgColor, var(--diffBlob-deletion-bgColor-line));padding-right:24px" tabindex="-1" valign="top" class="focusable-grid-cell diff-text-cell left-side-diff-cell border-right left-side">- const { value, done } = await reader.read();
151 |
| - if (timeoutID) clearTimeout(timeoutID); |
152 |
| - |
153 |
| - if (value) { |
154 |
| - for (const byte of value) { |
155 |
| - bytesRead[bytesReadIdx++] = byte; |
156 |
| - if (bytesReadIdx >= numBytes) break; |
157 |
| - } |
158 |
| - // count += value.byteLength; |
159 |
| - // console.log(`Read ${value.byteLength} (Total: ${count}) out of ${numBytes} bytes.}`); |
160 |
| - } |
161 |
| - |
162 |
| - if (done) { |
163 |
| - console.log('🚫 Reader has been canceled'); |
164 |
| - break; |
165 |
| - } |
166 |
| - } |
167 |
| - |
168 |
| - } catch (error) { |
169 |
| - console.error('💣 Error occurred while reading: ' + error.message); |
170 |
| - } finally { |
171 |
| - keepReading = false; |
172 |
| - // console.log('🔓 Releasing reader lock...'); |
173 |
| - reader?.releaseLock(); |
174 |
| - this.currentReader = null; |
| 166 | + const transformer = new BytesWaitTransformer(numBytes); |
| 167 | + const transformStream = new TransformStream(transformer); |
| 168 | + const pipedStream = this.currentPort.readable.pipeThrough(transformStream); |
| 169 | + const reader = pipedStream.getReader(); |
| 170 | + this.currentReader = reader; |
| 171 | + let timeoutID = null; |
| 172 | + |
| 173 | + try { |
| 174 | + if (timeout) { |
| 175 | + timeoutID = setTimeout(() => { |
| 176 | + console.log('⌛️ Timeout occurred while reading.'); |
| 177 | + if (this.currentPort?.readable) reader?.cancel(); |
| 178 | + }, timeout); |
| 179 | + } |
| 180 | + const { value, done } = await reader.read(); |
| 181 | + if (timeoutID) clearTimeout(timeoutID); |
| 182 | + |
| 183 | + if (done) { |
| 184 | + console.log('🚫 Reader has been canceled'); |
| 185 | + return null; |
175 | 186 | }
|
| 187 | + return value; |
| 188 | + } catch (error) { |
| 189 | + console.error('💣 Error occurred while reading: ' + error.message); |
| 190 | + } finally { |
| 191 | + // console.log('🔓 Releasing reader lock...'); |
| 192 | + await reader?.cancel(); // Discards any enqueued data |
| 193 | + reader?.releaseLock(); |
| 194 | + this.currentReader = null; |
176 | 195 | }
|
177 |
| - return bytesRead; |
178 | 196 | }
|
179 | 197 |
|
180 | 198 | async sendData(byteArray) {
|
|
0 commit comments