跳到主要内容

诊断

通过 device.diagnostics() 读诊断与报警(diag_snapshot() / alarms() 仍可用)。

配合事件使用

建议通过 事件 驱动异常处理,而非自行轮询。 直接读取诊断适用于界面显示。

运行态(connected / state / error_code)见 属性与状态机。过程数据读写见 过程数据

功能概览

功能说明
诊断快照链路、帧计数、看门狗、抖动、报警观测
现场问题对照按症状选字段
IOPS / IOCS过程数据质量
报警活动列表 / 历史 / 确认
I&M 标识订货号、序列号、站名
用户记录槽位参数记录
复位通信复位 / 工厂复位

诊断快照

类别成员类型说明
可用性okbool诊断是否可用。false = 设备未启动,读 message
可用性shared_memory_mappedbool过程数据是否已就绪
可用性messageString不可用时的原因;可用时为空
可用性timestampString快照时刻(ISO-8601 原文)
链路stateString运行状态文案
链路link_upbool网卡链路是否 Up
链路connectedbool周期数据是否正常
链路active_iocr_countu32已激活的 IO 连接数
链路iocr_active / iocr_healthyu32IO 连接激活 / 健康位图
计数heartbeat / cycle_counteru64心跳 / 周期循环计数
计数last_erroru64最近错误码(0 = 无)
计数rx_frames / tx_framesu64收 / 发总帧数
计数rx_rt_frames / tx_rt_framesu64收 / 发 RT 周期帧
计数dropped_frames / invalid_framesu64被丢弃 / 非法帧
计数watchdog_tripsu64看门狗超时次数
计数last_rx_tsc / last_tx_tscu64最近收 / 发帧 TSC
过程区input_area_length / output_area_lengthu32I 区 / Q 区长度(字节)
周期stack_cyclesu64周期计数
周期stack_frames_rx / stack_frames_txu64收 / 发帧
周期stack_missed_ticksu64错过的节拍
周期stack_max_jitter_usu64最大抖动(微秒)
报警观测stack_alarm_rx_countu64收到控制器报警总数
报警观测stack_alarm_tx_ack_ok / stack_alarm_tx_ack_failu64本机报警被确认 / 确认失败
报警观测stack_alarm_ack_pendingu64在途报警(0/1)
报警观测stack_alarm_maint_stateu64维护请求状态
报警观测stack_alarm_last_*u32最近控制器报警:arep / slot / subslot / type / seq / usi / len
错误error_codei32错误码原值(0 = 无)
错误error_code_enum()PnSRuntimeErrorCode上项的枚举形态。未识别值为 Unknown

diag_snapshot()

pub fn diag_snapshot(&self) -> ServiceResult<DiagSnapshot>

一次取回当前诊断快照,适合界面刷新和日志。设备尚未启动时 ok == false,读 message,不视为异常。get_diag_snapshot / get_diag 是同一函数。

示例:

let snap = device.diag_snapshot()?;
if !snap.ok {
println!("{}", snap.message);
return Ok(());
}

println!("{}", if snap.link_up { "Up" } else { "Down" });
println!("{}", if snap.connected { "周期交换正常" } else { "未建立" });
println!("帧: {} / {}", snap.rx_frames, snap.tx_frames);
println!("看门狗: {}", snap.watchdog_trips);

现场问题对照

现场症状看哪些字段
网线没插好snap.link_up
周期数据通不通snap.connected
看门狗反复跳snap.watchdog_trips
帧在丢dropped_frames + invalid_frames
周期稳不稳stack_max_jitter_us + stack_missed_ticks
控制器报警没消化stack_alarm_ack_pending / stack_alarm_tx_ack_fail
本机有没有活动报警alarms() / alarm_count()
过程数据质量io_status()

IOPS / IOCS

pub fn io_status(&self) -> IoStatus

过程数据好不好。0x80 = GOOD,0x00 = BAD。掉线如实全 BAD。未 start 或请求失败返回 IoStatus::empty()

pub struct IoStatus {
pub input_iops: Vec<u8>,
pub input_iocs: Vec<u8>,
pub output_iops: Vec<u8>,
pub output_iocs: Vec<u8>,
pub input_valid: bool,
pub output_valid: bool,
}

impl IoStatus {
pub fn empty() -> Self;
pub fn is_good(ioxs: u8) -> bool; // 0x80 = GOOD
pub fn is_all_good_bytes(ioxs: &[u8]) -> bool; // 空 = false
pub fn input_data_good(&self) -> bool;
pub fn output_data_good(&self) -> bool;
pub fn output_consumed(&self) -> bool;
pub fn all_good(&self) -> bool;
}

示例:

if !device.io_status().all_good() {
println!("{:?}", device.io_status());
}

报警

空列表 = 无活动报警,不是失败。响应不是列表就报 BadResponse,不折成「无报警」。get_alarms / alarms 同一函数;get_alarm_history / alarm_history 同一函数。

pub struct AlarmItem {
pub id: String, // 如 PnS.LinkDown
pub severity: String, // Info / Warning / Error / Critical
pub message: String,
pub occurred_at: String, // ISO-8601 原文
pub count: u32, // ≥1
pub source: String,
}

alarms()

pub fn alarms(&self) -> ServiceResult<Vec<AlarmItem>>

当前活动报警。无活动报警返回空列表,不是失败。

示例:

for a in device.alarms()? {
println!("{} {} {}", a.id, a.severity, a.message);
}

alarm_history()

pub fn alarm_history(&self) -> ServiceResult<Vec<AlarmItem>>

报警历史(最近 500 条,时间升序)。无历史返回空列表,不是失败。

示例:

for a in device.alarm_history()? {
println!("{} {} {}", a.occurred_at, a.severity, a.message);
}

ack_alarm / acknowledge_alarm

pub fn ack_alarm(&self, id: &str) -> ServiceResult<()>

确认一条活动报警。ID 取自 alarms()。没有这条活动报警会返回错误。acknowledge_alarm 是同一函数。

示例:

device.ack_alarm("PnS.LinkDown")?;

ack_all_alarms / acknowledge_all_alarms

pub fn ack_all_alarms(&self) -> ServiceResult<u32>

确认全部活动报警。返回被确认条数(0 = 无活动报警)。acknowledge_all_alarms 是同一函数。

示例:

let n = device.ack_all_alarms()?;

I&M 标识

pub fn im_data(&self) -> ServiceResult<ImData>

订货号、序列号、站名。get_im_data 是同一函数。I&M1–I&M3 已由原生栈启用并如实回读,I&M4 未启用;主站没写过时读到的是栈的出厂占位值。缺字段保持空 / 0 / false,这次读到什么看 im14_supportedim_note

pub struct ImData {
pub vendor_id: u32,
pub device_id: u32,
pub order_id: String,
pub serial_number: String,
pub hardware_revision: String,
pub software_revision: String,
pub station_name: String,
pub product_name: String,
pub im_supported: String,
pub im14_supported: bool,
pub im1_tag_function: String,
pub im1_tag_location: String,
pub im2_date: String,
pub im3_descriptor: String,
pub im4_signature_hex: String,
pub im_note: String,
pub project_order_number: String,
pub project_hardware_release: String,
pub project_software_release: String,
pub timestamp: String,
}

示例:

let im = device.im_data()?;
println!("{} {:04X} {:04X}", im.station_name, im.vendor_id, im.device_id);

用户记录

pub fn records(&self) -> ServiceResult<UserRecords>

get_records 是同一函数。缺字段默认 0 / 空。count 是服务端返回值,不按数组长度回填。

pub struct UserRecords {
pub records: Vec<UserRecord>,
pub count: i32,
pub timestamp: String,
}

pub struct UserRecord {
pub slot: u16,
pub subslot: u16,
pub index: u16,
pub length: u16,
pub data_hex: String,
}

复位

pub fn reset_device(&self, mode: &str) -> ServiceResult<()>
  • communication:软停再启(记录保留)
  • factory:销毁后全新启动(记录清空)

非法 mode 返回 InvalidParam。这是慢操作,可能要等几秒。

示例:

device.reset_device("communication")?;