1use crate::codec::{decode_raw, mask_for};
10use crate::desc::{AddressSpace, PointDesc};
11use crate::error::Error;
12use crate::transport::Transport;
13use crate::value::Value;
14
15pub async fn read_regs<T: Transport, const N: usize>(
18 t: &mut T,
19 space: AddressSpace,
20 off: u16,
21) -> Result<[u16; N], Error<T::Error>> {
22 let mut regs = [0u16; N];
23 match space {
24 AddressSpace::HoldingRegister => t
25 .read_holding(off, &mut regs)
26 .await
27 .map_err(Error::Transport)?,
28 AddressSpace::InputRegister => t
29 .read_input(off, &mut regs)
30 .await
31 .map_err(Error::Transport)?,
32 AddressSpace::Coil => {
33 let mut bits = [false];
34 t.read_coils(off, &mut bits)
35 .await
36 .map_err(Error::Transport)?;
37 regs[0] = bits[0] as u16;
38 }
39 AddressSpace::DiscreteInput => {
40 let mut bits = [false];
41 t.read_discrete(off, &mut bits)
42 .await
43 .map_err(Error::Transport)?;
44 regs[0] = bits[0] as u16;
45 }
46 }
47 Ok(regs)
48}
49
50pub async fn write_regs<T: Transport>(
52 t: &mut T,
53 space: AddressSpace,
54 off: u16,
55 regs: &[u16],
56) -> Result<(), Error<T::Error>> {
57 match space {
58 AddressSpace::HoldingRegister => t.write_holding(off, regs).await.map_err(Error::Transport),
59 AddressSpace::Coil => t
60 .write_coil(off, regs.first().is_some_and(|r| *r != 0))
61 .await
62 .map_err(Error::Transport),
63 _ => Err(Error::UnsupportedMapping("cannot write this address space")),
64 }
65}
66
67pub fn na_meaning(p: &PointDesc<'static>, regs: &[u16]) -> &'static str {
69 let Ok((raw, bits)) = decode_raw(p, regs) else {
70 return "";
71 };
72 p.na.iter()
73 .find(|na| (na.raw as u64) & mask_for(bits) == raw)
74 .map(|na| na.meaning)
75 .unwrap_or("")
76}
77
78const WRONG_KIND: &str = "decoded value kind does not match the generated signature";
79
80pub fn value_f64<E>(v: Value) -> Result<f64, Error<E>> {
81 v.as_f64().ok_or(Error::UnsupportedMapping(WRONG_KIND))
82}
83
84pub fn value_bool<E>(v: Value) -> Result<bool, Error<E>> {
85 match v {
86 Value::Bool(b) => Ok(b),
87 _ => Err(Error::UnsupportedMapping(WRONG_KIND)),
88 }
89}
90
91pub fn value_u64<E>(v: Value) -> Result<u64, Error<E>> {
92 match v {
93 Value::U64(x) => Ok(x),
94 _ => Err(Error::UnsupportedMapping(WRONG_KIND)),
95 }
96}
97
98pub fn value_i64<E>(v: Value) -> Result<i64, Error<E>> {
99 match v {
100 Value::I64(x) => Ok(x),
101 Value::U64(x) => i64::try_from(x).map_err(|_| Error::UnsupportedMapping(WRONG_KIND)),
102 _ => Err(Error::UnsupportedMapping(WRONG_KIND)),
103 }
104}
105
106pub fn value_flags<E>(v: Value) -> Result<u64, Error<E>> {
108 match v {
109 Value::Flags(m) => Ok(m),
110 _ => Err(Error::UnsupportedMapping(WRONG_KIND)),
111 }
112}
113
114pub fn value_fields<E>(v: Value) -> Result<u64, Error<E>> {
116 match v {
117 Value::Fields(w) => Ok(w),
118 _ => Err(Error::UnsupportedMapping(WRONG_KIND)),
119 }
120}
121
122pub fn value_datetime<E>(v: Value) -> Result<i64, Error<E>> {
124 match v {
125 Value::DateTime(t) => Ok(t),
126 _ => Err(Error::UnsupportedMapping(WRONG_KIND)),
127 }
128}
129
130pub fn ref_int<E>(v: Value) -> Result<i64, Error<E>> {
132 v.as_i64().ok_or(Error::UnsupportedMapping(WRONG_KIND))
133}
134
135const SUNS_MARKER: [u16; 2] = [0x5375, 0x6e53];
137
138pub async fn resolve_sunspec<T: Transport>(
142 t: &mut T,
143 space: AddressSpace,
144 anchors: &[u16],
145 model_id: u16,
146) -> Result<u16, Error<T::Error>> {
147 async fn header<T: Transport>(
148 t: &mut T,
149 space: AddressSpace,
150 off: u16,
151 ) -> Result<[u16; 2], T::Error> {
152 let mut hdr = [0u16; 2];
153 match space {
154 AddressSpace::InputRegister => t.read_input(off, &mut hdr).await?,
155 _ => t.read_holding(off, &mut hdr).await?,
156 }
157 Ok(hdr)
158 }
159
160 let mut anchor = None;
161 for &a in anchors {
162 if let Ok(hdr) = header(t, space, a).await {
164 if hdr == SUNS_MARKER {
165 anchor = Some(a);
166 break;
167 }
168 }
169 }
170 let Some(a) = anchor else {
171 return Err(Error::UnsupportedMapping("SunS marker not found"));
172 };
173
174 let mut off = a + 2;
175 for _ in 0..256 {
176 let hdr = header(t, space, off).await.map_err(Error::Transport)?;
177 if hdr[0] == 0xffff {
178 break;
179 }
180 if hdr[0] == model_id {
181 return Ok(off);
182 }
183 off = off
184 .checked_add(2 + hdr[1])
185 .ok_or(Error::UnsupportedMapping("SunSpec model chain overflows"))?;
186 }
187 Err(Error::UnsupportedMapping("SunSpec model not found"))
188}