mirror of https://github.com/nealey/vail.git
Compare commits
No commits in common. "c2c9dbcacbe28f45dab6b0a0ddbde5568da12f33" and "c835e84922538af7bb5e5aff65771e3e90df80a0" have entirely different histories.
c2c9dbcacb
...
c835e84922
|
@ -26,13 +26,11 @@ JSON-encoded Vail messages are a direct encoding of the struct:
|
|||
```json
|
||||
{
|
||||
"Timestamp": 1702846980,
|
||||
"Clients": 2,
|
||||
"Duration": [80, 80, 240]
|
||||
}
|
||||
```
|
||||
|
||||
This represents a transmission at Sun 17 Dec 2023 09:03:00 PM UTC, consisting of an 80ms tone, an 80ms silence, and a 240ms tone.
|
||||
2 clients were connectd to the repeater at this time.
|
||||
|
||||
|
||||
Binary Marshalling
|
||||
|
@ -40,12 +38,11 @@ Binary Marshalling
|
|||
|
||||
The binary marshalled version of a Vail message is encoded big-endian:
|
||||
|
||||
00 00 00 00 65 7f 62 04 00 02 00 50 00 50 00 f0
|
||||
00 00 00 00 65 7f 62 04 00 50 00 50 00 f0
|
||||
|
||||
Is decoded as:
|
||||
|
||||
* Timestamp (int64): `00 00 00 00 65 7f 62 04` = 1702846980
|
||||
* Clients (uint16): `00 02` = 02
|
||||
* Duration ([]uint16):
|
||||
* `00 50` = 80
|
||||
* `00 50` = 80
|
||||
|
|
|
@ -7,15 +7,6 @@ body {
|
|||
font-family: Arimo, Arial, monospace;
|
||||
}
|
||||
|
||||
section {
|
||||
border: 1px solid black;
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
p {
|
||||
max-width: 40em;
|
||||
}
|
||||
|
||||
.log {
|
||||
max-height: 10em;
|
||||
overflow: scroll;
|
||||
|
@ -32,7 +23,6 @@ const Second = Millisecond * 1000
|
|||
* event handlers as you desire.
|
||||
*
|
||||
* Events:
|
||||
* clients: the number of connected clients has changed
|
||||
* message: a message was recieved
|
||||
* sound: your application should start making a sound
|
||||
* nosound: your application should stop making a sound
|
||||
|
@ -101,26 +91,29 @@ class Vail extends EventTarget {
|
|||
|
||||
message(event) {
|
||||
let message = JSON.parse(event.data)
|
||||
this.dispatchEvent(new CustomEvent("message", {detail: {message}}))
|
||||
if (message.Clients != this.clients) {
|
||||
this.clients = message.Clients
|
||||
this.dispatchEvent(new CustomEvent("clients", {detail: {clients: this.clients}}))
|
||||
}
|
||||
if (message.Duration.length == 0) {
|
||||
this.offset = Date.now() - message.Timestamp
|
||||
return
|
||||
}
|
||||
this.clients = message.Clients
|
||||
|
||||
// Immediately dispatch a message event
|
||||
let detail = {
|
||||
message: message,
|
||||
}
|
||||
this.dispatchEvent(new CustomEvent("message", {detail}))
|
||||
|
||||
// Defer dispatching sound events
|
||||
let now = Date.now()
|
||||
let when = message.Timestamp + this.offset + this.delay
|
||||
let tx = true
|
||||
let detail = {}
|
||||
for (let duration of message.Duration) {
|
||||
let delay = when - now
|
||||
if (tx && (delay >= 0)) {
|
||||
setTimeout(() => this.sound(true, {when, duration}), delay)
|
||||
setTimeout(() => this.sound(false, {when: when+duration, duration}), delay + duration)
|
||||
detail.when = when
|
||||
detail.duration = duration
|
||||
setTimeout(() => this.sound(true, detail), delay)
|
||||
setTimeout(() => this.sound(false, detail), delay + duration)
|
||||
}
|
||||
when += duration
|
||||
tx = !tx
|
||||
|
@ -151,34 +144,28 @@ class Vail extends EventTarget {
|
|||
}
|
||||
}
|
||||
|
||||
/** Log an event's detail */
|
||||
function log(selector, event) {
|
||||
let section = document.querySelector(selector)
|
||||
let log = section.querySelector(".log")
|
||||
/** Handle the message event by logging it.
|
||||
*
|
||||
* We also take the opportunity to update the listed number of clients.
|
||||
*/
|
||||
function message(event) {
|
||||
let log = document.querySelector("#messages .log")
|
||||
let line = log.appendChild(document.createElement("div"))
|
||||
line.textContent = event.type + ": " + JSON.stringify(event.detail)
|
||||
line.textContent = JSON.stringify(event.detail.message)
|
||||
line.scrollIntoView()
|
||||
}
|
||||
|
||||
/** Update the displayed number of clients. */
|
||||
function clients(event) {
|
||||
let clients = document.querySelector("#nclients")
|
||||
let clients = document.querySelector("#clients")
|
||||
clients.textContent = event.target.clients
|
||||
}
|
||||
|
||||
/** Handle the sound and nosound events by displaying different glyphs. */
|
||||
/** Handle the sound and nosound events by displaying different glyphs */
|
||||
function sound(event, enable) {
|
||||
let sounder = document.querySelector("#sounder")
|
||||
sounder.textContent = enable ? "█" : ""
|
||||
}
|
||||
|
||||
let vail = new Vail("demo") // Connect to the "demo" repeater
|
||||
vail.addEventListener("message", event => log("#messages", event))
|
||||
vail.addEventListener("clients", event => log("#clients", event))
|
||||
vail.addEventListener("sound", event => log("#sounds", event))
|
||||
vail.addEventListener("nosound", event => log("#sounds", event))
|
||||
|
||||
vail.addEventListener("clients", event => clients(event))
|
||||
vail.addEventListener("message", event => message(event))
|
||||
vail.addEventListener("sound", event => sound(event, true))
|
||||
vail.addEventListener("nosound", event => sound(event, false))
|
||||
</script>
|
||||
|
@ -188,35 +175,11 @@ vail.addEventListener("nosound", event => sound(event, false))
|
|||
|
||||
<div><a href="https://vail.woozle.org/#demo" target="_blank">Repeater</a></div>
|
||||
<div>Sounder: <span id="sounder"></span></div>
|
||||
<div>Clients: <span id="nclients">0</span></div>
|
||||
<div>Clients: <span id="clients">0</span></div>
|
||||
|
||||
<section id="sounds">
|
||||
<h2>sound events</h2>
|
||||
<div id="messages">
|
||||
<h2>messages</h2>
|
||||
<div class="log"></div>
|
||||
<p>
|
||||
Implementations should use sound events, which will be dispatched
|
||||
in the correct order, and only have one duration per event.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="clients">
|
||||
<h2>clients events</h2>
|
||||
<div class="log"></div>
|
||||
<p>
|
||||
Clients events are dispatched whenever the number of
|
||||
connected clients changes.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="messages">
|
||||
<h2>message events</h2>
|
||||
<div class="log"></div>
|
||||
<p>
|
||||
Message events are "raw" events. They may arrive out of order,
|
||||
and can have multiple durations.
|
||||
The Vail class parses these into properly-sequenced
|
||||
"sound" events.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
Loading…
Reference in New Issue