1pub use crate::bindings::da::tagOPCITEMDEF;
8pub use crate::bindings::da::{tagOPCITEMRESULT, tagOPCITEMSTATE};
9use crate::opc_da::client::StringIterator;
10pub use crate::opc_da::utils::RemoteArray;
11pub use windows::Win32::System::Variant::VARIANT;
12
13pub trait ServerConnector: Send + Sync {
23 type Server: ConnectedServer;
25
26 fn enumerate_servers(&self) -> anyhow::Result<Vec<String>>;
32
33 fn connect(&self, server_name: &str) -> anyhow::Result<Self::Server>;
39}
40
41pub trait ConnectedServer {
49 type Group: ConnectedGroup;
51
52 fn query_organization(&self) -> anyhow::Result<u32>;
60
61 fn browse_opc_item_ids(
67 &self,
68 browse_type: u32,
69 filter: Option<&str>,
70 data_type: u16,
71 access_rights: u32,
72 ) -> anyhow::Result<StringIterator>;
73
74 fn change_browse_position(&self, direction: u32, name: &str) -> anyhow::Result<()>;
80
81 fn get_item_id(&self, item_name: &str) -> anyhow::Result<String>;
87
88 #[allow(clippy::too_many_arguments)]
94 fn add_group(
95 &self,
96 name: &str,
97 active: bool,
98 update_rate: u32,
99 client_handle: u32,
100 time_bias: i32,
101 percent_deadband: f32,
102 locale_id: u32,
103 revised_update_rate: &mut u32,
104 server_handle: &mut u32,
105 ) -> anyhow::Result<Self::Group>;
106
107 fn remove_group(&self, server_group: u32, force: bool) -> anyhow::Result<()>;
113}
114
115pub trait ConnectedGroup {
121 fn add_items(
127 &self,
128 items: &[tagOPCITEMDEF],
129 ) -> anyhow::Result<(
130 RemoteArray<tagOPCITEMRESULT>,
131 RemoteArray<windows::core::HRESULT>,
132 )>;
133
134 fn read(
140 &self,
141 source: crate::bindings::da::tagOPCDATASOURCE,
142 server_handles: &[u32],
143 ) -> anyhow::Result<(
144 RemoteArray<tagOPCITEMSTATE>,
145 RemoteArray<windows::core::HRESULT>,
146 )>;
147
148 fn write(
154 &self,
155 server_handles: &[u32],
156 values: &[VARIANT],
157 ) -> anyhow::Result<RemoteArray<windows::core::HRESULT>>;
158}
159
160use crate::opc_da::client::v2::{Client, Group, Server};
163use crate::opc_da::client::{
164 BrowseServerAddressSpaceTrait, ClientTrait, ItemMgtTrait, ServerTrait, SyncIoTrait,
165};
166use anyhow::Context;
167
168pub struct ComConnector;
172
173impl ServerConnector for ComConnector {
174 type Server = ComServer;
175
176 fn enumerate_servers(&self) -> anyhow::Result<Vec<String>> {
177 let client = Client;
178 let guid_iter = client
179 .get_servers()
180 .context("Failed to enumerate OPC DA servers from registry")?;
181
182 let mut servers = Vec::new();
183 for guid in guid_iter.flatten() {
184 let win_guid: windows::core::GUID = unsafe { std::mem::transmute_copy(&guid) };
189 if win_guid == windows::core::GUID::zeroed() {
190 continue;
191 }
192
193 if let Ok(progid) = crate::helpers::guid_to_progid(&win_guid)
194 && !progid.is_empty()
195 {
196 servers.push(progid);
197 }
198 }
199 servers.sort();
200 servers.dedup();
201 Ok(servers)
202 }
203
204 fn connect(&self, server_name: &str) -> anyhow::Result<Self::Server> {
205 let opc_server = crate::helpers::connect_server(server_name)?;
206 Ok(ComServer(opc_server))
207 }
208}
209
210pub struct ComServer(Server);
212
213impl ConnectedServer for ComServer {
214 type Group = ComGroup;
215
216 fn query_organization(&self) -> anyhow::Result<u32> {
217 Ok(self.0.query_organization()?.0.cast_unsigned())
218 }
219
220 fn browse_opc_item_ids(
221 &self,
222 browse_type: u32,
223 filter: Option<&str>,
224 data_type: u16,
225 access_rights: u32,
226 ) -> anyhow::Result<StringIterator> {
227 #[allow(clippy::cast_possible_wrap)]
228 let enum_str = self.0.browse_opc_item_ids(
229 crate::bindings::da::tagOPCBROWSETYPE(browse_type as i32),
230 filter,
231 data_type,
232 access_rights,
233 )?;
234 Ok(StringIterator::new(enum_str))
235 }
236
237 fn change_browse_position(&self, direction: u32, name: &str) -> anyhow::Result<()> {
238 #[allow(clippy::cast_possible_wrap)]
239 Ok(self.0.change_browse_position(
240 crate::bindings::da::tagOPCBROWSEDIRECTION(direction as i32),
241 name,
242 )?)
243 }
244
245 fn get_item_id(&self, item_name: &str) -> anyhow::Result<String> {
246 Ok(self.0.get_item_id(item_name)?)
247 }
248
249 fn add_group(
250 &self,
251 name: &str,
252 active: bool,
253 update_rate: u32,
254 client_handle: u32,
255 time_bias: i32,
256 percent_deadband: f32,
257 locale_id: u32,
258 revised_update_rate: &mut u32,
259 server_handle: &mut u32,
260 ) -> anyhow::Result<Self::Group> {
261 let group = self.0.add_group(
262 name,
263 active,
264 client_handle,
265 update_rate,
266 locale_id,
267 time_bias,
268 percent_deadband,
269 revised_update_rate,
270 server_handle,
271 )?;
272 Ok(ComGroup(group))
273 }
274
275 fn remove_group(&self, server_group: u32, force: bool) -> anyhow::Result<()> {
276 Ok(self.0.remove_group(server_group, force)?)
277 }
278}
279
280pub struct ComGroup(Group);
282
283impl ConnectedGroup for ComGroup {
284 fn add_items(
285 &self,
286 items: &[tagOPCITEMDEF],
287 ) -> anyhow::Result<(
288 RemoteArray<tagOPCITEMRESULT>,
289 RemoteArray<windows::core::HRESULT>,
290 )> {
291 Ok(self.0.add_items(items)?)
292 }
293
294 fn read(
295 &self,
296 source: crate::bindings::da::tagOPCDATASOURCE,
297 server_handles: &[u32],
298 ) -> anyhow::Result<(
299 RemoteArray<tagOPCITEMSTATE>,
300 RemoteArray<windows::core::HRESULT>,
301 )> {
302 Ok(self.0.read(source, server_handles)?)
303 }
304
305 fn write(
306 &self,
307 server_handles: &[u32],
308 values: &[VARIANT],
309 ) -> anyhow::Result<RemoteArray<windows::core::HRESULT>> {
310 Ok(self.0.write(server_handles, values)?)
311 }
312}