package uncanny /* // Include our message format definitions #cgo CPPFLAGS: -I${SRCDIR}/.. #include #include // 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! 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; 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 MessageTypeFeedbackRequest MessageTypePowerStatus MessageTypePowerRequest MessageTypeDispenseCommand MessageTypeDisplayStatus MessageTypeDisplayRequest MessageTypeDisplayCommand MessageTypeAutoCommand ) // 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 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 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 case uint32(C.can_msg_auto_status): msg, err := DecodeAutoCommand(frame) return MessageTypeAutoCommand, msg, err } return MessageTypeInvalid, nil, UnsupportedMessageType } type FeedbackStatus struct { 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 { return can.Frame{ ID: uint32(C.can_msg_feedback_status) | can.MaskRtr, } } func DecodeFeedbackStatus(frame can.Frame) (FeedbackStatus, error) { var ret FeedbackStatus if frame.ID != uint32(C.can_msg_feedback_status) { return ret, InvalidMessageID } if frame.Length != 2 { return ret, InvalidMessageLength } 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 return ret, nil } func (f FeedbackStatus) Encode() can.Frame { var data [8]uint8 if f.EndD { data[int(C.can_msg_feedback_status_field_end_d)] |= uint8(C.can_msg_feedback_status_bit_end_d) } if f.EndE { data[int(C.can_msg_feedback_status_field_end_e)] |= uint8(C.can_msg_feedback_status_bit_end_e) } if f.EndF { data[int(C.can_msg_feedback_status_field_end_f)] |= uint8(C.can_msg_feedback_status_bit_end_f) } if f.EndG { data[int(C.can_msg_feedback_status_field_end_g)] |= uint8(C.can_msg_feedback_status_bit_end_g) } if f.EndH { data[int(C.can_msg_feedback_status_field_end_h)] |= uint8(C.can_msg_feedback_status_bit_end_h) } if f.EmptyD { data[int(C.can_msg_feedback_status_field_empty_d)] |= uint8(C.can_msg_feedback_status_bit_empty_d) } if f.EmptyE { data[int(C.can_msg_feedback_status_field_empty_e)] |= uint8(C.can_msg_feedback_status_bit_empty_e) } if f.EmptyF { data[int(C.can_msg_feedback_status_field_empty_f)] |= uint8(C.can_msg_feedback_status_bit_empty_f) } if f.EmptyG { data[int(C.can_msg_feedback_status_field_empty_g)] |= uint8(C.can_msg_feedback_status_bit_empty_g) } if f.EmptyH { data[int(C.can_msg_feedback_status_field_empty_h)] |= uint8(C.can_msg_feedback_status_bit_empty_h) } if f.ResetSw { data[int(C.can_msg_feedback_status_field_reset_sw)] |= uint8(C.can_msg_feedback_status_bit_reset_sw) } return can.Frame{ ID: uint32(C.can_msg_feedback_status), Length: 2, Data: data, } } type PowerStatus struct { Bits int } func EncodePowerRequest() can.Frame { return can.Frame{ ID: uint32(C.can_msg_power_status) | can.MaskRtr, } } func DecodePowerStatus(frame can.Frame) (PowerStatus, error) { var ret PowerStatus 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 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 { Slot int } func DecodeDispenseCommand(frame can.Frame) (DispenseCommand, error) { var ret DispenseCommand if frame.ID != uint32(C.can_msg_power_dispense) { return ret, InvalidMessageID } if frame.Length != 1 { return ret, InvalidMessageLength } switch frame.Data[0] { case uint8(C.can_msg_power_dispense_off): ret.Slot = DispenseSlotOff case uint8(C.can_msg_power_dispense_slot1): ret.Slot = 0 case uint8(C.can_msg_power_dispense_slot2): ret.Slot = 1 case uint8(C.can_msg_power_dispense_slot3): ret.Slot = 2 case uint8(C.can_msg_power_dispense_slot4): ret.Slot = 3 case uint8(C.can_msg_power_dispense_slot5): ret.Slot = 4 } return ret, nil } func (f DispenseCommand) Encode() can.Frame { var data [8]uint8 switch f.Slot { case DispenseSlotOff: data[0] = uint8(C.can_msg_power_dispense_off) case 0: data[0] = uint8(C.can_msg_power_dispense_slot1) case 1: data[0] = uint8(C.can_msg_power_dispense_slot2) case 2: data[0] = uint8(C.can_msg_power_dispense_slot3) case 3: data[0] = uint8(C.can_msg_power_dispense_slot4) case 4: data[0] = uint8(C.can_msg_power_dispense_slot5) } 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), Length: length + 1, Data: data, } } type AutoCommand struct { AutoUpdate bool } func DecodeAutoCommand(frame can.Frame) (AutoCommand, error) { var ret AutoCommand 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] != uint8(C.can_msg_auto_status_disable) { ret.AutoUpdate = true } return ret, nil } func (f AutoCommand) Encode() can.Frame { data := [8]uint8{} if f.AutoUpdate { data[0] = uint8(C.can_msg_auto_status_enable) } else { data[0] = uint8(C.can_msg_auto_status_disable) } return can.Frame{ ID: uint32(C.can_msg_auto_status), Length: 1, Data: data, } }