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
use super::GCBorrowedDatapointValue;
use crate::{GCDatapoint, GCDatapointID, GCDatapointValue};
use gc_abi::device_config::{GCEthernetInterface, GCSerialInterface};
use gc_abi::error::GCPluginABIError;
use gc_abi::{GCAbiConversion, GCLogLevel, raw};
use std::ffi::CStr;
/// Zero cost abstraction over the GCPluginInterface
#[repr(transparent)]
pub struct GCPluginInterface(gc_abi::GCPluginInterface);
impl GCPluginInterface {
/// Publishes a datapoint value
#[inline(always)]
pub fn publish_datapoint(&self, datapoint_value: &GCDatapointValue) -> bool {
self.0.publish_datapoint(datapoint_value)
}
/// Stores a datapoint value in the timeseries database
#[inline(always)]
pub fn store_datapoint(&self, datapoint_value: &GCDatapointValue) {
self.0.store_datapoint(datapoint_value)
}
/// Get the datapoints that the plugin is allowed to publish
#[inline(always)]
pub fn get_own_datapoints(&self) -> &[GCDatapoint] {
self.0.get_config().get_own_datapoints()
}
/// Retrieve the last value of a datapoint from the realtime database
#[inline(always)]
pub fn get_last_datapoint_value(&self, datapoint_id: GCDatapointID) -> Option<GCBorrowedDatapointValue> {
// Safety:
// The GCBorrowedDatapointValue is ensures the pointer is deallocated when it goes out of scope
unsafe {
let ptr = self.0.get_last_datapoint_value(datapoint_id);
if ptr.is_null() {
None
} else {
Some(GCBorrowedDatapointValue::from(ptr as gc_abi::raw::GCDatapointValue))
}
}
}
/// Get the datapoints that the plugin is subscribed to
#[inline(always)]
pub fn get_subscribed_datapoints(&self) -> &[GCDatapoint] {
self.0.get_config().get_subscribed_datapoints()
}
/// Log a message
/// Note: for logging messages, the macros at [crate::logger] should be used instead.
#[inline(always)]
pub fn log(&self, level: raw::eGCLogLevel, message: &CStr) {
self.0.log(level, message)
}
/// Log a message audit
/// Note: for logging messages, the macro at [crate::logger] should be used instead.
#[inline(always)]
pub fn log_audit(&self, message: &CStr) {
self.0.log_audit(message)
}
/// Get the log level of the plugin
#[inline]
pub fn get_log_level(&self) -> Result<GCLogLevel, GCPluginABIError> {
self.0.get_log_level()
}
/// Get raw C log level of the plugin
#[inline]
pub fn get_raw_log_level(&self) -> raw::eGCLogLevel {
self.0.get_raw_log_level()
}
/// Parses the plugin specific configuration, this is an expensive operation and should be called only once
pub fn get_own_configuration<T>(&self) -> Result<T, GCPluginABIError>
where
T: serde::de::DeserializeOwned,
{
// Safety:
// The pointer is guaranteed to be valid by the gateway core
let config = unsafe { *self.0.ref_c().config.pluginJsonConfig };
let data = config.data;
let mut result: raw::eResultCallback = raw::eResultCallback_GC_JSON_ERROR;
// Safety:
// The callback is guaranteed to be valid by the gateway core
let str_data_obj =
unsafe { config.getObjectSerialized.ok_or(GCPluginABIError::ErrorCallbackNotAvailable)?(data, &mut result as *mut raw::eResultCallback) };
if result == raw::eResultCallback_GC_JSON_ERROR {
return Err(GCPluginABIError::ErrorSerializingJsonConfig);
}
// Safety:
// The string is guaranteed to be valid unless the function returns an error, which is checked above
let str_data = unsafe { CStr::from_ptr(str_data_obj).to_str()? };
let json_result = serde_json::from_str(str_data)?;
// Safety:
// The string is guaranteed to be valid unless the function returns an error, which is checked above
unsafe { config.freeString.ok_or(GCPluginABIError::ErrorCallbackNotAvailable)?(str_data_obj) };
Ok(json_result)
}
/// Retrieves an Ethernet interface configuration by logical name.
///
/// Returns `None` if:
/// - The core does not support device configuration
/// - The requested interface name does not exist in the device configuration
///
/// # Example
/// ```ignore
/// if let Some(eth) = plugin_interface.get_ethernet_interface("primary_eth") {
/// let interface = eth.interface; // e.g., "eth0"
/// // Use interface for network operations
/// }
/// ```
#[inline]
pub fn get_ethernet_interface(&self, name: &str) -> Option<GCEthernetInterface> {
use std::ffi::CString;
let c_name = CString::new(name).ok()?;
self.0.get_ethernet_interface(&c_name)
}
/// Retrieves a serial interface configuration by logical name.
///
/// Returns `None` if:
/// - The core does not support device configuration
/// - The requested interface name does not exist in the device configuration
///
/// # Example
/// ```ignore
/// if let Some(serial) = plugin_interface.get_serial_interface("moxa_port_1") {
/// let path = serial.interface; // e.g., "/dev/ttyM0"
/// let baud_rate = serial.baud_rate; // e.g., 115200
/// // Open and configure serial port
/// }
/// ```
#[inline]
pub fn get_serial_interface(&self, name: &str) -> Option<GCSerialInterface> {
use std::ffi::CString;
let c_name = CString::new(name).ok()?;
self.0.get_serial_interface(&c_name)
}
}
impl From<raw::GCPluginInterface> for &GCPluginInterface {
fn from(interface: raw::GCPluginInterface) -> Self {
// Safety:
// The current object is a zero cost abstraction of raw::GCPluginInterface,
// thus both types are guaranteed to have the same memory layout.
// TODO: A test should be made to ensure this condition is always true
unsafe {
let a = interface as *const GCPluginInterface;
(a.as_ref().unwrap()) as _
}
}
}
unsafe impl Send for GCPluginInterface {}
unsafe impl Sync for GCPluginInterface {}