属性与状态机
属性
| 类别 | 属性 | 类型 | 访问 | 说明 |
|---|---|---|---|---|
| 静态 | is_service_ready() | bool | 只读 | 本机服务是否就绪。失败写 last_error,不 panic |
last_error() | String | 只读 | 进程级最近一次失败文案(sticky,成功路径不清空) | |
last_error_code() | PnSErrorCode | 只读 | 进程级最近一次 SDK 调用错误码。从未失败 = Success | |
service_connected() | bool | 只读 | 主站是否周期交换。服务没起来 = false | |
service_state() | ServiceState | 只读 | 运行态。服务没起来 = Unknown | |
service_wait_reason() | String | 只读 | 等待原因。服务没起来 = 「服务未就绪」 | |
service_status_message() | String | 只读 | 状态文案。服务没起来 = 「服务未就绪」 | |
| 连接与会话 | connected() | bool | 只读 | IO 控制器是否已经建立周期数据交换。true 才表示周期数据在换 |
attached() | bool | 只读 | 本 SDK 会话是否已连上本机服务。new() 后为 true,close 后为 false。stop 不改变 | |
is_started() / is_running() | bool | 只读 | IO 设备是否已启用(start 之后、stop 之前)。两者同值 | |
| 运行态 | state() | ServiceState | 只读 | 运行态。读不到为 Unknown,不要当成 Stopped |
status() | ServiceResult<ServiceState> | 只读 | 取 status_ex 的 state。服务不可达返回 Err | |
status_ex() | ServiceResult<ServiceStatus> | 只读 | 一次取回 state / connected / error_code / 报警计数 / ever_connected | |
wait_reason() | String | 只读 | 本会话等待原因。主站已连时为空 | |
status_message() | String | 只读 | 本会话状态文案 | |
ever_connected() | bool | 只读 | 是否曾与 IO 控制器建立过连接。true 且 connected 为 false = 掉线 | |
| 错误码 | error_code() | PnSRuntimeErrorCode | 只读 | 最近一次运行错误。未识别值 Unknown。原值仍经 error_code_raw 透传 |
error_code_raw() | i32 | 只读 | 运行错误原值(0 = 无错误)。未识别值仍经本函数透传 | |
error_message() | String | 只读 | 运行错误中文消息。未识别带原值 | |
last_service_error() | String | 只读 | 本会话最近一次失败文案(sticky,成功路径不清空) | |
| 报警 | alarm_count() | i32 | 只读 | 活动报警总数。读不到 = 0。与 alarms() 同源 |
critical_alarm_count() | i32 | 只读 | Critical 报警数。读不到 = 0 | |
alarms() | ServiceResult<Vec<AlarmItem>> | 只读 | 活动报警。见 [诊断 · 报警](./diagnostics#报警) | |
alarm_history() | ServiceResult<Vec<AlarmItem>> | 只读 | 历史报警(最近 500 条) | |
| 诊断与过程数据 | events() | &ServiceEvents | 只读 | 本机运行态事件。见 [事件](./events) |
diag_snapshot() | ServiceResult<DiagSnapshot> | 只读 | 一次取回链路、帧计数、看门狗、抖动。读不到时 ok 为 false。字段见 [诊断](./diagnostics) | |
io_status() | IoStatus | 只读 | IOPS / IOCS 快照。0x80 = GOOD,0x00 = BAD。掉线如实全 BAD。字段见 [诊断](./diagnostics) | |
process_data() | ProcessData | 只读 | 过程数据视图,写 Q / 读 I。见 [过程数据](./io) | |
| 本机会话 | host() | &str | 只读 | 本会话连接的服务主机 |
port() | u16 | 只读 | 本会话连接的服务端口 |
没有 is_connected()。没有 PnSState / PnSStatus(类型名是 ServiceState / ServiceStatus)。失败返回 ServiceError,不是 PnSError。
同名自由函数在 darra_pns::statics::status(is_service_ready / service_connected / service_state / service_wait_reason / service_status_message / last_error / last_error_code),不必构造实例。
let ready = PnSService::is_service_ready();
let master = PnSService::service_connected();
let st = PnSService::service_state();
let wait = PnSService::service_wait_reason();
let err = PnSService::last_error();
let _ = (ready, master, st, wait, err);
ServiceState
运行态类型。取值见 枚举参考。
ServiceStatus
pub struct ServiceStatus {
pub state: ServiceState,
pub connected: bool,
pub error_code: i32,
pub alarm_count: i32,
pub critical_alarm_count: i32,
pub ever_connected: bool,
}
impl ServiceStatus {
pub fn error_code_enum(&self) -> PnSRuntimeErrorCode;
}
PnSRuntimeErrorCode
运行错误码类型。取值见 枚举参考。
runtime_error_message(raw) 把原值翻成中文;实例 error_message() 走同一张表。
启停
构造签名见 初始化。start 一次,不要每个周期调用。过程数据写 Q(模块输出)、读 I(模块输入)。
start()
pub fn start(&self) -> ServiceResult<()>
启用 IO。已 start 且尚未 stop 则幂等返回 Ok(())。这不是主站已周期交换,主站是否在换数据看 connected()。
同一时刻只能有一个实例 start。失败写入 last_error,返回 ServiceError(另一实例占用 = RealtimeGate / PnSErrorCode::Busy)。
示例:
device.start()?;
if !device.connected() {
eprintln!("{}", device.wait_reason());
}
stop()
pub fn stop(&self) -> ServiceResult<()>
停用 IO。实例仍在,可再次 start。不是 close()。
示例:
device.stop()?;
close()
pub fn close(&mut self)
结束本实例。清零 Q 的净荷窗(PPM 输出区前 3 字节 IOPS 前缀不动),I 保留。Drop 会调 close。不 stop。
示例:
device.close();
运行态
connected() / attached()
pub fn connected(&self) -> bool
pub fn attached(&self) -> bool
connected:IO 控制器是否已经建立周期数据交换。未建立实例 =false。attached:本实例是否已连上本机服务。new()后为true,close后为false。stop不改变。
示例:
if device.attached() && device.connected() {
println!("已连上服务且主站在换周期数据");
}
state()
pub fn state(&self) -> ServiceState
运行态。读不到真实状态为 Unknown,不要当成 Stopped。旁路查看用 service_state()。
示例:
if device.state() == darra_pns::ServiceState::DataExchange {
println!("周期数据交换正常");
}
status() / status_ex()
pub fn status(&self) -> ServiceResult<ServiceState>
pub fn status_ex(&self) -> ServiceResult<ServiceStatus>
status_ex 一次取回快照。status 等于 status_ex()?.state。服务不可达返回 Err。
connected == true 才表示已与 IO 控制器建立周期数据交换。new() 成功、甚至 start() 之后本值仍可能是 false。
示例:
let snap = device.status_ex()?;
println!("{:?} {}", snap.state, snap.connected);
wait_reason()
pub fn wait_reason(&self) -> String
本实例等待原因。主站已连时为空。循环 if !device.connected() 时读本函数或 last_service_error()。
示例:
if !device.connected() {
eprintln!("{}", device.wait_reason());
}
ever_connected()
pub fn ever_connected(&self) -> bool
是否曾与 IO 控制器建立过连接。false = 从未连过;true 且 connected() == false = 掉线。
示例:
if !device.connected() && device.ever_connected() {
println!("掉线");
}
错误码
error_code() / error_code_raw() / error_message()
pub fn error_code(&self) -> PnSRuntimeErrorCode
pub fn error_code_raw(&self) -> i32
pub fn error_message(&self) -> String
运行错误。与 SDK 调用码 PnSErrorCode 不是一类,不要混用。读不到:error_code_raw() = 0,error_code() = Ok。
示例:
let err = device.error_code();
let text = device.error_message();
let raw = device.error_code_raw();
let _ = (err, text, raw);
last_error() / last_error_code() / last_service_error()
pub fn last_error() -> String
pub fn last_error_code() -> PnSErrorCode
pub fn last_service_error(&self) -> String
静态是进程级;实例是本实例。静态成功路径不清空。循环 if !connected() 时若本属性为空,读 wait_reason()。
PnSErrorCode
SDK 调用错误码(成功为 Success)。取值见 枚举参考。
C 语言码 ≠ 本枚举码(-4..-8 同值不同义),跨语言换算必须走 PnSErrorCode::from_native_code(i32),禁止裸转。
服务路径:ServiceResult<T> = Result<T, ServiceError>。写 I 返回 OutputReadOnly(I 只读,写 Q)。
pub enum ServiceError {
NotConnected,
ConnectFailed(String, u16, String),
HttpStatus(u16, String),
BadResponse(String),
ServiceRejected(String),
ServiceRejectedCode(i32, String),
InvalidAddress(String),
InvalidParam(String),
OutputReadOnly, // 写 I(I 只读)
DbSlotNotSupported(u16),
OutOfRange(u16, usize, usize),
NotStarted, // 未 start
InvalidString(String),
Io(String),
RealtimeGate(String), // 另一实例已 start
SharedMemory(String), // 过程数据未就绪 / 读写失败
}
时钟
1ms 高精度时钟由服务进程持有,本 crate 不提供进程内开关:经服务 Web API
POST /api/high-resolution-timer(body {"highResolutionTimer": true|false})开关,见
Web API。安装包默认不打开。
打开后等待更准,鼠标可能变顿。非 Windows 为空操作。
完整示例
use darra_pns::PnSService;
fn main() -> darra_pns::ServiceResult<()> {
let mut device = PnSService::new();
device.start()?;
while running {
if !device.connected() {
println!("{:?}", device.state());
println!("{}", device.wait_reason());
println!("{}", device.error_message());
continue;
}
device.write_bool("Q0.0", true)?;
let from_plc = device.read_bool("I0.0")?;
let _ = from_plc;
}
device.stop()?;
Ok(())
}