-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathdevice.go
199 lines (158 loc) · 4.87 KB
/
device.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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
package prolink
import (
"fmt"
"net"
"sync"
"time"
)
// Defined device types.
const (
DeviceTypeCDJ DeviceType = 0x01
DeviceTypeMixer DeviceType = 0x03
DeviceTypeRB DeviceType = 0x04
)
var deviceTypeLabels = map[DeviceType]string{
DeviceTypeCDJ: "cdj",
DeviceTypeMixer: "djm",
DeviceTypeRB: "rekordbox",
}
// VirtualCDJName is the name given to the Virtual CDJ device.
const VirtualCDJName = "prolink-go"
// VirtualCDJFirmware is a string indicating the firmware version reported with
// status packets.
const VirtualCDJFirmware = "1.43"
// DeviceType represents the types of devices on the network.
type DeviceType byte
// String returns a string representation of a device.
func (d DeviceType) String() string {
return deviceTypeLabels[d]
}
// DeviceID represents the ID of the device. For CDJs this is the number
// displayed on screen.
type DeviceID byte
// Device represents a device on the network.
type Device struct {
Name string
ID DeviceID
Type DeviceType
MacAddr net.HardwareAddr
IP net.IP
LastActive time.Time
}
// String returns a string representation of a device.
func (d *Device) String() string {
return fmt.Sprintf("%s %02d @ %s [%s]", d.Name, d.ID, d.IP, d.MacAddr)
}
// A DeviceListener responds to devices being added and removed from the PRO DJ
// LINK network.
type DeviceListener interface {
OnChange(*Device)
}
// The DeviceListenerFunc is an adapter to allow a function to be used as a
// listener for device changes.
type DeviceListenerFunc func(*Device)
// OnChange implements the DeviceListener interface.
func (f DeviceListenerFunc) OnChange(d *Device) { f(d) }
// DeviceManager provides functionality for watching the connection status of
// PRO DJ LINK devices on the network.
type DeviceManager struct {
delHandlers map[string]DeviceListener
addHandlers map[string]DeviceListener
devices map[DeviceID]*Device
}
// OnDeviceAdded registers a listener that will be called when any PRO DJ LINK
// devices are added to the network. Provide a key if you wish to remove the
// handler later with RemoveListener by specifying the same key.
func (m *DeviceManager) OnDeviceAdded(key string, fn DeviceListener) {
m.addHandlers[key] = fn
}
// OnDeviceRemoved registers a listener that will be called when any PRO DJ
// LINK devices are removed from the network. Provide a key if you wish to
// remove the handler later with RemoveListener by specifying the same key.
func (m *DeviceManager) OnDeviceRemoved(key string, fn DeviceListener) {
m.delHandlers[key] = fn
}
// RemoveListener removes a DeviceListener that may have been added by
// OnDeviceAdded or OnDeviceRemoved. Use the key you provided when adding the
// handler.
func (m *DeviceManager) RemoveListener(key string, fn DeviceListener) {
delete(m.addHandlers, key)
delete(m.delHandlers, key)
}
// ActiveDeviceMap returns a mapping of device IDs to their associated devices.
func (m *DeviceManager) ActiveDeviceMap() map[DeviceID]*Device {
return m.devices
}
// ActiveDevices returns a list of active devices on the PRO DJ LINK network.
func (m *DeviceManager) ActiveDevices() []*Device {
devices := make([]*Device, 0, len(m.devices))
for _, dev := range m.devices {
devices = append(devices, dev)
}
return devices
}
// activate triggers the DeviceManager to begin watching for device changes on
// the PRO DJ LINK network.
func (m *DeviceManager) activate(announceConn *net.UDPConn) {
timeouts := map[DeviceID]*time.Timer{}
timeoutsLock := sync.Mutex{}
Log.Info("Now monitoring for PROLINK devices")
timeoutTimer := func(dev *Device) {
timeoutsLock.Lock()
defer timeoutsLock.Unlock()
timeouts[dev.ID] = time.NewTimer(deviceTimeout)
<-timeouts[dev.ID].C
// Device timeout expired. No longer active
delete(timeouts, dev.ID)
delete(m.devices, dev.ID)
Log.Info("Device timeout", "device", dev)
for _, h := range m.delHandlers {
go h.OnChange(dev)
}
}
announceLock := sync.Mutex{}
announceHandler := func() {
packet := make([]byte, announcePacketLen)
announceConn.Read(packet)
dev, err := deviceFromAnnouncePacket(packet)
if err != nil {
return
}
if dev.Name == VirtualCDJName {
return
}
// Update device keepalive
if dev, ok := m.devices[dev.ID]; ok {
timeout, ok := timeouts[dev.ID]
if !ok {
return
}
timeout.Stop()
timeout.Reset(deviceTimeout)
dev.LastActive = time.Now()
return
}
announceLock.Lock()
defer announceLock.Unlock()
// New device
m.devices[dev.ID] = dev
Log.Info("New device tracked", "device", dev)
for _, h := range m.addHandlers {
go h.OnChange(dev)
}
go timeoutTimer(dev)
}
// Begin listening for announce packets
go func() {
for {
announceHandler()
}
}()
}
func newDeviceManager() *DeviceManager {
return &DeviceManager{
addHandlers: map[string]DeviceListener{},
delHandlers: map[string]DeviceListener{},
devices: map[DeviceID]*Device{},
}
}