matemat/uncanny/can.go
2021-01-31 18:44:43 +01:00

131 lines
2.9 KiB
Go

package uncanny
import (
"errors"
"fmt"
"log"
"github.com/brutella/can"
)
type Can struct {
bus *can.Bus
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,
dispstate: -1,
}
bus.Subscribe(can)
return can, nil
}
func (c *Can) Start() {
go func() {
// send the init sequence, but (hopefully) defer until after we start listening
// FIXME this is inherently racy :(
go c.initialize()
// ConnectAndPublish will block, so we'll just leave the Printf hanging here
log.Printf("CAN bus shut down: %v", c.bus.ConnectAndPublish())
}()
}
func (c *Can) Stop() {
c.bus.Disconnect()
}
func (c *Can) Handle(frame can.Frame) {
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 off: 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 {
switch (slot) {
case 0:
return c.swstate.EmptyD
case 1:
return c.swstate.EmptyE
case 2:
return c.swstate.EmptyF
case 3:
return c.swstate.EmptyG
case 4:
return c.swstate.EmptyH
}
return false
}
func (c *Can) IsDispensing(slot int) bool {
switch (slot) {
case 0:
return c.swstate.EndD
case 1:
return c.swstate.EndE
case 2:
return c.swstate.EndF
case 3:
return c.swstate.EndG
case 4:
return c.swstate.EndH
}
return false
}
func (c *Can) ActiveDispenser() int {
return c.dispstate
}
func (c *Can) Dispense(slot int) error {
log.Printf("Starting dispense on slot %d", slot)
return c.bus.Publish(DispenseMessage{slot}.Encode())
}
func (c *Can) Cancel() error {
log.Printf("Stopping dispense")
return c.bus.Publish(DispenseMessage{DispenseSlotOff}.Encode())
}
func (c *Can) initialize() {
log.Printf("Initializing")
// enable automatic status updates
c.bus.Publish(AutoMessage{true}.Encode())
c.bus.Publish(RequestFeedbackMessage())
c.bus.Publish(RequestPowerMessage())
}