306 lines
10 KiB
Go
306 lines
10 KiB
Go
|
package uncanny
|
||
|
|
||
|
/*
|
||
|
// include our message format definitions
|
||
|
#cgo CPPFLAGS: -I${SRCDIR}/..
|
||
|
#include <messages.h>
|
||
|
|
||
|
// since we can't use macros in Go directly, let's add some wrappers
|
||
|
// to avoid conflicts, we'll simply lowercase the symbol names
|
||
|
|
||
|
const uint16_t can_msg_feedback_status = CAN_MSG_FEEDBACK_STATUS;
|
||
|
const size_t can_msg_feedback_status_field_reset_sw = CAN_MSG_FEEDBACK_STATUS_FIELD_RESET_SW;
|
||
|
const uint8_t can_msg_feedback_status_bit_reset_sw = CAN_MSG_FEEDBACK_STATUS_BIT_RESET_SW;
|
||
|
const size_t can_msg_feedback_status_field_empty_h = CAN_MSG_FEEDBACK_STATUS_FIELD_EMPTY_H;
|
||
|
const uint8_t can_msg_feedback_status_bit_empty_h = CAN_MSG_FEEDBACK_STATUS_BIT_EMPTY_H;
|
||
|
const size_t can_msg_feedback_status_field_empty_g = CAN_MSG_FEEDBACK_STATUS_FIELD_EMPTY_G;
|
||
|
const uint8_t can_msg_feedback_status_bit_empty_g = CAN_MSG_FEEDBACK_STATUS_BIT_EMPTY_G;
|
||
|
const size_t can_msg_feedback_status_field_empty_f = CAN_MSG_FEEDBACK_STATUS_FIELD_EMPTY_F;
|
||
|
const uint8_t can_msg_feedback_status_bit_empty_f = CAN_MSG_FEEDBACK_STATUS_BIT_EMPTY_F;
|
||
|
const size_t can_msg_feedback_status_field_empty_e = CAN_MSG_FEEDBACK_STATUS_FIELD_EMPTY_E;
|
||
|
const uint8_t can_msg_feedback_status_bit_empty_e = CAN_MSG_FEEDBACK_STATUS_BIT_EMPTY_E;
|
||
|
const size_t can_msg_feedback_status_field_empty_d = CAN_MSG_FEEDBACK_STATUS_FIELD_EMPTY_D;
|
||
|
const uint8_t can_msg_feedback_status_bit_empty_d = CAN_MSG_FEEDBACK_STATUS_BIT_EMPTY_D;
|
||
|
const size_t can_msg_feedback_status_field_end_h = CAN_MSG_FEEDBACK_STATUS_FIELD_END_H;
|
||
|
const uint8_t can_msg_feedback_status_bit_end_h = CAN_MSG_FEEDBACK_STATUS_BIT_END_H;
|
||
|
const size_t can_msg_feedback_status_field_end_g = CAN_MSG_FEEDBACK_STATUS_FIELD_END_G;
|
||
|
const uint8_t can_msg_feedback_status_bit_end_g = CAN_MSG_FEEDBACK_STATUS_BIT_END_G;
|
||
|
const size_t can_msg_feedback_status_field_end_f = CAN_MSG_FEEDBACK_STATUS_FIELD_END_F;
|
||
|
const uint8_t can_msg_feedback_status_bit_end_f = CAN_MSG_FEEDBACK_STATUS_BIT_END_F;
|
||
|
const size_t can_msg_feedback_status_field_end_e = CAN_MSG_FEEDBACK_STATUS_FIELD_END_E;
|
||
|
const uint8_t can_msg_feedback_status_bit_end_e = CAN_MSG_FEEDBACK_STATUS_BIT_END_E;
|
||
|
const size_t can_msg_feedback_status_field_end_d = CAN_MSG_FEEDBACK_STATUS_FIELD_END_D;
|
||
|
const uint8_t can_msg_feedback_status_bit_end_d = CAN_MSG_FEEDBACK_STATUS_BIT_END_D;
|
||
|
|
||
|
const uint16_t can_msg_power_status = CAN_MSG_POWER_STATUS;
|
||
|
|
||
|
const uint16_t can_msg_power_dispense = CAN_MSG_POWER_DISPENSE;
|
||
|
const uint8_t can_msg_power_dispense_off = CAN_MSG_POWER_DISPENSE_OFF;
|
||
|
const uint8_t can_msg_power_dispense_slot1 = CAN_MSG_POWER_DISPENSE_SLOT1;
|
||
|
const uint8_t can_msg_power_dispense_slot2 = CAN_MSG_POWER_DISPENSE_SLOT2;
|
||
|
const uint8_t can_msg_power_dispense_slot3 = CAN_MSG_POWER_DISPENSE_SLOT3;
|
||
|
const uint8_t can_msg_power_dispense_slot4 = CAN_MSG_POWER_DISPENSE_SLOT4;
|
||
|
const uint8_t can_msg_power_dispense_slot5 = CAN_MSG_POWER_DISPENSE_SLOT5;
|
||
|
|
||
|
const uint16_t can_msg_auto_status = CAN_MSG_AUTO_STATUS;
|
||
|
const uint8_t can_msg_auto_status_disable = CAN_MSG_AUTO_STATUS_DISABLE;
|
||
|
const uint8_t can_msg_auto_status_enable = CAN_MSG_AUTO_STATUS_ENABLE;
|
||
|
*/
|
||
|
import "C"
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"github.com/brutella/can"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
InvalidMessageID = errors.New("Invalid message ID, cannot decode")
|
||
|
InvalidMessageLength = errors.New("Invalid message length, cannot decode")
|
||
|
UnsupportedMessageType = errors.New("Unsupported message type, cannot decode")
|
||
|
)
|
||
|
|
||
|
type MessageType int
|
||
|
const (
|
||
|
MessageTypeInvalid MessageType = iota
|
||
|
MessageTypeFeedback
|
||
|
MessageTypeFeedbackRequest
|
||
|
MessageTypePower
|
||
|
MessageTypePowerRequest
|
||
|
MessageTypeDispense
|
||
|
MessageTypeAuto
|
||
|
)
|
||
|
|
||
|
// DecodeMessage decodes a CAN frame and returns an appropriate message
|
||
|
// object and the message type.
|
||
|
//
|
||
|
// For request messages, only the type is returned (object and error are nil)
|
||
|
//
|
||
|
// When the message cannot be decoded, a nil object and MessageTypeInvalid is
|
||
|
// returned, plus an appropriate error. If the message ID is unknown, error
|
||
|
// will be UnsupportedMessageType.
|
||
|
func DecodeMessage(frame can.Frame) (MessageType, interface{}, error) {
|
||
|
switch frame.ID {
|
||
|
case uint32(C.can_msg_feedback_status):
|
||
|
msg, err := DecodeFeedbackMessage(frame)
|
||
|
return MessageTypeFeedback, msg, err
|
||
|
case uint32(C.can_msg_feedback_status) | can.MaskRtr:
|
||
|
return MessageTypeFeedbackRequest, nil, nil
|
||
|
case uint32(C.can_msg_power_status):
|
||
|
msg, err := DecodePowerMessage(frame)
|
||
|
return MessageTypePower, msg, err
|
||
|
case uint32(C.can_msg_power_status) | can.MaskRtr:
|
||
|
return MessageTypePowerRequest, nil, nil
|
||
|
case uint32(C.can_msg_power_dispense):
|
||
|
msg, err := DecodeDispenseMessage(frame)
|
||
|
return MessageTypeDispense, msg, err
|
||
|
case uint32(C.can_msg_auto_status):
|
||
|
msg, err := DecodeAutoMessage(frame)
|
||
|
return MessageTypeAuto, msg, err
|
||
|
}
|
||
|
return MessageTypeInvalid, nil, UnsupportedMessageType
|
||
|
}
|
||
|
|
||
|
type FeedbackMessage struct {
|
||
|
EndD bool
|
||
|
EndE bool
|
||
|
EndF bool
|
||
|
EndG bool
|
||
|
EndH bool
|
||
|
EmptyD bool
|
||
|
EmptyE bool
|
||
|
EmptyF bool
|
||
|
EmptyG bool
|
||
|
EmptyH bool
|
||
|
ResetSw bool
|
||
|
}
|
||
|
|
||
|
func RequestFeedbackMessage() can.Frame {
|
||
|
return can.Frame{
|
||
|
ID: uint32(C.can_msg_feedback_status) | can.MaskRtr,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func DecodeFeedbackMessage(frame can.Frame) (FeedbackMessage, error) {
|
||
|
var ret FeedbackMessage
|
||
|
if frame.ID != uint32(C.can_msg_feedback_status) {
|
||
|
return ret, InvalidMessageID
|
||
|
}
|
||
|
if frame.Length != 2 {
|
||
|
return ret, InvalidMessageLength
|
||
|
}
|
||
|
ret.EndD = frame.Data[C.can_msg_feedback_status_field_end_d] & C.can_msg_feedback_status_bit_end_d != 0
|
||
|
ret.EndE = frame.Data[C.can_msg_feedback_status_field_end_e] & C.can_msg_feedback_status_bit_end_e != 0
|
||
|
ret.EndF = frame.Data[C.can_msg_feedback_status_field_end_f] & C.can_msg_feedback_status_bit_end_f != 0
|
||
|
ret.EndG = frame.Data[C.can_msg_feedback_status_field_end_g] & C.can_msg_feedback_status_bit_end_g != 0
|
||
|
ret.EndH = frame.Data[C.can_msg_feedback_status_field_end_h] & C.can_msg_feedback_status_bit_end_h != 0
|
||
|
ret.EmptyD = frame.Data[C.can_msg_feedback_status_field_empty_d] & C.can_msg_feedback_status_bit_empty_d != 0
|
||
|
ret.EmptyE = frame.Data[C.can_msg_feedback_status_field_empty_e] & C.can_msg_feedback_status_bit_empty_e != 0
|
||
|
ret.EmptyF = frame.Data[C.can_msg_feedback_status_field_empty_f] & C.can_msg_feedback_status_bit_empty_f != 0
|
||
|
ret.EmptyG = frame.Data[C.can_msg_feedback_status_field_empty_g] & C.can_msg_feedback_status_bit_empty_g != 0
|
||
|
ret.EmptyH = frame.Data[C.can_msg_feedback_status_field_empty_h] & C.can_msg_feedback_status_bit_empty_h != 0
|
||
|
ret.ResetSw = frame.Data[C.can_msg_feedback_status_field_reset_sw] & C.can_msg_feedback_status_bit_reset_sw != 0
|
||
|
return ret, nil
|
||
|
}
|
||
|
|
||
|
func (f FeedbackMessage) Encode() can.Frame {
|
||
|
data := [8]uint8{}
|
||
|
if f.EndD {
|
||
|
data[C.can_msg_feedback_status_field_end_d] |= C.can_msg_feedback_status_bit_end_d
|
||
|
}
|
||
|
if f.EndE {
|
||
|
data[C.can_msg_feedback_status_field_end_e] |= C.can_msg_feedback_status_bit_end_e
|
||
|
}
|
||
|
if f.EndF {
|
||
|
data[C.can_msg_feedback_status_field_end_f] |= C.can_msg_feedback_status_bit_end_f
|
||
|
}
|
||
|
if f.EndG {
|
||
|
data[C.can_msg_feedback_status_field_end_g] |= C.can_msg_feedback_status_bit_end_g
|
||
|
}
|
||
|
if f.EndH {
|
||
|
data[C.can_msg_feedback_status_field_end_h] |= C.can_msg_feedback_status_bit_end_h
|
||
|
}
|
||
|
if f.EmptyD {
|
||
|
data[C.can_msg_feedback_status_field_empty_d] |= C.can_msg_feedback_status_bit_empty_d
|
||
|
}
|
||
|
if f.EmptyE {
|
||
|
data[C.can_msg_feedback_status_field_empty_e] |= C.can_msg_feedback_status_bit_empty_e
|
||
|
}
|
||
|
if f.EmptyF {
|
||
|
data[C.can_msg_feedback_status_field_empty_f] |= C.can_msg_feedback_status_bit_empty_f
|
||
|
}
|
||
|
if f.EmptyG {
|
||
|
data[C.can_msg_feedback_status_field_empty_g] |= C.can_msg_feedback_status_bit_empty_g
|
||
|
}
|
||
|
if f.EmptyH {
|
||
|
data[C.can_msg_feedback_status_field_empty_h] |= C.can_msg_feedback_status_bit_empty_h
|
||
|
}
|
||
|
if f.ResetSw {
|
||
|
data[C.can_msg_feedback_status_field_reset_sw] |= C.can_msg_feedback_status_bit_reset_sw
|
||
|
}
|
||
|
return can.Frame{
|
||
|
ID: uint32(C.can_msg_feedback_status),
|
||
|
Length: 2,
|
||
|
Data: data,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type PowerMessage struct {
|
||
|
Bits int
|
||
|
}
|
||
|
|
||
|
func RequestPowerMessage() can.Frame {
|
||
|
return can.Frame{
|
||
|
ID: uint32(C.can_msg_power_status) | can.MaskRtr,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func DecodePowerMessage(frame can.Frame) (PowerMessage, error) {
|
||
|
var ret PowerMessage
|
||
|
if frame.ID != uint32(C.can_msg_power_status) {
|
||
|
return ret, InvalidMessageID
|
||
|
}
|
||
|
if frame.Length != 2 {
|
||
|
return ret, InvalidMessageLength
|
||
|
}
|
||
|
ret.Bits = (int(frame.Data[0]) << 8) | int(frame.Data[1])
|
||
|
return ret, nil
|
||
|
}
|
||
|
|
||
|
func (f PowerMessage) Encode() can.Frame {
|
||
|
data := [8]uint8{}
|
||
|
data[0] = uint8(f.Bits >> 8)
|
||
|
data[1] = uint8(f.Bits)
|
||
|
return can.Frame{
|
||
|
ID: uint32(C.can_msg_power_status),
|
||
|
Length: 2,
|
||
|
Data: data,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const DispenseSlotOff int = -1
|
||
|
|
||
|
type DispenseMessage struct {
|
||
|
Slot int
|
||
|
}
|
||
|
|
||
|
func DecodeDispenseMessage(frame can.Frame) (DispenseMessage, error) {
|
||
|
var ret DispenseMessage
|
||
|
if frame.ID != uint32(C.can_msg_power_dispense) {
|
||
|
return ret, InvalidMessageID
|
||
|
}
|
||
|
if frame.Length != 1 {
|
||
|
return ret, InvalidMessageLength
|
||
|
}
|
||
|
switch frame.Data[0] {
|
||
|
case C.can_msg_power_dispense_off:
|
||
|
ret.Slot = DispenseSlotOff
|
||
|
case C.can_msg_power_dispense_slot1:
|
||
|
ret.Slot = 0
|
||
|
case C.can_msg_power_dispense_slot2:
|
||
|
ret.Slot = 1
|
||
|
case C.can_msg_power_dispense_slot3:
|
||
|
ret.Slot = 2
|
||
|
case C.can_msg_power_dispense_slot4:
|
||
|
ret.Slot = 3
|
||
|
case C.can_msg_power_dispense_slot5:
|
||
|
ret.Slot = 4
|
||
|
}
|
||
|
return ret, nil
|
||
|
}
|
||
|
|
||
|
func (f DispenseMessage) Encode() can.Frame {
|
||
|
data := [8]uint8{}
|
||
|
switch f.Slot {
|
||
|
case DispenseSlotOff:
|
||
|
data[0] = C.can_msg_power_dispense_off
|
||
|
case 0:
|
||
|
data[0] = C.can_msg_power_dispense_slot1
|
||
|
case 1:
|
||
|
data[0] = C.can_msg_power_dispense_slot2
|
||
|
case 2:
|
||
|
data[0] = C.can_msg_power_dispense_slot3
|
||
|
case 3:
|
||
|
data[0] = C.can_msg_power_dispense_slot4
|
||
|
case 4:
|
||
|
data[0] = C.can_msg_power_dispense_slot5
|
||
|
}
|
||
|
return can.Frame{
|
||
|
ID: uint32(C.can_msg_power_dispense),
|
||
|
Length: 1,
|
||
|
Data: data,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type AutoMessage struct {
|
||
|
AutoUpdate bool
|
||
|
}
|
||
|
|
||
|
func DecodeAutoMessage(frame can.Frame) (AutoMessage, error) {
|
||
|
var ret AutoMessage
|
||
|
if frame.ID != uint32(C.can_msg_auto_status) {
|
||
|
return ret, InvalidMessageID
|
||
|
}
|
||
|
if frame.Length != 1 {
|
||
|
return ret, InvalidMessageLength
|
||
|
}
|
||
|
// we accept all non-null values as "enable"
|
||
|
if frame.Data[0] != C.can_msg_auto_status_disable {
|
||
|
ret.AutoUpdate = true
|
||
|
}
|
||
|
return ret, nil
|
||
|
}
|
||
|
|
||
|
func (f AutoMessage) Encode() can.Frame {
|
||
|
data := [8]uint8{}
|
||
|
if f.AutoUpdate {
|
||
|
data[0] = C.can_msg_auto_status_enable
|
||
|
} else {
|
||
|
data[0] = C.can_msg_auto_status_disable
|
||
|
}
|
||
|
return can.Frame{
|
||
|
ID: uint32(C.can_msg_auto_status),
|
||
|
Length: 1,
|
||
|
Data: data,
|
||
|
}
|
||
|
}
|