131 lines
2.9 KiB
Go
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.EmptyD && !c.swstate.EmptyE && !c.swstate.EmptyF && !c.swstate.EmptyG && !c.swstate.EmptyH {
|
|
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())
|
|
}
|