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()) }