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
use crate::opc_da::{
com_utils::RemoteArray,
errors::{OpcError, OpcResult},
typedefs::ItemHandle,
};
use windows::Win32::System::Variant::VARIANT;
/// Asynchronous I/O functionality (OPC DA 1.0).
///
/// Provides basic asynchronous read/write operations using connection point callbacks.
/// This is the original asynchronous interface defined in OPC DA 1.0.
pub trait AsyncIoTrait {
fn interface(&self) -> OpcResult<&crate::bindings::da::IOPCAsyncIO>;
/// Reads values asynchronously from the server.
///
/// # Arguments
/// * `connection` - Connection point cookie for receiving callbacks
/// * `source` - Specifies whether to read from cache or device
/// * `server_handles` - Array of server item handles to read
///
/// # Returns
/// * `transaction_id` - Identifies this operation in callbacks
/// * `errors` - Array of per-item error codes
///
/// # Errors
/// Returns E_INVALIDARG if server_handles is empty
fn read(
&self,
connection: u32,
source: crate::bindings::da::tagOPCDATASOURCE,
server_handles: &[ItemHandle],
) -> OpcResult<(u32, RemoteArray<windows::core::HRESULT>)> {
if server_handles.is_empty() {
return Err(OpcError::InvalidState(
"server_handles cannot be empty".to_string(),
));
}
let len = server_handles.len().try_into()?;
let mut transaction_id = 0;
let mut errors = RemoteArray::new(len);
// SAFETY: Calling COM interface method Read with valid array pointers and handles.
unsafe {
self.interface()?.Read(
connection,
source,
len,
server_handles.as_ptr() as *const u32,
&mut transaction_id,
errors.as_mut_ptr(),
)?;
}
Ok((transaction_id, errors))
}
/// Writes values asynchronously to the server.
///
/// # Arguments
/// * `connection` - Connection point cookie for receiving callbacks
/// * `server_handles` - Array of server item handles to write
/// * `values` - Array of values to write
///
/// # Returns
/// * `transaction_id` - Identifies this operation in callbacks
/// * `errors` - Array of per-item error codes
///
/// # Errors
/// Returns E_INVALIDARG if arrays are empty or have different lengths
fn write(
&self,
connection: u32,
server_handles: &[ItemHandle],
values: &[VARIANT],
) -> OpcResult<(u32, RemoteArray<windows::core::HRESULT>)> {
if server_handles.len() != values.len() {
return Err(OpcError::InvalidState(
"server_handles and values must have the same length".to_string(),
));
}
if server_handles.is_empty() {
return Err(OpcError::InvalidState(
"server_handles cannot be empty".to_string(),
));
}
let len = server_handles.len().try_into()?;
let mut transaction_id = 0;
let mut errors = RemoteArray::new(len);
// SAFETY: Calling COM interface method Write with valid array pointers and handles.
unsafe {
self.interface()?.Write(
connection,
len,
server_handles.as_ptr() as *const u32,
values.as_ptr(),
&mut transaction_id,
errors.as_mut_ptr(),
)?;
}
Ok((transaction_id, errors))
}
/// Refreshes all active items asynchronously.
///
/// # Arguments
/// * `connection` - Connection point cookie for receiving callbacks
/// * `source` - Specifies whether to refresh from cache or device
///
/// # Returns
/// Transaction ID for identifying the operation in callbacks
fn refresh(
&self,
connection: u32,
source: crate::bindings::da::tagOPCDATASOURCE,
) -> OpcResult<u32> {
// SAFETY: Calling COM interface method Refresh.
unsafe { Ok(self.interface()?.Refresh(connection, source)?) }
}
/// Cancels an outstanding asynchronous operation.
///
/// # Arguments
/// * `transaction_id` - ID of the operation to cancel
///
/// # Returns
/// Result indicating success or failure of cancel request
fn cancel(&self, transaction_id: u32) -> OpcResult<()> {
// SAFETY: Calling COM interface method Cancel.
unsafe { Ok(self.interface()?.Cancel(transaction_id)?) }
}
}