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 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) { 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: c.swstate = obj.(FeedbackMessage) case MessageTypeFeedbackRequest: // ignore case MessageTypePower: // ignore case MessageTypePowerRequest: // ignore case MessageTypeDispense: 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 { return c.bus.Publish(DispenseMessage{slot}.Encode()) } func (c *Can) Cancel() error { return c.bus.Publish(DispenseMessage{DispenseSlotOff}.Encode()) } func (c *Can) initialize() error { // enable automatic status updates return c.bus.Publish(AutoMessage{true}.Encode()) }