mbus_core/function_codes/public.rs
1//! # Modbus Public Function Codes and Sub-functions
2//!
3//! This module defines the standard function codes and sub-function codes used in the
4//! Modbus Application Protocol. It provides enums for:
5//!
6//! - **[`FunctionCode`]**: The primary operation identifier (e.g., Read Coils, Write Register).
7//! - **[`DiagnosticSubFunction`]**: Sub-codes for serial-line diagnostics (FC 0x08).
8//! - **[`EncapsulatedInterfaceType`]**: MEI types for tunneling other protocols (FC 0x2B).
9//!
10//! All types implement `TryFrom` for safe conversion from raw bytes and include
11//! documentation referencing the Modbus Application Protocol Specification V1.1b3.
12//!
13//! This module is `no_std` compatible and uses `repr` attributes to ensure
14//! memory layout matches the protocol's byte-level requirements.
15
16use crate::errors::{ExceptionCode, MbusError};
17
18/// Modbus Public Function Codes.
19///
20/// These are the standardized function codes defined in
21/// the Modbus Application Protocol Specification V1.1b3.
22///
23/// See:
24/// - Section 5.1 Public Function Code Definition
25/// - Section 6.x for individual function behaviors
26///
27/// Reference: :contentReference[oaicite:1]{index=1}
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
29#[repr(u8)]
30pub enum FunctionCode {
31 // ============================================================
32 // Bit Access (Single-bit data)
33 // ============================================================
34 /// 0x00 — Undefined
35 /// This value is not defined in the specification and can be used as a placeholder
36 /// for uninitialized or unknown function codes.
37 /// It is not a valid function code for actual Modbus transactions.
38 #[default]
39 Default = 0x00, // Placeholder for uninitialized or unknown function code
40
41 #[cfg(feature = "coils")]
42 /// 0x01 — Read Coils
43 ///
44 /// Reads the ON/OFF status of discrete output coils.
45 /// Section 6.1
46 ReadCoils = 0x01,
47
48 #[cfg(feature = "discrete-inputs")]
49 /// 0x02 — Read Discrete Inputs
50 ///
51 /// Reads the ON/OFF status of discrete input contacts.
52 /// Section 6.2
53 ReadDiscreteInputs = 0x02,
54
55 #[cfg(feature = "coils")]
56 /// 0x05 — Write Single Coil
57 ///
58 /// Forces a single coil to ON (0xFF00) or OFF (0x0000).
59 /// Section 6.5
60 WriteSingleCoil = 0x05,
61
62 #[cfg(feature = "coils")]
63 /// 0x0F — Write Multiple Coils
64 ///
65 /// Forces multiple coils to ON/OFF.
66 /// Section 6.11
67 WriteMultipleCoils = 0x0F,
68
69 // ============================================================
70 // 16-bit Register Access
71 // ============================================================
72 #[cfg(feature = "registers")]
73 /// 0x03 — Read Holding Registers
74 ///
75 /// Reads one or more 16-bit holding registers.
76 /// Section 6.3
77 ReadHoldingRegisters = 0x03,
78
79 #[cfg(feature = "registers")]
80 /// 0x04 — Read Input Registers
81 ///
82 /// Reads one or more 16-bit input registers.
83 /// Section 6.4
84 ReadInputRegisters = 0x04,
85
86 /// 0x06 — Write Single Register
87 #[cfg(feature = "registers")]
88 ///
89 /// Writes a single 16-bit holding register.
90 /// Section 6.6
91 WriteSingleRegister = 0x06,
92
93 #[cfg(feature = "registers")]
94 /// 0x10 — Write Multiple Registers
95 ///
96 /// Writes multiple 16-bit holding registers.
97 /// Section 6.12
98 WriteMultipleRegisters = 0x10,
99
100 #[cfg(feature = "registers")]
101 /// 0x16 — Mask Write Register
102 ///
103 /// Performs a bitwise mask write on a single register.
104 /// Section 6.16
105 MaskWriteRegister = 0x16,
106
107 #[cfg(feature = "registers")]
108 /// 0x17 — Read/Write Multiple Registers
109 ///
110 /// Reads and writes multiple registers in a single transaction.
111 /// Section 6.17
112 ReadWriteMultipleRegisters = 0x17,
113
114 #[cfg(feature = "fifo")]
115 /// 0x18 — Read FIFO Queue
116 ///
117 /// Reads the contents of a FIFO queue.
118 /// Section 6.18
119 ReadFifoQueue = 0x18,
120
121 // ============================================================
122 // File Record Access
123 // ============================================================
124 #[cfg(feature = "file-record")]
125 /// 0x14 — Read File Record
126 ///
127 /// Reads structured file records.
128 /// Section 6.14
129 ReadFileRecord = 0x14,
130
131 /// 0x15 — Write File Record
132 #[cfg(feature = "file-record")]
133 ///
134 /// Writes structured file records.
135 /// Section 6.15
136 WriteFileRecord = 0x15,
137
138 // ============================================================
139 // Diagnostics & Device Information
140 // ============================================================
141 #[cfg(feature = "diagnostics")]
142 /// 0x07 — Read Exception Status (Serial Line Only)
143 ///
144 /// Returns 8-bit exception status.
145 /// Section 6.7
146 ReadExceptionStatus = 0x07,
147
148 #[cfg(feature = "diagnostics")]
149 /// 0x08 — Diagnostics (Serial Line Only)
150 ///
151 /// Provides diagnostic and loopback tests.
152 /// Requires sub-function codes.
153 /// Section 6.8
154 Diagnostics = 0x08,
155
156 #[cfg(feature = "diagnostics")]
157 /// 0x0B — Get Communication Event Counter (Serial Line Only)
158 ///
159 /// Returns communication event counter.
160 /// Section 6.9
161 GetCommEventCounter = 0x0B,
162
163 #[cfg(feature = "diagnostics")]
164 /// 0x0C — Get Communication Event Log (Serial Line Only)
165 ///
166 /// Returns communication event log.
167 /// Section 6.10
168 GetCommEventLog = 0x0C,
169
170 #[cfg(feature = "diagnostics")]
171 /// 0x11 — Report Server ID (Serial Line Only)
172 ///
173 /// Returns server identification.
174 /// Section 6.13
175 ReportServerId = 0x11,
176
177 #[cfg(feature = "diagnostics")]
178 /// 0x2B — Encapsulated Interface Transport
179 ///
180 /// Used for:
181 /// - CANopen General Reference (Sub-function 0x0D)
182 /// - Read Device Identification (Sub-function 0x0E)
183 ///
184 /// Section 6.19, 6.20, 6.21
185 EncapsulatedInterfaceTransport = 0x2B,
186
187 // ============================================================
188 // Exception Responses (0x80 bit set)
189 // ============================================================
190 /// 0x81 — Exception Response for Read Coils (0x01 | 0x80)
191 #[cfg(feature = "coils")]
192 ReadCoilsException = 0x81,
193
194 /// 0x82 — Exception Response for Read Discrete Inputs (0x02 | 0x80)
195 #[cfg(feature = "discrete-inputs")]
196 ReadDiscreteInputsException = 0x82,
197
198 /// 0x83 — Exception Response for Read Holding Registers (0x03 | 0x80)
199 #[cfg(feature = "registers")]
200 ReadHoldingRegistersException = 0x83,
201
202 /// 0x84 — Exception Response for Read Input Registers (0x04 | 0x80)
203 #[cfg(feature = "registers")]
204 ReadInputRegistersException = 0x84,
205
206 /// 0x85 — Exception Response for Write Single Coil (0x05 | 0x80)
207 #[cfg(feature = "coils")]
208 WriteSingleCoilException = 0x85,
209
210 /// 0x86 — Exception Response for Write Single Register (0x06 | 0x80)
211 #[cfg(feature = "registers")]
212 WriteSingleRegisterException = 0x86,
213
214 /// 0x87 — Exception Response for Read Exception Status (0x07 | 0x80)
215 #[cfg(feature = "diagnostics")]
216 ReadExceptionStatusException = 0x87,
217
218 /// 0x88 — Exception Response for Diagnostics (0x08 | 0x80)
219 #[cfg(feature = "diagnostics")]
220 DiagnosticsException = 0x88,
221
222 /// 0x8B — Exception Response for Get Communication Event Counter (0x0B | 0x80)
223 #[cfg(feature = "diagnostics")]
224 GetCommEventCounterException = 0x8B,
225
226 /// 0x8C — Exception Response for Get Communication Event Log (0x0C | 0x80)
227 #[cfg(feature = "diagnostics")]
228 GetCommEventLogException = 0x8C,
229
230 /// 0x8F — Exception Response for Write Multiple Coils (0x0F | 0x80)
231 #[cfg(feature = "coils")]
232 WriteMultipleCoilsException = 0x8F,
233
234 /// 0x90 — Exception Response for Write Multiple Registers (0x10 | 0x80)
235 #[cfg(feature = "registers")]
236 WriteMultipleRegistersException = 0x90,
237
238 /// 0x91 — Exception Response for Report Server ID (0x11 | 0x80)
239 #[cfg(feature = "diagnostics")]
240 ReportServerIdException = 0x91,
241
242 /// 0x94 — Exception Response for Read File Record (0x14 | 0x80)
243 #[cfg(feature = "file-record")]
244 ReadFileRecordException = 0x94,
245
246 /// 0x95 — Exception Response for Write File Record (0x15 | 0x80)
247 #[cfg(feature = "file-record")]
248 WriteFileRecordException = 0x95,
249
250 /// 0x96 — Exception Response for Mask Write Register (0x16 | 0x80)
251 #[cfg(feature = "registers")]
252 MaskWriteRegisterException = 0x96,
253
254 /// 0x97 — Exception Response for Read/Write Multiple Registers (0x17 | 0x80)
255 #[cfg(feature = "registers")]
256 ReadWriteMultipleRegistersException = 0x97,
257
258 /// 0x98 — Exception Response for Read FIFO Queue (0x18 | 0x80)
259 #[cfg(feature = "fifo")]
260 ReadFifoQueueException = 0x98,
261
262 /// 0xAB — Exception Response for Encapsulated Interface Transport (0x2B | 0x80)
263 #[cfg(feature = "diagnostics")]
264 EncapsulatedInterfaceTransportException = 0xAB,
265}
266
267impl TryFrom<u8> for FunctionCode {
268 type Error = MbusError;
269
270 fn try_from(value: u8) -> Result<Self, Self::Error> {
271 use FunctionCode::*;
272
273 match value {
274 #[cfg(feature = "coils")]
275 0x01 => Ok(ReadCoils),
276 #[cfg(feature = "discrete-inputs")]
277 0x02 => Ok(ReadDiscreteInputs),
278 #[cfg(feature = "registers")]
279 0x03 => Ok(ReadHoldingRegisters),
280 #[cfg(feature = "registers")]
281 0x04 => Ok(ReadInputRegisters),
282 #[cfg(feature = "coils")]
283 0x05 => Ok(WriteSingleCoil),
284 #[cfg(feature = "registers")]
285 0x06 => Ok(WriteSingleRegister),
286 #[cfg(feature = "diagnostics")]
287 0x07 => Ok(ReadExceptionStatus),
288 #[cfg(feature = "diagnostics")]
289 0x08 => Ok(Diagnostics),
290 #[cfg(feature = "diagnostics")]
291 0x0B => Ok(GetCommEventCounter),
292 #[cfg(feature = "diagnostics")]
293 0x0C => Ok(GetCommEventLog),
294 #[cfg(feature = "coils")]
295 0x0F => Ok(WriteMultipleCoils),
296 #[cfg(feature = "registers")]
297 0x10 => Ok(WriteMultipleRegisters),
298 #[cfg(feature = "diagnostics")]
299 0x11 => Ok(ReportServerId),
300 #[cfg(feature = "file-record")]
301 0x14 => Ok(ReadFileRecord),
302 #[cfg(feature = "file-record")]
303 0x15 => Ok(WriteFileRecord),
304 #[cfg(feature = "registers")]
305 0x16 => Ok(MaskWriteRegister),
306 #[cfg(feature = "registers")]
307 0x17 => Ok(ReadWriteMultipleRegisters),
308 #[cfg(feature = "fifo")]
309 0x18 => Ok(ReadFifoQueue),
310 #[cfg(feature = "diagnostics")]
311 0x2B => Ok(EncapsulatedInterfaceTransport),
312 // Exception responses (0x80 bit set)
313 #[cfg(feature = "coils")]
314 0x81 => Ok(ReadCoilsException),
315 #[cfg(feature = "discrete-inputs")]
316 0x82 => Ok(ReadDiscreteInputsException),
317 #[cfg(feature = "registers")]
318 0x83 => Ok(ReadHoldingRegistersException),
319 #[cfg(feature = "registers")]
320 0x84 => Ok(ReadInputRegistersException),
321 #[cfg(feature = "coils")]
322 0x85 => Ok(WriteSingleCoilException),
323 #[cfg(feature = "registers")]
324 0x86 => Ok(WriteSingleRegisterException),
325 #[cfg(feature = "diagnostics")]
326 0x87 => Ok(ReadExceptionStatusException),
327 #[cfg(feature = "diagnostics")]
328 0x88 => Ok(DiagnosticsException),
329 #[cfg(feature = "diagnostics")]
330 0x8B => Ok(GetCommEventCounterException),
331 #[cfg(feature = "diagnostics")]
332 0x8C => Ok(GetCommEventLogException),
333 #[cfg(feature = "coils")]
334 0x8F => Ok(WriteMultipleCoilsException),
335 #[cfg(feature = "registers")]
336 0x90 => Ok(WriteMultipleRegistersException),
337 #[cfg(feature = "diagnostics")]
338 0x91 => Ok(ReportServerIdException),
339 #[cfg(feature = "file-record")]
340 0x94 => Ok(ReadFileRecordException),
341 #[cfg(feature = "file-record")]
342 0x95 => Ok(WriteFileRecordException),
343 #[cfg(feature = "registers")]
344 0x96 => Ok(MaskWriteRegisterException),
345 #[cfg(feature = "registers")]
346 0x97 => Ok(ReadWriteMultipleRegistersException),
347 #[cfg(feature = "fifo")]
348 0x98 => Ok(ReadFifoQueueException),
349 #[cfg(feature = "diagnostics")]
350 0xAB => Ok(EncapsulatedInterfaceTransportException),
351 _ => Err(MbusError::UnsupportedFunction(value)),
352 }
353 }
354}
355
356impl FunctionCode {
357 /// Maps an application error to the corresponding Modbus exception code.
358 ///
359 /// This method determines the appropriate exception code to return based on the
360 /// error that occurred during request processing. For errors that don't map to
361 /// a specific exception code, `ServerDeviceFailure` is used as a default.
362 ///
363 /// # Arguments
364 /// * `error` - The error that occurred during processing
365 ///
366 /// # Returns
367 /// The Modbus exception code to send in the response
368 ///
369 /// # Example
370 /// ```ignore
371 /// let fc = FunctionCode::ReadHoldingRegisters;
372 /// let error = MbusError::InvalidAddress;
373 /// let exc_code = fc.exception_code_for_error(&error);
374 /// assert_eq!(exc_code, ExceptionCode::IllegalDataAddress);
375 /// ```
376 pub fn exception_code_for_error(&self, error: &MbusError) -> ExceptionCode {
377 match error {
378 // Protocol/address errors
379 MbusError::InvalidAddress | MbusError::InvalidOffset => {
380 ExceptionCode::IllegalDataAddress
381 }
382 // Data length and parsing errors — the data field itself is malformed
383 MbusError::InvalidDataLen
384 | MbusError::ParseError
385 | MbusError::BasicParseError
386 | MbusError::InvalidPduLength => ExceptionCode::IllegalDataAddress,
387 // Quantity/value errors
388 MbusError::InvalidQuantity
389 | MbusError::InvalidValue
390 | MbusError::InvalidByteCount
391 | MbusError::InvalidAndMask
392 | MbusError::InvalidOrMask
393 | MbusError::InvalidDeviceIdCode => ExceptionCode::IllegalDataValue,
394 // Function code errors — also includes illegal sub-function / MEI types
395 MbusError::InvalidFunctionCode
396 | MbusError::UnsupportedFunction(_)
397 | MbusError::ReservedSubFunction(_)
398 | MbusError::InvalidMeiType
399 | MbusError::BroadcastNotAllowed
400 | MbusError::InvalidBroadcastAddress => ExceptionCode::IllegalFunction,
401 // Default: all other errors map to server device failure
402 _ => ExceptionCode::ServerDeviceFailure,
403 }
404 }
405
406 /// Returns the exception function code variant (with 0x80 bit set) for this function code.
407 ///
408 /// Exception responses use function codes with the high bit (0x80) set to indicate
409 /// that an exception occurred. This method maps normal function codes to their
410 /// exception equivalents.
411 ///
412 /// # Returns
413 /// The exception function code variant, or `None` if this is not a valid function code
414 /// that can have exceptions.
415 ///
416 /// # Example
417 /// ```ignore
418 /// let fc = FunctionCode::ReadHoldingRegisters;
419 /// let exc_fc = fc.exception_response();
420 /// assert_eq!(exc_fc, Some(FunctionCode::ReadHoldingRegistersException));
421 /// ```
422 pub fn exception_response(&self) -> Option<FunctionCode> {
423 match self {
424 #[cfg(feature = "coils")]
425 FunctionCode::ReadCoils => Some(FunctionCode::ReadCoilsException),
426 #[cfg(feature = "discrete-inputs")]
427 FunctionCode::ReadDiscreteInputs => Some(FunctionCode::ReadDiscreteInputsException),
428 #[cfg(feature = "registers")]
429 FunctionCode::ReadHoldingRegisters => Some(FunctionCode::ReadHoldingRegistersException),
430 #[cfg(feature = "registers")]
431 FunctionCode::ReadInputRegisters => Some(FunctionCode::ReadInputRegistersException),
432 #[cfg(feature = "coils")]
433 FunctionCode::WriteSingleCoil => Some(FunctionCode::WriteSingleCoilException),
434 #[cfg(feature = "registers")]
435 FunctionCode::WriteSingleRegister => Some(FunctionCode::WriteSingleRegisterException),
436 #[cfg(feature = "diagnostics")]
437 FunctionCode::ReadExceptionStatus => Some(FunctionCode::ReadExceptionStatusException),
438 #[cfg(feature = "diagnostics")]
439 FunctionCode::Diagnostics => Some(FunctionCode::DiagnosticsException),
440 #[cfg(feature = "diagnostics")]
441 FunctionCode::GetCommEventCounter => Some(FunctionCode::GetCommEventCounterException),
442 #[cfg(feature = "diagnostics")]
443 FunctionCode::GetCommEventLog => Some(FunctionCode::GetCommEventLogException),
444 #[cfg(feature = "coils")]
445 FunctionCode::WriteMultipleCoils => Some(FunctionCode::WriteMultipleCoilsException),
446 #[cfg(feature = "registers")]
447 FunctionCode::WriteMultipleRegisters => {
448 Some(FunctionCode::WriteMultipleRegistersException)
449 }
450 #[cfg(feature = "diagnostics")]
451 FunctionCode::ReportServerId => Some(FunctionCode::ReportServerIdException),
452 #[cfg(feature = "file-record")]
453 FunctionCode::ReadFileRecord => Some(FunctionCode::ReadFileRecordException),
454 #[cfg(feature = "file-record")]
455 FunctionCode::WriteFileRecord => Some(FunctionCode::WriteFileRecordException),
456 #[cfg(feature = "registers")]
457 FunctionCode::MaskWriteRegister => Some(FunctionCode::MaskWriteRegisterException),
458 #[cfg(feature = "registers")]
459 FunctionCode::ReadWriteMultipleRegisters => {
460 Some(FunctionCode::ReadWriteMultipleRegistersException)
461 }
462 #[cfg(feature = "fifo")]
463 FunctionCode::ReadFifoQueue => Some(FunctionCode::ReadFifoQueueException),
464 #[cfg(feature = "diagnostics")]
465 FunctionCode::EncapsulatedInterfaceTransport => {
466 Some(FunctionCode::EncapsulatedInterfaceTransportException)
467 }
468 // Already exception codes or default
469 _ => None,
470 }
471 }
472}
473
474/// Sub-function codes for Function Code 0x08 (Diagnostics).
475///
476/// Serial line only.
477/// See Modbus Application Protocol Specification V1.1b3, Section 6.8.
478///
479/// These values are 16-bit and encoded big-endian inside the PDU data field.
480#[derive(Debug, Clone, Copy, PartialEq, Eq)]
481#[repr(u16)]
482pub enum DiagnosticSubFunction {
483 /// 0x0000 — Return Query Data (Loopback test)
484 ReturnQueryData = 0x0000,
485
486 /// 0x0001 — Restart Communications Option
487 RestartCommunicationsOption = 0x0001,
488
489 /// 0x0002 — Return Diagnostic Register
490 ReturnDiagnosticRegister = 0x0002,
491
492 /// 0x0003 — Change ASCII Input Delimiter
493 ChangeAsciiInputDelimiter = 0x0003,
494
495 /// 0x0004 — Force Listen Only Mode
496 ForceListenOnlyMode = 0x0004,
497
498 /// 0x000A — Clear Counters and Diagnostic Register
499 ClearCountersAndDiagnosticRegister = 0x000A,
500
501 /// 0x000B — Return Bus Message Count
502 ReturnBusMessageCount = 0x000B,
503
504 /// 0x000C — Return Bus Communication Error Count
505 ReturnBusCommunicationErrorCount = 0x000C,
506
507 /// 0x000D — Return Bus Exception Error Count
508 ReturnBusExceptionErrorCount = 0x000D,
509
510 /// 0x000E — Return Server Message Count
511 ReturnServerMessageCount = 0x000E,
512
513 /// 0x000F — Return Server No Response Count
514 ReturnServerNoResponseCount = 0x000F,
515
516 /// 0x0010 — Return Server NAK Count
517 ReturnServerNakCount = 0x0010,
518
519 /// 0x0011 — Return Server Busy Count
520 ReturnServerBusyCount = 0x0011,
521
522 /// 0x0012 — Return Bus Character Overrun Count
523 ReturnBusCharacterOverrunCount = 0x0012,
524
525 /// 0x0014 — Clear Overrun Counter and Flag
526 ClearOverrunCounterAndFlag = 0x0014,
527}
528
529impl DiagnosticSubFunction {
530 /// Converts the `DiagnosticSubFunction` enum variant into its 2-byte big-endian representation.
531 pub fn to_be_bytes(self) -> [u8; 2] {
532 (self as u16).to_be_bytes()
533 }
534}
535
536impl From<DiagnosticSubFunction> for u16 {
537 fn from(sub_func: DiagnosticSubFunction) -> Self {
538 sub_func as u16
539 }
540}
541
542impl TryFrom<u16> for DiagnosticSubFunction {
543 type Error = MbusError;
544
545 fn try_from(value: u16) -> Result<Self, Self::Error> {
546 use DiagnosticSubFunction::*;
547
548 match value {
549 0x0000 => Ok(ReturnQueryData),
550 0x0001 => Ok(RestartCommunicationsOption),
551 0x0002 => Ok(ReturnDiagnosticRegister),
552 0x0003 => Ok(ChangeAsciiInputDelimiter),
553 0x0004 => Ok(ForceListenOnlyMode),
554
555 // 0x0005–0x0009 Reserved
556 0x000A => Ok(ClearCountersAndDiagnosticRegister),
557 0x000B => Ok(ReturnBusMessageCount),
558 0x000C => Ok(ReturnBusCommunicationErrorCount),
559 0x000D => Ok(ReturnBusExceptionErrorCount),
560 0x000E => Ok(ReturnServerMessageCount),
561 0x000F => Ok(ReturnServerNoResponseCount),
562 0x0010 => Ok(ReturnServerNakCount),
563 0x0011 => Ok(ReturnServerBusyCount),
564 0x0012 => Ok(ReturnBusCharacterOverrunCount),
565
566 // 0x0013 Reserved
567 0x0014 => Ok(ClearOverrunCounterAndFlag),
568
569 // Everything else reserved per spec
570 _ => Err(MbusError::ReservedSubFunction(value)),
571 }
572 }
573}
574
575/// MEI (Modbus Encapsulated Interface) types
576/// for Function Code 0x2B.
577///
578/// See Section 6.19–6.21 of the specification.
579///
580/// Encoded as 1 byte following the function code.
581#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
582#[repr(u8)]
583pub enum EncapsulatedInterfaceType {
584 /// Placeholder default value used before a concrete MEI type is parsed.
585 /// This value should not appear in a valid decoded protocol frame.
586 #[default]
587 Err,
588 /// 0x0D — CANopen General Reference
589 CanopenGeneralReference = 0x0D,
590
591 /// 0x0E — Read Device Identification
592 ReadDeviceIdentification = 0x0E,
593}
594
595impl From<EncapsulatedInterfaceType> for u8 {
596 fn from(val: EncapsulatedInterfaceType) -> Self {
597 val as u8
598 }
599}
600
601impl TryFrom<u8> for EncapsulatedInterfaceType {
602 type Error = MbusError;
603
604 fn try_from(value: u8) -> Result<Self, Self::Error> {
605 match value {
606 0x0D => Ok(Self::CanopenGeneralReference),
607 0x0E => Ok(Self::ReadDeviceIdentification),
608 _ => Err(MbusError::ReservedSubFunction(value as u16)),
609 }
610 }
611}