package uncanny /* // include our message format definitions #cgo CPPFLAGS: -I${SRCDIR}/.. #include // 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, } }