1use crate::types::NodeId;
34use crate::{Error, Result};
35
36pub const EMCY_COB_BASE: u16 = 0x080;
38
39pub const fn emcy_cob_id(node: NodeId) -> u16 {
41 EMCY_COB_BASE + node.raw() as u16
42}
43
44pub type EmcyPayload = [u8; 8];
46
47pub mod error_code {
50 pub const ERROR_RESET: u16 = 0x0000;
52 pub const GENERIC: u16 = 0x1000;
54 pub const CURRENT: u16 = 0x2000;
56 pub const VOLTAGE: u16 = 0x3000;
58 pub const TEMPERATURE: u16 = 0x4000;
60 pub const DEVICE_HARDWARE: u16 = 0x5000;
62 pub const DEVICE_SOFTWARE: u16 = 0x6000;
64 pub const ADDITIONAL_MODULES: u16 = 0x7000;
66 pub const MONITORING: u16 = 0x8000;
68 pub const COMMUNICATION: u16 = 0x8100;
70 pub const EXTERNAL: u16 = 0x9000;
72 pub const ADDITIONAL_FUNCTIONS: u16 = 0xF000;
74 pub const DEVICE_SPECIFIC: u16 = 0xFF00;
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
81pub struct ErrorRegister(pub u8);
82
83impl ErrorRegister {
84 pub const GENERIC: u8 = 0x01;
86 pub const CURRENT: u8 = 0x02;
88 pub const VOLTAGE: u8 = 0x04;
90 pub const TEMPERATURE: u8 = 0x08;
92 pub const COMMUNICATION: u8 = 0x10;
94 pub const DEVICE_PROFILE: u8 = 0x20;
96 pub const MANUFACTURER: u8 = 0x80;
98
99 pub const NONE: ErrorRegister = ErrorRegister(0);
101
102 pub const fn contains(self, bits: u8) -> bool {
104 self.0 & bits == bits
105 }
106
107 pub const fn has_error(self) -> bool {
109 self.0 != 0
110 }
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq)]
115pub struct EmergencyMessage {
116 pub error_code: u16,
118 pub error_register: ErrorRegister,
120 pub vendor_specific: [u8; 5],
122}
123
124impl EmergencyMessage {
125 pub const fn new(
127 error_code: u16,
128 error_register: ErrorRegister,
129 vendor_specific: [u8; 5],
130 ) -> Self {
131 Self {
132 error_code,
133 error_register,
134 vendor_specific,
135 }
136 }
137
138 pub const fn error_reset() -> Self {
140 Self {
141 error_code: error_code::ERROR_RESET,
142 error_register: ErrorRegister::NONE,
143 vendor_specific: [0; 5],
144 }
145 }
146
147 pub const fn is_error_reset(&self) -> bool {
149 self.error_code == error_code::ERROR_RESET
150 }
151
152 pub fn encode(&self) -> EmcyPayload {
154 let mut p = [0u8; 8];
155 p[0..2].copy_from_slice(&self.error_code.to_le_bytes());
156 p[2] = self.error_register.0;
157 p[3..8].copy_from_slice(&self.vendor_specific);
158 p
159 }
160
161 pub fn decode(data: &[u8]) -> Result<Self> {
165 if data.len() != 8 {
166 return Err(Error::BadLength);
167 }
168 let mut vendor_specific = [0u8; 5];
169 vendor_specific.copy_from_slice(&data[3..8]);
170 Ok(Self {
171 error_code: u16::from_le_bytes([data[0], data[1]]),
172 error_register: ErrorRegister(data[2]),
173 vendor_specific,
174 })
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181
182 #[test]
183 fn cob_id_follows_convention() {
184 assert_eq!(emcy_cob_id(NodeId::new(0x05).unwrap()), 0x085);
185 }
186
187 #[test]
191 fn encode_matches_known_frame() {
192 let msg = EmergencyMessage::new(
193 0x3210,
194 ErrorRegister(ErrorRegister::GENERIC | ErrorRegister::VOLTAGE),
195 [0xAA, 0xBB, 0xCC, 0xDD, 0xEE],
196 );
197 assert_eq!(
198 msg.encode(),
199 [0x10, 0x32, 0x05, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE]
200 );
201 }
202
203 #[test]
204 fn decode_matches_known_frame() {
205 let msg =
206 EmergencyMessage::decode(&[0x10, 0x32, 0x05, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE]).unwrap();
207 assert_eq!(msg.error_code, 0x3210);
208 assert!(msg.error_register.contains(ErrorRegister::VOLTAGE));
209 assert!(msg.error_register.has_error());
210 assert_eq!(msg.vendor_specific, [0xAA, 0xBB, 0xCC, 0xDD, 0xEE]);
211 }
212
213 #[test]
214 fn encode_decode_roundtrip() {
215 let msg = EmergencyMessage::new(
216 error_code::COMMUNICATION,
217 ErrorRegister(ErrorRegister::GENERIC | ErrorRegister::COMMUNICATION),
218 [1, 2, 3, 4, 5],
219 );
220 assert_eq!(EmergencyMessage::decode(&msg.encode()).unwrap(), msg);
221 }
222
223 #[test]
224 fn error_reset_is_recognised() {
225 let reset = EmergencyMessage::error_reset();
226 assert!(reset.is_error_reset());
227 assert!(!reset.error_register.has_error());
228 assert_eq!(reset.encode(), [0; 8]);
229 }
230
231 #[test]
232 fn decode_rejects_wrong_length() {
233 assert_eq!(EmergencyMessage::decode(&[0; 7]), Err(Error::BadLength));
234 }
235}