-
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathcontroller.go
152 lines (136 loc) · 3.49 KB
/
controller.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
package dcc
import "sync"
// CommandRepeat specifies how many times a single
// packet is sent.
var CommandRepeat = 30
// CommandMaxQueue specifies how many commands can
// queue before sending a new command blocks
// the sender
var CommandMaxQueue = 3
// Controller represents a DCC Control Station. The
// controller keeps tracks of the DCC Locomotives and
// is in charge of sending DCC packets continuously to
// the tracks.
type Controller struct {
locomotives map[string]*Locomotive
mux sync.RWMutex
driver Driver
started bool
doneCh chan bool
shutdownCh chan bool
commandCh chan *Packet
}
// NewController builds a Controller.
func NewController(d Driver) *Controller {
d.TracksOff()
return &Controller{
driver: d,
locomotives: make(map[string]*Locomotive),
doneCh: make(chan bool),
shutdownCh: make(chan bool),
commandCh: make(chan *Packet, CommandMaxQueue),
}
}
// NewControllerWithConfig builds a new Controller using the
// given configuration.
func NewControllerWithConfig(d Driver, cfg *Config) *Controller {
c := NewController(d)
for _, loco := range cfg.Locomotives {
c.AddLoco(&Locomotive{
Name: loco.Name,
Address: loco.Address,
Speed: loco.Speed,
Direction: loco.Direction,
Fl: loco.Fl})
}
return c
}
// AddLoco adds a DCC device to the controller. The device
// will start receiving packets if the controller is running.
func (c *Controller) AddLoco(l *Locomotive) {
c.mux.Lock()
defer c.mux.Unlock()
c.locomotives[l.Name] = l
}
// RmLoco removes a DCC device from the controller. There
// will be no longer packets sent to it.
func (c *Controller) RmLoco(l *Locomotive) {
c.mux.Lock()
defer c.mux.Unlock()
delete(c.locomotives, l.Name)
}
// GetLoco retrieves a DCC device by its Name. The boolean is
// true if the Locomotive was found.
func (c *Controller) GetLoco(n string) (*Locomotive, bool) {
c.mux.RLock()
defer c.mux.RUnlock()
l, ok := c.locomotives[n]
return l, ok
}
// Locos returns a list of all registered Locomotives.
func (c *Controller) Locos() []*Locomotive {
c.mux.RLock()
defer c.mux.RUnlock()
locos := make([]*Locomotive, 0, len(c.locomotives))
for _, l := range c.locomotives {
locos = append(locos, l)
}
return locos
}
// Command allows to send a custom Packet to the tracks.
// The packet will be sent CommandRepeat times.
func (c *Controller) Command(p *Packet) {
c.commandCh <- p
}
// Start starts the controller: powers on the tracks
// and starts sending packets on them.
func (c *Controller) Start() {
c.driver.TracksOn()
go c.run()
c.started = true
}
// Stop shuts down the controller by stopping to send
// packets and removing power from the tracks.
func (c *Controller) Stop() {
if c.started {
c.shutdownCh <- true
<-c.doneCh
c.started = false
}
}
func (c *Controller) run() {
idle := NewBroadcastIdlePacket(c.driver)
stop := NewBroadcastStopPacket(c.driver, Forward, false, true)
for {
select {
case <-c.shutdownCh:
for i := 0; i < CommandRepeat; i++ {
stop.Send()
}
c.driver.TracksOff()
c.doneCh <- true
return
case p := <-c.commandCh:
for i := 0; i < CommandRepeat; i++ {
p.Send()
}
default:
c.mux.RLock()
{
// Idle and retry later
if len(c.locomotives) == 0 {
c.commandCh <- idle
c.mux.RUnlock()
break // from the select
}
for _, loco := range c.locomotives {
for i := 0; i < CommandRepeat; i++ {
loco.sendPackets(c.driver)
}
}
}
c.mux.RUnlock()
idle.PacketPause()
}
}
}