matemat/uncanny/message.go

395 lines
13 KiB
Go
Raw Normal View History

2021-01-31 16:56:33 +01:00
package uncanny
/*
2021-01-31 19:35:58 +01:00
// Include our message format definitions
2021-01-31 16:56:33 +01:00
#cgo CPPFLAGS: -I${SRCDIR}/..
2021-01-31 19:35:58 +01:00
#include <stdint.h>
2021-01-31 16:56:33 +01:00
#include <messages.h>
2021-01-31 19:35:58 +01:00
// Since we can't use C macros in Go directly, let's add some wrappers.
// WARNING: CGo type translation can be very wonky, so make sure to always
// cast to the expected type!
2021-01-31 16:56:33 +01:00
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 uint8_t can_msg_display_status = CAN_MSG_DISPLAY_STATUS;
const uint8_t can_msg_display_cmd = CAN_MSG_DISPLAY_CMD;
2021-01-31 16:56:33 +01:00
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
MessageTypeFeedbackStatus
2021-01-31 16:56:33 +01:00
MessageTypeFeedbackRequest
MessageTypePowerStatus
2021-01-31 16:56:33 +01:00
MessageTypePowerRequest
MessageTypeDispenseCommand
MessageTypeDisplayStatus
MessageTypeDisplayRequest
MessageTypeDisplayCommand
MessageTypeAutoCommand
2021-01-31 16:56:33 +01:00
)
// 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 := DecodeFeedbackStatus(frame)
return MessageTypeFeedbackStatus, msg, err
2021-01-31 16:56:33 +01:00
case uint32(C.can_msg_feedback_status) | can.MaskRtr:
return MessageTypeFeedbackRequest, nil, nil
case uint32(C.can_msg_power_status):
msg, err := DecodePowerStatus(frame)
return MessageTypePowerStatus, msg, err
2021-01-31 16:56:33 +01:00
case uint32(C.can_msg_power_status) | can.MaskRtr:
return MessageTypePowerRequest, nil, nil
case uint32(C.can_msg_power_dispense):
msg, err := DecodeDispenseCommand(frame)
return MessageTypeDispenseCommand, msg, err
case uint32(C.can_msg_display_status):
msg, err := DecodeDisplayStatus(frame)
return MessageTypeDisplayStatus, msg, err
case uint32(C.can_msg_display_status) | can.MaskRtr:
return MessageTypeDisplayRequest, nil, nil
case uint32(C.can_msg_display_cmd):
msg, err := DecodeDisplayCommand(frame)
return MessageTypeDisplayCommand, msg, err
2021-01-31 16:56:33 +01:00
case uint32(C.can_msg_auto_status):
msg, err := DecodeAutoCommand(frame)
return MessageTypeAutoCommand, msg, err
2021-01-31 16:56:33 +01:00
}
return MessageTypeInvalid, nil, UnsupportedMessageType
}
type FeedbackStatus struct {
2021-01-31 16:56:33 +01:00
EndD bool
EndE bool
EndF bool
EndG bool
EndH bool
EmptyD bool
EmptyE bool
EmptyF bool
EmptyG bool
EmptyH bool
ResetSw bool
}
func EncodeFeedbackRequest() can.Frame {
2021-01-31 16:56:33 +01:00
return can.Frame{
ID: uint32(C.can_msg_feedback_status) | can.MaskRtr,
}
}
func DecodeFeedbackStatus(frame can.Frame) (FeedbackStatus, error) {
var ret FeedbackStatus
2021-01-31 16:56:33 +01:00
if frame.ID != uint32(C.can_msg_feedback_status) {
return ret, InvalidMessageID
}
if frame.Length != 2 {
return ret, InvalidMessageLength
}
2021-01-31 19:35:58 +01:00
ret.EndD = frame.Data[int(C.can_msg_feedback_status_field_end_d)] & uint8(C.can_msg_feedback_status_bit_end_d) != 0
ret.EndE = frame.Data[int(C.can_msg_feedback_status_field_end_e)] & uint8(C.can_msg_feedback_status_bit_end_e) != 0
ret.EndF = frame.Data[int(C.can_msg_feedback_status_field_end_f)] & uint8(C.can_msg_feedback_status_bit_end_f) != 0
ret.EndG = frame.Data[int(C.can_msg_feedback_status_field_end_g)] & uint8(C.can_msg_feedback_status_bit_end_g) != 0
ret.EndH = frame.Data[int(C.can_msg_feedback_status_field_end_h)] & uint8(C.can_msg_feedback_status_bit_end_h) != 0
ret.EmptyD = frame.Data[int(C.can_msg_feedback_status_field_empty_d)] & uint8(C.can_msg_feedback_status_bit_empty_d) != 0
ret.EmptyE = frame.Data[int(C.can_msg_feedback_status_field_empty_e)] & uint8(C.can_msg_feedback_status_bit_empty_e) != 0
ret.EmptyF = frame.Data[int(C.can_msg_feedback_status_field_empty_f)] & uint8(C.can_msg_feedback_status_bit_empty_f) != 0
ret.EmptyG = frame.Data[int(C.can_msg_feedback_status_field_empty_g)] & uint8(C.can_msg_feedback_status_bit_empty_g) != 0
ret.EmptyH = frame.Data[int(C.can_msg_feedback_status_field_empty_h)] & uint8(C.can_msg_feedback_status_bit_empty_h) != 0
ret.ResetSw = frame.Data[int(C.can_msg_feedback_status_field_reset_sw)] & uint8(C.can_msg_feedback_status_bit_reset_sw) != 0
2021-01-31 16:56:33 +01:00
return ret, nil
}
func (f FeedbackStatus) Encode() can.Frame {
var data [8]uint8
2021-01-31 16:56:33 +01:00
if f.EndD {
2021-01-31 19:35:58 +01:00
data[int(C.can_msg_feedback_status_field_end_d)] |= uint8(C.can_msg_feedback_status_bit_end_d)
2021-01-31 16:56:33 +01:00
}
if f.EndE {
2021-01-31 19:35:58 +01:00
data[int(C.can_msg_feedback_status_field_end_e)] |= uint8(C.can_msg_feedback_status_bit_end_e)
2021-01-31 16:56:33 +01:00
}
if f.EndF {
2021-01-31 19:35:58 +01:00
data[int(C.can_msg_feedback_status_field_end_f)] |= uint8(C.can_msg_feedback_status_bit_end_f)
2021-01-31 16:56:33 +01:00
}
if f.EndG {
2021-01-31 19:35:58 +01:00
data[int(C.can_msg_feedback_status_field_end_g)] |= uint8(C.can_msg_feedback_status_bit_end_g)
2021-01-31 16:56:33 +01:00
}
if f.EndH {
2021-01-31 19:35:58 +01:00
data[int(C.can_msg_feedback_status_field_end_h)] |= uint8(C.can_msg_feedback_status_bit_end_h)
2021-01-31 16:56:33 +01:00
}
if f.EmptyD {
2021-01-31 19:35:58 +01:00
data[int(C.can_msg_feedback_status_field_empty_d)] |= uint8(C.can_msg_feedback_status_bit_empty_d)
2021-01-31 16:56:33 +01:00
}
if f.EmptyE {
2021-01-31 19:35:58 +01:00
data[int(C.can_msg_feedback_status_field_empty_e)] |= uint8(C.can_msg_feedback_status_bit_empty_e)
2021-01-31 16:56:33 +01:00
}
if f.EmptyF {
2021-01-31 19:35:58 +01:00
data[int(C.can_msg_feedback_status_field_empty_f)] |= uint8(C.can_msg_feedback_status_bit_empty_f)
2021-01-31 16:56:33 +01:00
}
if f.EmptyG {
2021-01-31 19:35:58 +01:00
data[int(C.can_msg_feedback_status_field_empty_g)] |= uint8(C.can_msg_feedback_status_bit_empty_g)
2021-01-31 16:56:33 +01:00
}
if f.EmptyH {
2021-01-31 19:35:58 +01:00
data[int(C.can_msg_feedback_status_field_empty_h)] |= uint8(C.can_msg_feedback_status_bit_empty_h)
2021-01-31 16:56:33 +01:00
}
if f.ResetSw {
2021-01-31 19:35:58 +01:00
data[int(C.can_msg_feedback_status_field_reset_sw)] |= uint8(C.can_msg_feedback_status_bit_reset_sw)
2021-01-31 16:56:33 +01:00
}
return can.Frame{
ID: uint32(C.can_msg_feedback_status),
Length: 2,
Data: data,
}
}
type PowerStatus struct {
2021-01-31 16:56:33 +01:00
Bits int
}
func EncodePowerRequest() can.Frame {
2021-01-31 16:56:33 +01:00
return can.Frame{
ID: uint32(C.can_msg_power_status) | can.MaskRtr,
}
}
func DecodePowerStatus(frame can.Frame) (PowerStatus, error) {
var ret PowerStatus
2021-01-31 16:56:33 +01:00
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 PowerStatus) Encode() can.Frame {
var data [8]uint8
2021-01-31 16:56:33 +01:00
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 DispenseCommand struct {
2021-01-31 16:56:33 +01:00
Slot int
}
func DecodeDispenseCommand(frame can.Frame) (DispenseCommand, error) {
var ret DispenseCommand
2021-01-31 16:56:33 +01:00
if frame.ID != uint32(C.can_msg_power_dispense) {
return ret, InvalidMessageID
}
if frame.Length != 1 {
return ret, InvalidMessageLength
}
switch frame.Data[0] {
2021-01-31 19:35:58 +01:00
case uint8(C.can_msg_power_dispense_off):
2021-01-31 16:56:33 +01:00
ret.Slot = DispenseSlotOff
2021-01-31 19:35:58 +01:00
case uint8(C.can_msg_power_dispense_slot1):
2021-01-31 16:56:33 +01:00
ret.Slot = 0
2021-01-31 19:35:58 +01:00
case uint8(C.can_msg_power_dispense_slot2):
2021-01-31 16:56:33 +01:00
ret.Slot = 1
2021-01-31 19:35:58 +01:00
case uint8(C.can_msg_power_dispense_slot3):
2021-01-31 16:56:33 +01:00
ret.Slot = 2
2021-01-31 19:35:58 +01:00
case uint8(C.can_msg_power_dispense_slot4):
2021-01-31 16:56:33 +01:00
ret.Slot = 3
2021-01-31 19:35:58 +01:00
case uint8(C.can_msg_power_dispense_slot5):
2021-01-31 16:56:33 +01:00
ret.Slot = 4
}
return ret, nil
}
func (f DispenseCommand) Encode() can.Frame {
var data [8]uint8
2021-01-31 16:56:33 +01:00
switch f.Slot {
case DispenseSlotOff:
2021-01-31 19:35:58 +01:00
data[0] = uint8(C.can_msg_power_dispense_off)
2021-01-31 16:56:33 +01:00
case 0:
2021-01-31 19:35:58 +01:00
data[0] = uint8(C.can_msg_power_dispense_slot1)
2021-01-31 16:56:33 +01:00
case 1:
2021-01-31 19:35:58 +01:00
data[0] = uint8(C.can_msg_power_dispense_slot2)
2021-01-31 16:56:33 +01:00
case 2:
2021-01-31 19:35:58 +01:00
data[0] = uint8(C.can_msg_power_dispense_slot3)
2021-01-31 16:56:33 +01:00
case 3:
2021-01-31 19:35:58 +01:00
data[0] = uint8(C.can_msg_power_dispense_slot4)
2021-01-31 16:56:33 +01:00
case 4:
2021-01-31 19:35:58 +01:00
data[0] = uint8(C.can_msg_power_dispense_slot5)
2021-01-31 16:56:33 +01:00
}
return can.Frame{
ID: uint32(C.can_msg_power_dispense),
Length: 1,
Data: data,
}
}
type DisplayStatus struct {
Buttons []bool
}
func EncodeDisplayRequest() can.Frame {
return can.Frame{
ID: uint32(C.can_msg_display_status) | can.MaskRtr,
}
}
func DecodeDisplayStatus(frame can.Frame) (DisplayStatus, error) {
var ret DisplayStatus
if frame.ID != uint32(C.can_msg_display_status) {
return ret, InvalidMessageID
}
if frame.Length != 1 {
return ret, InvalidMessageLength
}
ret.Buttons = make([]bool, 8)
for i := uint(0); i < 8; i++ {
ret.Buttons[i] = frame.Data[0] & (1 << i) != 0;
}
return ret, nil
}
func (f DisplayStatus) Encode() can.Frame {
var data [8]uint8
for i := uint(0); i < 8; i++ {
if f.Buttons[i] {
data[0] |= (1 << i)
}
}
return can.Frame{
ID: uint32(C.can_msg_display_status),
Length: 1,
Data: data,
}
}
type DisplayCommand struct {
Command int
Data []byte
}
func DecodeDisplayCommand(frame can.Frame) (DisplayCommand, error) {
var ret DisplayCommand
if frame.ID != uint32(C.can_msg_display_cmd) {
return ret, InvalidMessageID
}
if frame.Length < 1 {
return ret, InvalidMessageLength
}
ret.Command = int(frame.Data[0])
ret.Data = make([]byte, frame.Length - 1)
copy(ret.Data, frame.Data[1:frame.Length-1])
return ret, nil
}
func (f DisplayCommand) Encode() can.Frame {
data := [8]uint8{}
data[0] = uint8(f.Command)
length := len(f.Data)
if length > 7 {
length = 7
}
copy(data[1:], f.Data[:length])
return can.Frame{
ID: uint32(C.can_msg_display_cmd),
2021-05-05 23:16:42 +02:00
Length: uint8(length + 1),
Data: data,
}
}
type AutoCommand struct {
2021-01-31 16:56:33 +01:00
AutoUpdate bool
}
func DecodeAutoCommand(frame can.Frame) (AutoCommand, error) {
var ret AutoCommand
2021-01-31 16:56:33 +01:00
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"
2021-01-31 19:35:58 +01:00
if frame.Data[0] != uint8(C.can_msg_auto_status_disable) {
2021-01-31 16:56:33 +01:00
ret.AutoUpdate = true
}
return ret, nil
}
func (f AutoCommand) Encode() can.Frame {
2021-01-31 16:56:33 +01:00
data := [8]uint8{}
if f.AutoUpdate {
2021-01-31 19:35:58 +01:00
data[0] = uint8(C.can_msg_auto_status_enable)
2021-01-31 16:56:33 +01:00
} else {
2021-01-31 19:35:58 +01:00
data[0] = uint8(C.can_msg_auto_status_disable)
2021-01-31 16:56:33 +01:00
}
return can.Frame{
ID: uint32(C.can_msg_auto_status),
Length: 1,
Data: data,
}
}