mirror of https://github.com/nealey/vail.git
Log clock offset, work on #14
This commit is contained in:
parent
5b26d92b20
commit
5460c9e3c9
30
book.go
30
book.go
|
@ -7,18 +7,18 @@ import (
|
||||||
|
|
||||||
type Book struct {
|
type Book struct {
|
||||||
entries map[string]*Repeater
|
entries map[string]*Repeater
|
||||||
events chan bookEvent
|
events chan bookEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func NewBook() Book {
|
func NewBook() Book {
|
||||||
return Book{
|
return Book{
|
||||||
entries: make(map[string]*Repeater),
|
entries: make(map[string]*Repeater),
|
||||||
events: make(chan bookEvent, 5),
|
events: make(chan bookEvent, 5),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type bookEventType int
|
type bookEventType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
joinEvent = bookEventType(iota)
|
joinEvent = bookEventType(iota)
|
||||||
partEvent
|
partEvent
|
||||||
|
@ -27,32 +27,32 @@ const (
|
||||||
|
|
||||||
type bookEvent struct {
|
type bookEvent struct {
|
||||||
eventType bookEventType
|
eventType bookEventType
|
||||||
name string
|
name string
|
||||||
w io.Writer
|
w io.Writer
|
||||||
p []byte
|
p []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b Book) Join(name string, w io.Writer) {
|
func (b Book) Join(name string, w io.Writer) {
|
||||||
b.events <- bookEvent{
|
b.events <- bookEvent{
|
||||||
eventType: joinEvent,
|
eventType: joinEvent,
|
||||||
name: name,
|
name: name,
|
||||||
w: w,
|
w: w,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b Book) Part(name string, w io.Writer) {
|
func (b Book) Part(name string, w io.Writer) {
|
||||||
b.events <- bookEvent{
|
b.events <- bookEvent{
|
||||||
eventType: partEvent,
|
eventType: partEvent,
|
||||||
name: name,
|
name: name,
|
||||||
w: w,
|
w: w,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b Book) Send(name string, p []byte) {
|
func (b Book) Send(name string, p []byte) {
|
||||||
b.events <- bookEvent{
|
b.events <- bookEvent{
|
||||||
eventType: sendEvent,
|
eventType: sendEvent,
|
||||||
name: name,
|
name: name,
|
||||||
p: p,
|
p: p,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,13 +68,13 @@ func (b Book) loop() {
|
||||||
|
|
||||||
switch event.eventType {
|
switch event.eventType {
|
||||||
case joinEvent:
|
case joinEvent:
|
||||||
if ! ok {
|
if !ok {
|
||||||
repeater = NewRepeater()
|
repeater = NewRepeater()
|
||||||
b.entries[event.name] = repeater
|
b.entries[event.name] = repeater
|
||||||
}
|
}
|
||||||
repeater.Join(event.w)
|
repeater.Join(event.w)
|
||||||
case partEvent:
|
case partEvent:
|
||||||
if ! ok {
|
if !ok {
|
||||||
log.Println("WARN: Parting an empty channel:", event.name)
|
log.Println("WARN: Parting an empty channel:", event.name)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ func (b Book) loop() {
|
||||||
delete(b.entries, event.name)
|
delete(b.entries, event.name)
|
||||||
}
|
}
|
||||||
case sendEvent:
|
case sendEvent:
|
||||||
if ! ok {
|
if !ok {
|
||||||
log.Println("WARN: Sending to an empty channel:", event.name)
|
log.Println("WARN: Sending to an empty channel:", event.name)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
5
main.go
5
main.go
|
@ -1,6 +1,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
"os"
|
"os"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -18,6 +20,9 @@ func (c Client) Handle(ws *websocket.Conn) {
|
||||||
book.Join(c.repeaterName, ws)
|
book.Join(c.repeaterName, ws)
|
||||||
defer book.Part(c.repeaterName, ws)
|
defer book.Part(c.repeaterName, ws)
|
||||||
|
|
||||||
|
// Tell the client what time we think it is
|
||||||
|
fmt.Fprintf(ws, "[%d]", time.Now().UnixNano() / time.Millisecond.Nanoseconds())
|
||||||
|
|
||||||
for {
|
for {
|
||||||
buf := make([]byte, ws.MaxPayloadBytes)
|
buf := make([]byte, ws.MaxPayloadBytes)
|
||||||
|
|
||||||
|
|
20
message.go
20
message.go
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// VailMessage is a single Vail message.
|
// VailMessage is a single Vail message.
|
||||||
|
@ -10,7 +11,7 @@ type Message struct {
|
||||||
// Relative time in ms of this message.
|
// Relative time in ms of this message.
|
||||||
// These timestamps need to be consistent, but the offset can be anything.
|
// These timestamps need to be consistent, but the offset can be anything.
|
||||||
// ECMAScript `performance.now()` is ideal.
|
// ECMAScript `performance.now()` is ideal.
|
||||||
Timestamp uint64
|
Timestamp int64
|
||||||
|
|
||||||
// Message timing in ms.
|
// Message timing in ms.
|
||||||
// Timings alternate between tone and silence.
|
// Timings alternate between tone and silence.
|
||||||
|
@ -18,6 +19,23 @@ type Message struct {
|
||||||
Duration []uint8
|
Duration []uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewMessage(ts time.Time, durations []time.Duration) Message {
|
||||||
|
msg := Message{
|
||||||
|
Timestamp: ts.UnixNano() / time.Millisecond.Nanoseconds(),
|
||||||
|
Duration: make([]uint8, len(durations)),
|
||||||
|
}
|
||||||
|
for i, dns := range durations {
|
||||||
|
ms := dns.Milliseconds()
|
||||||
|
if (ms > 255) {
|
||||||
|
ms = 255
|
||||||
|
} else if (ms < 0) {
|
||||||
|
ms = 0
|
||||||
|
}
|
||||||
|
msg.Duration[i] = uint8(ms)
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
// Marshaling presumes something else is keeping track of lengths
|
// Marshaling presumes something else is keeping track of lengths
|
||||||
func (m Message) MarshalBinary() ([]byte, error) {
|
func (m Message) MarshalBinary() ([]byte, error) {
|
||||||
var w bytes.Buffer
|
var w bytes.Buffer
|
||||||
|
|
|
@ -3,19 +3,20 @@ package main
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMessage(t *testing.T) {
|
func TestMessage(t *testing.T) {
|
||||||
m := Message{0x1122334455667788, []uint8{0xaa, 0xbb, 0xcc}}
|
m := Message{0x1122334455, []uint8{0xaa, 0xbb, 0xcc}}
|
||||||
m2 := Message{12, []uint8{1}}
|
m2 := Message{12, []uint8{1}}
|
||||||
|
|
||||||
if ! m.Equal(m) {
|
if !m.Equal(m) {
|
||||||
t.Error("Equal messages did not compare equal")
|
t.Error("Equal messages did not compare equal")
|
||||||
}
|
}
|
||||||
if m.Equal(m2) {
|
if m.Equal(m2) {
|
||||||
t.Error("Unequal messages compared equal")
|
t.Error("Unequal messages compared equal")
|
||||||
}
|
}
|
||||||
if m.Equal(Message{m.Timestamp, []uint8{1,2,3}}) {
|
if m.Equal(Message{m.Timestamp, []uint8{1, 2, 3}}) {
|
||||||
t.Error("Messages with different payloads compared equal")
|
t.Error("Messages with different payloads compared equal")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,14 +24,29 @@ func TestMessage(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
if ! bytes.Equal(bm, []byte("\x11\x22\x33\x44\x55\x66\x77\x88\xaa\xbb\xcc")) {
|
if !bytes.Equal(bm, []byte("\x00\x00\x00\x11\x22\x33\x44\x55\xaa\xbb\xcc")) {
|
||||||
t.Error("Encoded wrong:", bm)
|
t.Error("Encoded wrong:", bm)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := m2.UnmarshalBinary(bm); err != nil {
|
if err := m2.UnmarshalBinary(bm); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
if ! m.Equal(m2) {
|
if !m.Equal(m2) {
|
||||||
t.Error("Decoded wrong", m2)
|
t.Error("Decoded wrong", m2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m3 := NewMessage(
|
||||||
|
time.Unix(
|
||||||
|
0,
|
||||||
|
m.Timestamp*time.Millisecond.Nanoseconds(),
|
||||||
|
),
|
||||||
|
[]time.Duration{
|
||||||
|
time.Duration(m.Duration[0]) * time.Millisecond,
|
||||||
|
time.Duration(m.Duration[1]) * time.Millisecond,
|
||||||
|
time.Duration(m.Duration[2]) * time.Millisecond,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if !m.Equal(m3) {
|
||||||
|
t.Error("NewMessage didn't work", m, m3)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -291,6 +291,7 @@ class Vail {
|
||||||
this.sent = []
|
this.sent = []
|
||||||
this.lagTimes = [0]
|
this.lagTimes = [0]
|
||||||
this.rxDurations = [0]
|
this.rxDurations = [0]
|
||||||
|
this.clockOffset = null // How badly our clock is off of the server's
|
||||||
this.rxDelay = 0 // Milliseconds to add to incoming timestamps
|
this.rxDelay = 0 // Milliseconds to add to incoming timestamps
|
||||||
this.beginTxTime = null // Time when we began transmitting
|
this.beginTxTime = null // Time when we began transmitting
|
||||||
|
|
||||||
|
@ -478,6 +479,16 @@ class Vail {
|
||||||
let beginTxTime = msg[0]
|
let beginTxTime = msg[0]
|
||||||
let durations = msg.slice(1)
|
let durations = msg.slice(1)
|
||||||
|
|
||||||
|
// Server is telling us the current time
|
||||||
|
if (durations.length == 0) {
|
||||||
|
let offset = now - beginTxTime
|
||||||
|
console.log("Our clock ahead of server by", offset, "ms")
|
||||||
|
if (this.clockOffset === null) {
|
||||||
|
this.clockOffset = offset
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let sent = this.sent.filter(e => e != jmsg)
|
let sent = this.sent.filter(e => e != jmsg)
|
||||||
if (sent.length < this.sent.length) {
|
if (sent.length < this.sent.length) {
|
||||||
// We're getting our own message back, which tells us our lag.
|
// We're getting our own message back, which tells us our lag.
|
||||||
|
|
Loading…
Reference in New Issue