177 lines
3.7 KiB
Go
177 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 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,
|
|
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 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 {
|
|
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(DispenseMessage{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(DispenseMessage{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(AutoMessage{true}.Encode())
|
|
c.bus.Publish(RequestFeedbackMessage())
|
|
c.bus.Publish(RequestPowerMessage())
|
|
}
|
|
}
|