1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
use super::Device;
use crate::{ASCOMError, ASCOMResult};
use macro_rules_attribute::apply;
/// Switch Specific Methods.
#[apply(rpc_trait)]
pub trait Switch: Device + Send + Sync {
/// Returns the number of switch devices managed by this driver.
///
/// Devices are numbered from 0 to MaxSwitch - 1.
#[http("maxswitch", method = Get)]
async fn max_switch(&self) -> ASCOMResult<usize>;
/// This endpoint must be implemented and indicates whether the given switch can operate asynchronously.
///
/// _ISwitchV3 and later._
#[http("canasync", method = Get)]
async fn can_async(&self, #[http("Id")] id: usize) -> ASCOMResult<bool> {
Ok(false)
}
/// Reports if the specified switch device can be written to, default true.
///
/// This is false if the device cannot be written to, for example a limit switch or a sensor. Devices are numbered from 0 to MaxSwitch - 1.
#[http("canwrite", method = Get)]
async fn can_write(&self, #[http("Id")] id: usize) -> ASCOMResult<bool> {
Ok(false)
}
/// Return the state of switch device id as a boolean. Devices are numbered from 0 to MaxSwitch - 1.
#[http("getswitch", method = Get)]
async fn get_switch(&self, #[http("Id")] id: usize) -> ASCOMResult<bool>;
/// Gets the description of the specified switch device.
///
/// This is to allow a fuller description of the device to be returned, for example for a tool tip. Devices are numbered from 0 to MaxSwitch - 1.
#[http("getswitchdescription", method = Get)]
async fn get_switch_description(&self, #[http("Id")] id: usize) -> ASCOMResult<String>;
/// Gets the name of the specified switch device.
///
/// Devices are numbered from 0 to MaxSwitch - 1.
#[http("getswitchname", method = Get)]
async fn get_switch_name(&self, #[http("Id")] id: usize) -> ASCOMResult<String>;
/// Gets the value of the specified switch device as a double.
///
/// Devices are numbered from 0 to MaxSwitch - 1, The value of this switch is expected to be between MinSwitchValue and MaxSwitchValue.
#[http("getswitchvalue", method = Get)]
async fn get_switch_value(&self, #[http("Id")] id: usize) -> ASCOMResult<f64>;
/// Gets the minimum value of the specified switch device as a double.
///
/// Devices are numbered from 0 to MaxSwitch - 1.
#[http("minswitchvalue", method = Get)]
async fn min_switch_value(&self, #[http("Id")] id: usize) -> ASCOMResult<f64>;
/// Gets the maximum value of the specified switch device as a double.
///
/// Devices are numbered from 0 to MaxSwitch - 1.
#[http("maxswitchvalue", method = Get)]
async fn max_switch_value(&self, #[http("Id")] id: usize) -> ASCOMResult<f64>;
/// This is an asynchronous method that must return as soon as the state change operation has been successfully started, with StateChangeComplete(Int16) for the given switch Id = False. After the state change has completed StateChangeComplete(Int16) becomes True.
///
/// _ISwitchV3 and later._
#[http("setasync", method = Put)]
async fn set_async(
&self,
#[http("Id")] id: usize,
#[http("State")] state: bool,
) -> ASCOMResult<()> {
Err(ASCOMError::NOT_IMPLEMENTED)
}
/// This is an asynchronous method that must return as soon as the state change operation has been successfully started, with StateChangeComplete(Int16) for the given switch Id = False. After the state change has completed StateChangeComplete(Int16) becomes True.
///
/// _ISwitchV3 and later._
#[http("setasyncvalue", method = Put)]
async fn set_async_value(
&self,
#[http("Id")] id: usize,
#[http("Value")] value: f64,
) -> ASCOMResult<()> {
Err(ASCOMError::NOT_IMPLEMENTED)
}
/// Sets a switch controller device to the specified state, true or false.
#[http("setswitch", method = Put)]
async fn set_switch(
&self,
#[http("Id")] id: usize,
#[http("State")] state: bool,
) -> ASCOMResult<()> {
Err(ASCOMError::NOT_IMPLEMENTED)
}
/// Sets a switch device name to the specified value.
#[http("setswitchname", method = Put)]
async fn set_switch_name(
&self,
#[http("Id")] id: usize,
#[http("Name")] name: String,
) -> ASCOMResult<()> {
Err(ASCOMError::NOT_IMPLEMENTED)
}
/// Sets a switch device value to the specified value.
#[http("setswitchvalue", method = Put)]
async fn set_switch_value(
&self,
#[http("Id")] id: usize,
#[http("Value")] value: f64,
) -> ASCOMResult<()> {
Err(ASCOMError::NOT_IMPLEMENTED)
}
/// True if the state of the specified switch is changing, otherwise false.
///
/// _ISwitchV3 and later._
#[http("statechangecomplete", method = Get)]
async fn state_change_complete(&self, #[http("Id")] id: usize) -> ASCOMResult<bool>;
/// Returns the step size that this device supports (the difference between successive values of the device).
///
/// Devices are numbered from 0 to MaxSwitch - 1.
#[http("switchstep", method = Get)]
async fn switch_step(&self, #[http("Id")] id: usize) -> ASCOMResult<f64>;
/// This method returns the version of the ASCOM device interface contract to which this device complies.
///
/// Only one interface version is current at a moment in time and all new devices should be built to the latest interface version. Applications can choose which device interface versions they support and it is in their interest to support previous versions as well as the current version to ensure thay can use the largest number of devices.
#[http("interfaceversion", method = Get)]
async fn interface_version(&self) -> ASCOMResult<u16> {
Ok(3)
}
}
/// An object representing operational properties of a specific device connected to the switch.
#[derive(Default, Debug, Clone, Copy)]
pub struct SwitchDeviceState {
/// Result of [`Switch::get_switch`].
pub get_switch: Option<bool>,
/// Result of [`Switch::get_switch_value`].
pub get_switch_value: Option<f64>,
/// Result of [`Switch::state_change_complete`].
pub state_change_complete: Option<bool>,
}
impl SwitchDeviceState {
async fn new(switch: &(impl ?Sized + Switch), id: usize) -> Self {
Self {
get_switch: switch.get_switch(id).await.ok(),
get_switch_value: switch.get_switch_value(id).await.ok(),
state_change_complete: switch.state_change_complete(id).await.ok(),
}
}
}
/// An object representing all operational properties of the device.
#[derive(Default, Debug, Clone)]
pub struct DeviceState {
/// States of individual switch devices, indexed by their ID.
pub switch_devices: Vec<SwitchDeviceState>,
}
impl DeviceState {
async fn new(switch: &(impl ?Sized + Switch)) -> Self {
Self {
switch_devices: match switch.max_switch().await {
Ok(n) => {
futures::future::join_all((0..n).map(|id| SwitchDeviceState::new(switch, id)))
.await
}
Err(err) => {
tracing::error!(%err, "Failed to get max switch");
Vec::new()
}
},
}
}
}
#[cfg(feature = "server")]
impl serde::Serialize for DeviceState {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeMap;
let mut map = serializer.serialize_map(None)?;
for (i, device) in self.switch_devices.iter().enumerate() {
if let Some(value) = &device.get_switch {
map.serialize_entry(&format_args!("GetSwitch{i}"), value)?;
}
if let Some(value) = &device.get_switch_value {
map.serialize_entry(&format_args!("GetSwitchValue{i}"), value)?;
}
if let Some(value) = &device.state_change_complete {
map.serialize_entry(&format_args!("StateChangeComplete{i}"), value)?;
}
}
map.end()
}
}
#[cfg(feature = "client")]
impl<'de> serde::Deserialize<'de> for DeviceState {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
use serde::de;
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = DeviceState;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("device state object")
}
fn visit_map<A: de::MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
let mut state = DeviceState::default();
while let Some(name) = map.next_key::<&'de str>()? {
// This is pretty complicated because we want to transform shape like `{Name: "GetSwitch2", Value}` into `switch_devices[2].get_switch = Value`.
let index_start = name.find(|c: char| c.is_ascii_digit()).ok_or_else(|| {
de::Error::custom(format!("could not find switch device index in {name:?}"))
})?;
let (name, index) = name.split_at(index_start);
let index = index.parse::<usize>().map_err(|err| {
de::Error::custom(format_args!(
"could not parse switch device index {index:?}: {err}"
))
})?;
// Auto-extend the vec to accommodate the new index. We don't have access to total number of devices here without another async call,
// so we have to make guesses based on the returned data.
if index >= state.switch_devices.len() {
state
.switch_devices
.resize_with(index + 1, SwitchDeviceState::default);
}
let switch_device = &mut state.switch_devices[index];
match name {
"GetSwitch" => {
switch_device.get_switch = Some(map.next_value()?);
}
"GetSwitchValue" => {
switch_device.get_switch_value = Some(map.next_value()?);
}
"StateChangeComplete" => {
switch_device.state_change_complete = Some(map.next_value()?);
}
other => {
return Err(de::Error::unknown_field(
other,
&["GetSwitch", "GetSwitchValue", "StateChangeComplete"],
));
}
}
}
Ok(state)
}
}
deserializer.deserialize_map(Visitor)
}
}