Log clock offset, work on #14

This commit is contained in:
Neale Pickett 2020-05-30 22:23:53 -06:00
parent 5b26d92b20
commit 5460c9e3c9
7 changed files with 80 additions and 30 deletions

30
book.go
View File

@ -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
} }

View File

@ -14,14 +14,14 @@ func TestBook(t *testing.T) {
if len(b.entries) != 1 { if len(b.entries) != 1 {
t.Error("Wrong number of entries") t.Error("Wrong number of entries")
} }
// Send to an empty channel // Send to an empty channel
b.Send("merf", []byte("goober")) b.Send("merf", []byte("goober"))
b.loop() b.loop()
if buf1.String() != "buf1" { if buf1.String() != "buf1" {
t.Error("Sending to empty channel sent to non-empty channel") t.Error("Sending to empty channel sent to non-empty channel")
} }
// Send to a non-empty channel! // Send to a non-empty channel!
b.Send("moo", []byte("goober")) b.Send("moo", []byte("goober"))
b.loop() b.loop()
@ -43,11 +43,11 @@ func TestBook(t *testing.T) {
if buf2.String() != "buf2snerk" { if buf2.String() != "buf2snerk" {
t.Error("Send to 2-member channel busted", buf2) t.Error("Send to 2-member channel busted", buf2)
} }
// Part a client // Part a client
b.Part("moo", buf1) b.Part("moo", buf1)
b.loop() b.loop()
b.Send("moo", []byte("peanut")) b.Send("moo", []byte("peanut"))
b.loop() b.loop()
if buf1.String() != "buf1goobersnerk" { if buf1.String() != "buf1goobersnerk" {

View File

@ -1,6 +1,8 @@
package main package main
import ( import (
"fmt"
"time"
"os" "os"
"log" "log"
"net/http" "net/http"
@ -17,6 +19,9 @@ func (c Client) Handle(ws *websocket.Conn) {
ws.MaxPayloadBytes = 500 ws.MaxPayloadBytes = 500
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)

View File

@ -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

View File

@ -3,34 +3,50 @@ 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")
} }
bm, err := m.MarshalBinary() bm, err := m.MarshalBinary()
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)
}
} }

View File

@ -17,7 +17,7 @@ func TestRepeater(t *testing.T) {
if buf1.String() != "buf1moo" { if buf1.String() != "buf1moo" {
t.Error("Client 1 not repeating", buf1) t.Error("Client 1 not repeating", buf1)
} }
buf2 := bytes.NewBufferString("buf2") buf2 := bytes.NewBufferString("buf2")
r.Join(buf2) r.Join(buf2)
r.Send([]byte("bar")) r.Send([]byte("bar"))
@ -27,7 +27,7 @@ func TestRepeater(t *testing.T) {
if buf2.String() != "buf2bar" { if buf2.String() != "buf2bar" {
t.Error("Client 2 not repeating", buf2) t.Error("Client 2 not repeating", buf2)
} }
r.Part(buf1) r.Part(buf1)
r.Send([]byte("baz")) r.Send([]byte("baz"))
if buf1.String() != "buf1moobar" { if buf1.String() != "buf1moobar" {

View File

@ -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
@ -477,6 +478,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) {