-
Notifications
You must be signed in to change notification settings - Fork 0
/
coordinator.go
157 lines (134 loc) · 3.38 KB
/
coordinator.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package main
import (
"math"
"sync"
)
type Coordinator struct {
sync.Mutex
Clients map[int]*Client
Observations map[string][]Observation
Limits map[Road]uint16
SentTickets map[string][]uint32
TicketsToSendLater map[Road][]Ticket
}
func (c *Coordinator) AddClient(client *Client) {
c.Lock()
defer c.Unlock()
c.Clients[client.ID] = client
if client.IsCamera {
c.Limits[client.Road] = client.Limit
}
if client.IsDispatcher {
for _, road := range client.Roads {
for _, ticket := range c.TicketsToSendLater[road] {
client.WriteTicket(ticket)
}
delete(c.TicketsToSendLater, road)
}
}
}
func (c *Coordinator) AddPlate(plate string, tm uint32, road Road, mile uint16) {
c.Lock()
defer c.Unlock()
observation := Observation{
Plate: plate,
Timestamp: tm,
Road: road,
Mile: mile,
}
c.Observations[plate] = append(c.Observations[plate], observation)
// See if this observation leads to any tickets
for _, other := range c.Observations[plate] {
if observation.Road != other.Road || observation.Timestamp == other.Timestamp {
continue
}
ds := Abs(int(observation.Mile) - int(other.Mile))
dt := Abs(int(observation.Timestamp) - int(other.Timestamp))
speed := math.Round(3600 * float64(ds) / float64(dt))
limit := float64(c.Limits[observation.Road])
if speed <= limit {
continue
}
// This generates a ticket
var earlier, later Observation
if observation.Timestamp < other.Timestamp {
earlier = observation
later = other
} else {
earlier = other
later = observation
}
c.SendTicket(Ticket{
Plate: observation.Plate,
Road: observation.Road,
Timestamp1: earlier.Timestamp,
Timestamp2: later.Timestamp,
Mile1: earlier.Mile,
Mile2: later.Mile,
Speed: uint16(100 * speed),
})
}
}
func (c *Coordinator) SendTicket(t Ticket) {
// NOTE: Don't lock/unlock, this is called with the mutex already acquired.
// Don't send a 2nd ticket for this day
day1 := t.Timestamp1 / 86400
day2 := t.Timestamp2 / 86400
contains1 := Contains(c.SentTickets[t.Plate], day1)
contains2 := Contains(c.SentTickets[t.Plate], day2)
if contains1 || contains2 {
return
}
if !contains1 {
c.SentTickets[t.Plate] = append(c.SentTickets[t.Plate], day1)
}
if !contains2 {
c.SentTickets[t.Plate] = append(c.SentTickets[t.Plate], day2)
}
// Find a dispatcher for this ticket's road.
var dispatcher *Client
for _, client := range c.Clients {
if client.IsDispatcher && Contains(client.Roads, t.Road) {
dispatcher = client
break
}
}
if dispatcher == nil {
c.TicketsToSendLater[t.Road] = append(c.TicketsToSendLater[t.Road], t)
return
}
// Send this ticket now
dispatcher.WriteTicket(t)
}
func (c *Coordinator) Remove(client *Client) {
c.Lock()
defer c.Unlock()
delete(c.Clients, client.ID)
}
func (c *Coordinator) SendHeartbeats() {
c.Lock()
defer c.Unlock()
for _, client := range c.Clients {
if !client.WantsHeartbeat {
continue
}
client.HeartbeatCounter--
if client.HeartbeatCounter == 0 {
client.HeartbeatCounter = client.HeartbeatInterval
client.Write8(0x41)
}
}
}
type Observation struct {
Plate string
Timestamp uint32
Road Road
Mile uint16
}
type Ticket struct {
Plate string
Road Road
Timestamp1, Timestamp2 uint32
Mile1, Mile2 uint16
Speed uint16
}