跳到主要内容

属性与状态机

提示
  • 初始化请参考 初始化 页面。
  • 过程数据(写 Q / 读 I)请参考 过程数据 页面。
  • 诊断功能请参考 诊断 页面。

属性

类别属性类型访问说明
静态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::statusis_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() 后为 trueclose 后为 falsestop 不改变。

示例:

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 = 从未连过;trueconnected() == 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(())
}