2021-01-31 16:56:33 +01:00
|
|
|
package uncanny
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"github.com/brutella/can"
|
2021-05-06 10:30:50 +02:00
|
|
|
"time"
|
2021-01-31 16:56:33 +01:00
|
|
|
)
|
|
|
|
|
2021-02-08 13:04:30 +01:00
|
|
|
const queueSize = 10
|
|
|
|
|
2021-01-31 16:56:33 +01:00
|
|
|
type Can struct {
|
|
|
|
bus *can.Bus
|
2021-02-08 13:04:30 +01:00
|
|
|
queue chan func()
|
|
|
|
|
2021-05-04 23:52:43 +02:00
|
|
|
swstate FeedbackStatus
|
2021-01-31 16:56:33 +01:00
|
|
|
dispstate int
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewCan(intf string) (*Can, error) {
|
|
|
|
bus, err := can.NewBusForInterfaceWithName(intf)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.New(fmt.Sprintf("Can't open %s: %v", intf, err))
|
|
|
|
}
|
|
|
|
can := &Can{
|
|
|
|
bus: bus,
|
2021-02-08 13:04:30 +01:00
|
|
|
queue: make(chan func(), queueSize),
|
2021-01-31 16:56:33 +01:00
|
|
|
dispstate: -1,
|
|
|
|
}
|
|
|
|
bus.Subscribe(can)
|
|
|
|
return can, nil
|
|
|
|
}
|
|
|
|
|
2021-02-08 13:04:30 +01:00
|
|
|
func (c *Can) Start() error {
|
|
|
|
go c.process()
|
|
|
|
log.Printf("Starting bus listener")
|
|
|
|
return c.bus.ConnectAndPublish()
|
2021-01-31 16:56:33 +01:00
|
|
|
}
|
|
|
|
|
2021-02-08 13:04:30 +01:00
|
|
|
func (c *Can) process() {
|
|
|
|
running := true
|
|
|
|
log.Printf("Starting processing queue")
|
|
|
|
for running {
|
|
|
|
select {
|
|
|
|
case fn, ok := <-c.queue:
|
|
|
|
if ok {
|
|
|
|
fn()
|
|
|
|
} else {
|
|
|
|
running = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for range c.queue {}
|
|
|
|
log.Printf("Processing queue shut down")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Can) Stop() error {
|
|
|
|
log.Printf("Shutting down processing queue and bus listener")
|
|
|
|
close(c.queue)
|
|
|
|
return c.bus.Disconnect()
|
2021-01-31 16:56:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Can) Handle(frame can.Frame) {
|
2021-02-08 13:04:30 +01:00
|
|
|
c.queue <- func() {
|
|
|
|
log.Printf("Got CAN frame to 0x%08x", frame.ID)
|
|
|
|
typ, obj, err := DecodeMessage(frame)
|
|
|
|
if err != nil {
|
|
|
|
if err == UnsupportedMessageType {
|
|
|
|
log.Printf("Unsupported message type: 0x%08x", frame.ID)
|
|
|
|
} else {
|
|
|
|
log.Printf("Cannot decode message: %v", err)
|
|
|
|
}
|
2021-01-31 16:56:33 +01:00
|
|
|
} else {
|
2021-02-08 13:04:30 +01:00
|
|
|
switch typ {
|
2021-05-04 23:52:43 +02:00
|
|
|
case MessageTypeFeedbackStatus:
|
|
|
|
log.Printf("Got feedback status message: %+v", obj.(FeedbackStatus))
|
2021-02-08 13:04:30 +01:00
|
|
|
log.Printf("Data: 0x%02x 0x%02x", frame.Data[0], frame.Data[1])
|
2021-05-04 23:52:43 +02:00
|
|
|
c.swstate = obj.(FeedbackStatus)
|
2021-02-14 05:10:19 +01:00
|
|
|
// when all end switches are open (i.e. true): send stop command
|
|
|
|
if c.swstate.EndD && c.swstate.EndE && c.swstate.EndF && c.swstate.EndG && c.swstate.EndH {
|
2021-02-08 13:04:30 +01:00
|
|
|
log.Printf("All end switches off, stopping dispenser")
|
|
|
|
c.Cancel()
|
|
|
|
}
|
2021-05-04 23:52:43 +02:00
|
|
|
case MessageTypePowerStatus:
|
|
|
|
log.Printf("Got power status message: %+v", obj.(PowerStatus))
|
2021-02-08 13:04:30 +01:00
|
|
|
// ignore
|
2021-05-04 23:52:43 +02:00
|
|
|
case MessageTypeDispenseCommand:
|
|
|
|
log.Printf("Got dispense command message: %+v", obj.(DispenseCommand))
|
|
|
|
c.dispstate = obj.(DispenseCommand).Slot
|
|
|
|
case MessageTypeDisplayStatus:
|
|
|
|
log.Printf("Got display status message: %+v", obj.(DisplayStatus))
|
2021-01-31 18:37:47 +01:00
|
|
|
}
|
2021-01-31 16:56:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Can) IsEmpty(slot int) bool {
|
2021-02-08 13:04:30 +01:00
|
|
|
ret := make(chan bool)
|
|
|
|
c.queue <- func() {
|
|
|
|
switch (slot) {
|
|
|
|
case 0:
|
|
|
|
ret <- c.swstate.EmptyD
|
|
|
|
case 1:
|
|
|
|
ret <- c.swstate.EmptyE
|
|
|
|
case 2:
|
|
|
|
ret <- c.swstate.EmptyF
|
|
|
|
case 3:
|
|
|
|
ret <- c.swstate.EmptyG
|
|
|
|
case 4:
|
|
|
|
ret <- c.swstate.EmptyH
|
|
|
|
}
|
|
|
|
ret <- false
|
2021-01-31 16:56:33 +01:00
|
|
|
}
|
2021-02-08 13:04:30 +01:00
|
|
|
return <-ret
|
2021-01-31 16:56:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Can) IsDispensing(slot int) bool {
|
2021-02-08 13:04:30 +01:00
|
|
|
ret := make(chan bool)
|
|
|
|
c.queue <- func() {
|
|
|
|
switch (slot) {
|
|
|
|
case 0:
|
|
|
|
ret <- c.swstate.EndD
|
|
|
|
case 1:
|
|
|
|
ret <- c.swstate.EndE
|
|
|
|
case 2:
|
|
|
|
ret <- c.swstate.EndF
|
|
|
|
case 3:
|
|
|
|
ret <- c.swstate.EndG
|
|
|
|
case 4:
|
|
|
|
ret <- c.swstate.EndH
|
|
|
|
}
|
|
|
|
ret <- false
|
2021-01-31 16:56:33 +01:00
|
|
|
}
|
2021-02-08 13:04:30 +01:00
|
|
|
return <-ret
|
2021-01-31 16:56:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Can) ActiveDispenser() int {
|
2021-02-08 13:04:30 +01:00
|
|
|
ret := make(chan int)
|
|
|
|
c.queue <- func() {
|
|
|
|
ret <- c.dispstate
|
|
|
|
}
|
|
|
|
return <- ret
|
2021-01-31 16:56:33 +01:00
|
|
|
}
|
|
|
|
|
2021-02-08 13:04:30 +01:00
|
|
|
func (c *Can) Dispense(slot int) {
|
|
|
|
c.queue <- func() {
|
|
|
|
log.Printf("Starting dispense on slot %d", slot)
|
2021-05-04 23:52:43 +02:00
|
|
|
err := c.bus.Publish(DispenseCommand{slot}.Encode())
|
2021-02-08 13:04:30 +01:00
|
|
|
if err != nil {
|
|
|
|
log.Printf("Error sending dispense command: %v", err)
|
|
|
|
}
|
|
|
|
}
|
2021-01-31 16:56:33 +01:00
|
|
|
}
|
|
|
|
|
2021-02-08 13:04:30 +01:00
|
|
|
func (c *Can) Cancel() {
|
|
|
|
c.queue <- func() {
|
|
|
|
log.Printf("Stopping dispense")
|
2021-05-04 23:52:43 +02:00
|
|
|
err := c.bus.Publish(DispenseCommand{DispenseSlotOff}.Encode())
|
2021-02-08 13:04:30 +01:00
|
|
|
if err != nil {
|
|
|
|
log.Printf("Error sending stop command: %v", err)
|
|
|
|
}
|
|
|
|
}
|
2021-01-31 16:56:33 +01:00
|
|
|
}
|
|
|
|
|
2021-02-08 13:04:30 +01:00
|
|
|
func (c *Can) Initialize() {
|
|
|
|
c.queue <- func() {
|
|
|
|
log.Printf("Initializing devices")
|
|
|
|
// enable automatic status updates
|
2021-05-04 23:52:43 +02:00
|
|
|
c.bus.Publish(AutoCommand{true}.Encode())
|
|
|
|
c.bus.Publish(EncodeFeedbackRequest())
|
|
|
|
c.bus.Publish(EncodePowerRequest())
|
|
|
|
c.bus.Publish(EncodeDisplayRequest())
|
2021-05-05 22:34:41 +02:00
|
|
|
c.DisplayPrint(0, "Hello world")
|
2021-05-06 10:30:50 +02:00
|
|
|
go func() {
|
|
|
|
t := time.Tick(100 * time.Millisecond)
|
|
|
|
var d rune
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-t:
|
|
|
|
c.DisplayPrint(1, string([]rune{d}))
|
|
|
|
d = (d + 1) & 0xff
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
2021-02-08 13:04:30 +01:00
|
|
|
}
|
2021-01-31 16:56:33 +01:00
|
|
|
}
|