usr-tag-interaction/index.js

155 lines
4.6 KiB
JavaScript

/** Video container element */
const videoRoot = /** @type {HTMLElement} */
(document.getElementById('video-root'));
/**
* A map from tag id to video element
* {[$tagId]: <video><source src="$src"/></video>}
*/
const videos = Object.fromEntries(Object.entries(tags).map(([tagId, src]) => {
const video = document.createElement('video');
const source = document.createElement('source');
source.setAttribute('src', src);
video.appendChild(source);
videoRoot.appendChild(video);
return [tagId, video];
}));
/**
* Current playing video element
* @type {HTMLVideoElement|null}
*/
let currentVideo = null;
/**
* Callback function when a tag is read
* @param {string} tagId
*/
function onTag(tagId) {
const nextVideo = videos[tagId];
// ignore unknown tag
if (nextVideo == null) {
console.log('unknown tag', tagId);
return;
}
// ignore if the same tag is used
if (nextVideo === currentVideo) return;
// play the next video from the beginning
nextVideo.currentTime = 0;
nextVideo.play();
nextVideo.classList.add('playing');
// stop the current video
currentVideo?.pause();
currentVideo?.classList.remove('playing');
// update video reference
currentVideo = nextVideo;
}
/**
* Open and read the serial port
* @param {SerialPort} port
*/
async function openPort(port, baudRate=9600) {
// open the serial port
await port.open({baudRate});
// reader of byte stream from the serial port
const reader = port.readable.getReader({mode: 'byob'});
// buffer for reading the serial port
const bufferSize = 16;
let buffer = new ArrayBuffer(bufferSize);
let offset = 0;
try {
while (true) {
// read bytes from the serial port to buffer
const {value, done} = await reader.read(
new Uint8Array(buffer, offset, bufferSize - offset),
);
// no more message
if (done) {
showMessage('The serial port is closed');
break;
}
// update buffer and offset
buffer = value.buffer;
offset += value.byteLength;
// message format:
// - (u8) N = tag id length
// - (u8[N]) tag id
/** pointer (array index) to the message in buffer */
let ptr = 0;
// if tag id length of the next message presents
while (offset > ptr) {
const tagIdLength = new DataView(buffer).getUint8(0);
// break if the message is not completely read
const ptrNext = ptr + 1 + tagIdLength;
if (offset < ptrNext) break;
// convert tag id bytes to HEX string
const tagId = Array.from(
new Uint8Array(buffer, ptr + 1, tagIdLength),
// %02X
x => x.toString(16).toUpperCase().padStart(2, '0'),
).join('');
// handle the tag
onTag(tagId);
// try to read the next message
ptr = ptrNext;
}
// move the remaining bytes in the buffer to the beginning
// if any message is read
if (ptr > 0) {
// remaining byte count
const remainingLength = offset - ptr;
// copy bytes
if (remainingLength > 0) {
new Uint8Array(buffer)
.set(new Uint8Array(buffer, ptr, remainingLength));
}
// update offset
offset = remainingLength;
}
}
} catch (err) {
showMessage(
'Error occurred while reading serial port:\n'
+ (err?.toString() ?? ''),
);
console.error(err);
} finally {
reader.releaseLock();
}
}
/** HTML element for showing message */
const msgElm = /** @type {HTMLElement} */ (document.getElementById('message'));
/** Show message on page */
const showMessage = (/** @type {string} */ msg) => msgElm.textContent = msg;
// check if this browser supports Web Serial API
if (navigator.serial == null) {
showMessage('Your browser does not support Web Serial API!');
} else {
// click body to select serial port to listen
// this is required since videos cannot be played without user's interaction
document.body.addEventListener('click', async function onBodyClicked() {
// get serial port
try {
// try to get serial ports
const ports = await navigator.serial.getPorts();
// choose the first port (FIXME) if availble port presents
// or request user to select a port
const port = ports[0] ?? await navigator.serial.requestPort();
// unregister body onClick listener
document.body.removeEventListener('click', onBodyClicked);
// remove message from page
showMessage('');
// start handling the messages from the serial port
openPort(port);
} catch(err) {
showMessage('Failed to access serial port:\n' + (err?.toString() ?? ''));
}
});
}