Skip to content

Commit

Permalink
fix(webserial): correctly close serial port, allow port to be closed …
Browse files Browse the repository at this point in the history
…by consumer, close #31
  • Loading branch information
foxxyz committed Jun 10, 2024
1 parent ee15f1a commit 96365ea
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 6 deletions.
29 changes: 24 additions & 5 deletions connections/web-serial.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ class MagicByteLengthParser {
}

// Async pipeline transformer that splits a stream by 0x82 magic bytes
async function *read(port) {
const transformed = port.readable.pipeThrough(new TransformStream(new MagicByteLengthParser({ magicByte: 0x82 })))
async function *read(port, { signal }) {
const transformer = new TransformStream(new MagicByteLengthParser({ magicByte: 0x82 }))
const transformed = port.readable.pipeThrough(transformer, { signal })
const reader = transformed.getReader()
try {
while (true) {
Expand Down Expand Up @@ -83,11 +84,27 @@ export default class LoupedeckWebSerialConnection extends EventEmitter {
return connections
}
async close() {
this.aborter.abort('Manual close initiated')
await new Promise(res => this.on('readEnd', res))
this.writer.close()
this.writer.releaseLock()
// Without the line below, the serialport will refuse to close with
// "TypeError: Failed to execute 'close': Cannot cancel a locked stream"
//
// However, the port.readable stream should have been unlocked via reader.releaseLock() (see read() above)
//
// Apparently it unlocks after a one tick propagation (due to pipeThrough()), but there doesn't
// seem to be a promise anywhere to await this happening...
//
// Annoyingly, we have to wait one tick here which is really hacky,
// and we should find a way to do this more robustly
await new Promise(res => setTimeout(res, 10))
await this.port.close()
}
async connect() {
await this.port.open({ baudRate: 256000 })
if (!this.isReady()) {
await this.port.open({ baudRate: 256000 })
}

const reader = this.port.readable.getReader()
this.writer = this.port.writable.getWriter()
Expand All @@ -101,7 +118,8 @@ export default class LoupedeckWebSerialConnection extends EventEmitter {
reader.releaseLock()

// Set up data pipeline
this.reader = read(this.port)
this.aborter = new AbortController()
this.readStream = read(this.port, this.aborter)
this.emit('connect', { address: 'web' })
this.read()
}
Expand All @@ -118,10 +136,11 @@ export default class LoupedeckWebSerialConnection extends EventEmitter {
this.onDisconnect(err)
}
async read() {
for await (const message of this.reader) {
for await (const message of this.readStream) {
if (!this.port.readable) break
this.emit('message', message)
}
this.emit('readEnd')
}
send(buff, raw = false) {
if (!raw) {
Expand Down
13 changes: 12 additions & 1 deletion examples/web/src/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@
<canvas width="480" height="270" ref="screenCanvas" />
</div>
</div>
<button v-if="connected" @click="close" type="button" class="close">
Close Device
</button>
</template>

<script setup>
Expand Down Expand Up @@ -174,6 +177,13 @@ async function request() {
}
request()
async function close() {
if (!device) return
await device.close()
connected.value = false
deviceInfo.value = null
}
// Force override (for debugging)
const forceDevice = ref('')
watch(forceDevice, value => {
Expand Down Expand Up @@ -202,7 +212,8 @@ body
font-family: sans-serif
min-height: 100vh
padding: 1rem 2rem
.close
margin-top: 1em
.device
background-color: #444
max-width: 50rem
Expand Down

0 comments on commit 96365ea

Please sign in to comment.