matemat/uncanny/can.go
2021-05-05 22:34:41 +02:00

175 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 FeedbackStatus
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 MessageTypeFeedbackStatus:
log.Printf("Got feedback status message: %+v", obj.(FeedbackStatus))
log.Printf("Data: 0x%02x 0x%02x", frame.Data[0], frame.Data[1])
c.swstate = obj.(FeedbackStatus)
// 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 MessageTypePowerStatus:
log.Printf("Got power status message: %+v", obj.(PowerStatus))
// ignore
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))
}
}
}
}
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(DispenseCommand{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(DispenseCommand{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(AutoCommand{true}.Encode())
c.bus.Publish(EncodeFeedbackRequest())
c.bus.Publish(EncodePowerRequest())
c.bus.Publish(EncodeDisplayRequest())
c.DisplayPrint(0, "Hello world")
}
}