matemat/uncanny/can.go
2021-02-14 05:10:19 +01:00

177 lines
3.7 KiB
Go

package uncanny
import (
"errors"
"fmt"
"log"
"github.com/brutella/can"
)
const queueSize = 10
type Can struct {
bus *can.Bus
queue chan func()
swstate FeedbackMessage
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,
queue: make(chan func(), queueSize),
dispstate: -1,
}
bus.Subscribe(can)
return can, nil
}
func (c *Can) Start() error {
go c.process()
log.Printf("Starting bus listener")
return c.bus.ConnectAndPublish()
}
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()
}
func (c *Can) Handle(frame can.Frame) {
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)
}
} else {
switch typ {
case MessageTypeFeedback:
log.Printf("Got feedback message: %+v", obj.(FeedbackMessage))
log.Printf("Data: 0x%02x 0x%02x", frame.Data[0], frame.Data[1])
c.swstate = obj.(FeedbackMessage)
// 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 {
log.Printf("All end switches off, stopping dispenser")
c.Cancel()
}
case MessageTypeFeedbackRequest:
// ignore
case MessageTypePower:
log.Printf("Got power message: %+v", obj.(PowerMessage))
// ignore
case MessageTypePowerRequest:
// ignore
case MessageTypeDispense:
log.Printf("Got dispense message: %+v", obj.(DispenseMessage))
c.dispstate = obj.(DispenseMessage).Slot
case MessageTypeAuto:
// ignore
}
}
}
}
func (c *Can) IsEmpty(slot int) bool {
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
}
return <-ret
}
func (c *Can) IsDispensing(slot int) bool {
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
}
return <-ret
}
func (c *Can) ActiveDispenser() int {
ret := make(chan int)
c.queue <- func() {
ret <- c.dispstate
}
return <- ret
}
func (c *Can) Dispense(slot int) {
c.queue <- func() {
log.Printf("Starting dispense on slot %d", slot)
err := c.bus.Publish(DispenseMessage{slot}.Encode())
if err != nil {
log.Printf("Error sending dispense command: %v", err)
}
}
}
func (c *Can) Cancel() {
c.queue <- func() {
log.Printf("Stopping dispense")
err := c.bus.Publish(DispenseMessage{DispenseSlotOff}.Encode())
if err != nil {
log.Printf("Error sending stop command: %v", err)
}
}
}
func (c *Can) Initialize() {
c.queue <- func() {
log.Printf("Initializing devices")
// enable automatic status updates
c.bus.Publish(AutoMessage{true}.Encode())
c.bus.Publish(RequestFeedbackMessage())
c.bus.Publish(RequestPowerMessage())
}
}