Skip to main content

moddef_core/
command.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Command (multi-step register procedure) support, spec §11.7. The executor
4//! itself is [`crate::device::Device::run_command`]; this module holds the
5//! pure pieces — poll-condition evaluation, the single-PDU write cap, the
6//! caller-facing param value type — and the [`Delay`] abstraction the poll
7//! loop is parameterized over ([`Transport`](crate::transport::Transport)
8//! has no time primitive, and none exists in the `alloc`-only tier).
9
10use crate::schema;
11use crate::value::Value;
12
13/// Modbus single-PDU practical write cap (FC16); larger writes chunk inside
14/// the executor. (Reads are chunked by transports per `max_read_words`.)
15pub const MAX_WRITE_WORDS: usize = 123;
16
17/// Default poll interval when a `PollStep` omits `interval_ms`.
18pub const DEFAULT_POLL_INTERVAL_MS: u32 = 250;
19
20/// Async sleep used by poll steps. `std`/tokio callers get an impl from
21/// `moddef-tokio-modbus`; embedded callers supply their own (e.g. an
22/// embassy timer). Elapsed poll time is accounted by accumulating the
23/// requested delays, so no wall clock is required.
24#[allow(async_fn_in_trait)]
25pub trait Delay {
26    async fn delay_ms(&mut self, ms: u32);
27}
28
29/// Caller-supplied value for a command param (numeric, string, or bytes —
30/// matching the split encode paths of the codec).
31#[derive(Clone, Copy, Debug)]
32pub enum ParamValue<'a> {
33    Value(Value),
34    Str(&'a str),
35    Bytes(&'a [u8]),
36}
37
38impl From<i64> for ParamValue<'_> {
39    fn from(v: i64) -> Self {
40        ParamValue::Value(Value::I64(v))
41    }
42}
43impl From<u64> for ParamValue<'_> {
44    fn from(v: u64) -> Self {
45        ParamValue::Value(Value::U64(v))
46    }
47}
48impl From<f64> for ParamValue<'_> {
49    fn from(v: f64) -> Self {
50        ParamValue::Value(Value::F64(v))
51    }
52}
53impl From<bool> for ParamValue<'_> {
54    fn from(v: bool) -> Self {
55        ParamValue::Value(Value::Bool(v))
56    }
57}
58impl<'a> From<&'a str> for ParamValue<'a> {
59    fn from(v: &'a str) -> Self {
60        ParamValue::Str(v)
61    }
62}
63impl<'a> From<&'a [u8]> for ParamValue<'a> {
64    fn from(v: &'a [u8]) -> Self {
65        ParamValue::Bytes(v)
66    }
67}
68
69/// Evaluate a §11.7 poll exit condition against a raw integer.
70pub fn condition_met(c: Option<&schema::Condition>, raw: i64) -> bool {
71    let Some(c) = c else { return false };
72    match c.op() {
73        schema::ConditionOp::Eq => raw == c.value,
74        schema::ConditionOp::Ne => raw != c.value,
75        schema::ConditionOp::Mask => raw & c.mask == c.value,
76        schema::ConditionOp::Range => c.min <= raw && raw <= c.max,
77        schema::ConditionOp::Unspecified => false,
78    }
79}