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
use windows::core::Interface as _;
use crate::opc_da::{
client::{GroupIterator, StringIterator},
com_utils::{LocalPointer, RemotePointer},
errors::{OpcError, OpcResult},
typedefs::GroupHandle,
};
/// OPC Server management functionality.
///
/// Provides methods to create and manage groups within an OPC server,
/// as well as monitor server status and enumerate existing groups.
pub trait ServerTrait<Group: TryFrom<windows::core::IUnknown, Error = windows::core::Error>> {
fn interface(&self) -> OpcResult<&crate::bindings::da::IOPCServer>;
/// Adds a new group to the OPC server.
///
/// # Arguments
/// * `name` - Group name for identification
/// * `active` - Whether the group should initially be active
/// * `client_handle` - Client-assigned handle for the group
/// * `update_rate` - Requested update rate in milliseconds
/// * `locale_id` - Locale ID for text strings
/// * `time_bias` - Time zone bias in minutes from UTC
/// * `percent_deadband` - Percent change required to trigger updates
///
/// # Returns
/// The newly created group object
///
/// # Errors
/// Returns E_POINTER if group creation fails
#[allow(clippy::too_many_arguments)]
fn add_group(
&self,
name: &str,
active: bool,
update_rate: u32,
client_handle: GroupHandle,
time_bias: i32,
percent_deadband: f32,
locale_id: u32,
revised_update_rate: &mut u32,
server_handle: &mut GroupHandle,
) -> OpcResult<Group> {
let mut group = None;
let group_name = LocalPointer::from(name);
let group_name = group_name.as_pcwstr();
let mut raw_server_handle = 0u32;
// SAFETY: Calling COM interface method AddGroup with group params and pointers.
unsafe {
self.interface()?.AddGroup(
group_name,
active,
update_rate,
client_handle.0,
&time_bias,
&percent_deadband,
locale_id,
&mut raw_server_handle,
revised_update_rate,
&crate::bindings::da::IOPCItemMgt::IID,
&mut group,
)?;
}
*server_handle = GroupHandle(raw_server_handle);
match group {
None => Err(OpcError::Com {
source: windows::core::Error::new(
windows::Win32::Foundation::E_POINTER,
"Failed to add group, returned null",
),
}),
Some(group) => group
.cast::<windows::core::IUnknown>()?
.try_into()
.map_err(|source| OpcError::Com { source }),
}
}
/// Gets the current server status.
///
/// # Returns
/// Server status structure containing vendor info, time, state,
/// and group counts
fn get_status(&self) -> OpcResult<RemotePointer<crate::bindings::da::tagOPCSERVERSTATUS>> {
// SAFETY: Calling COM interface method GetStatus.
let status = unsafe { self.interface()?.GetStatus()? };
Ok(RemotePointer::from_raw(status))
}
/// Removes a group from the server.
///
/// # Arguments
/// * `server_handle` - Server's handle for the group
/// * `force` - If true, remove even if clients are connected
fn remove_group(&self, server_handle: GroupHandle, force: bool) -> OpcResult<()> {
// SAFETY: Calling COM interface method RemoveGroup with server_handle.
unsafe {
self.interface()?.RemoveGroup(server_handle.0, force)?;
}
Ok(())
}
/// Creates an enumerator for groups.
///
/// # Arguments
/// * `scope` - Scope of groups to enumerate (public, private, or all)
///
/// # Returns
/// Enumerator interface for iterating through groups
fn create_group_enumerator(
&self,
scope: crate::bindings::da::tagOPCENUMSCOPE,
) -> OpcResult<GroupIterator<Group>> {
// SAFETY: Calling COM interface method CreateGroupEnumerator with IEnumUnknown IID.
let enumerator = unsafe {
self.interface()?
.CreateGroupEnumerator(scope, &windows::Win32::System::Com::IEnumUnknown::IID)?
};
Ok(GroupIterator::new(enumerator.cast()?))
}
/// Creates an enumerator for group names.
///
/// # Arguments
/// * `scope` - Scope of group names to enumerate (public, private, or all)
///
/// # Returns
/// Enumerator interface for iterating through group names
fn create_group_name_enumerator(
&self,
scope: crate::bindings::da::tagOPCENUMSCOPE,
) -> OpcResult<StringIterator> {
// SAFETY: Calling COM interface method CreateGroupEnumerator with IEnumString IID.
let enumerator = unsafe {
self.interface()?
.CreateGroupEnumerator(scope, &windows::Win32::System::Com::IEnumString::IID)?
};
Ok(StringIterator::new(enumerator.cast()?))
}
}