在上位机软件开发中,除了向设备发送控制指令之外,还有一个非常常见的功能,就是实时监控设备的运行状态。
例如一台自动化设备,上位机可能需要持续显示:
设备是否在线
当前运行模式
温度
电压
转速
运行时间
当前任务状态
设备是否处于忙碌状态
这些状态通常来自串口、TCP、CAN 或其他通信方式。
如果把所有状态处理都直接写在界面代码中,随着设备参数越来越多,代码会变得比较混乱。因此实际开发中,可以单独设计一个设备状态监控模块,由它统一接收、保存和通知设备状态变化。
本文简单介绍一种 C# 上位机状态监控系统的实现思路。
一、状态监控系统主要做什么
状态监控和设备控制的作用并不完全一样。
设备控制主要是:
上位机
↓
发送指令
↓
设备执行
而状态监控更多是:
设备
↓
周期上传数据
↓
上位机接收
↓
解析状态
↓
更新内存数据
↓
刷新界面
例如设备每隔1秒上传一次:
温度:36.5℃
电压:12.1V
转速:1200 RPM
状态:Running
上位机收到以后,需要将最新数据保存下来。
一个比较简单的结构可以设计成:
Device Communication
↓
Protocol Parser
↓
DeviceStateMonitor
↓
DeviceState
↓
UI
通信模块只负责收发数据。
协议模块负责解析数据。
DeviceStateMonitor 负责维护当前状态。
界面只读取或者监听状态变化。
这样各个模块职责会比较清楚。
二、定义设备状态模型
首先可以定义一个 DeviceState,用于保存设备当前状态。
例如:
public class DeviceState
{
public bool IsOnline { get; set; }
public DeviceRunState RunState { get; set; }
public double Temperature { get; set; }
public double Voltage { get; set; }
public int Speed { get; set; }
public DateTime LastUpdateTime { get; set; }
}
设备运行状态可以使用枚举:
public enum DeviceRunState
{
Unknown,
Idle,
Running,
Paused,
Stopped
}
例如当前设备状态:
DeviceState state = new DeviceState
{
IsOnline = true,
RunState = DeviceRunState.Running,
Temperature = 36.5,
Voltage = 12.1,
Speed = 1200,
LastUpdateTime = DateTime.Now
};
这样相比在程序中到处使用:
double temperature;
double voltage;
bool connected;
int state;
统一使用 DeviceState 会更加方便。
后续如果设备增加:
压力
流量
电流
工作模式
也可以继续在状态模型中扩展。
三、实现设备状态监控器
接下来创建 DeviceStateMonitor。
它主要负责维护当前设备状态。
例如:
public class DeviceStateMonitor
{
private DeviceState currentState =
new DeviceState();
public DeviceState CurrentState
{
get
{
return currentState;
}
}
public void Update(DeviceState newState)
{
currentState = newState;
currentState.LastUpdateTime =
DateTime.Now;
}
}
设备收到新的数据后:
DeviceState state = new DeviceState
{
IsOnline = true,
RunState = DeviceRunState.Running,
Temperature = 38.2,
Voltage = 12.3,
Speed = 1250
};
monitor.Update(state);
然后其他模块可以读取:
Console.WriteLine(
monitor.CurrentState.Temperature
);
这种实现比较简单。
但在真实上位机软件中,通常还需要解决一个问题:
状态变化以后,如何通知界面刷新?
因此可以增加状态变化事件。
四、设备状态发生变化时通知界面
可以在 DeviceStateMonitor 中增加一个事件:
public class DeviceStateMonitor
{
private DeviceState currentState =
new DeviceState();
public event Action<DeviceState>
StateChanged;
public DeviceState CurrentState
{
get
{
return currentState;
}
}
public void Update(DeviceState newState)
{
currentState = newState;
currentState.LastUpdateTime =
DateTime.Now;
StateChanged?.Invoke(currentState);
}
}
界面或者业务模块进行订阅:
monitor.StateChanged += state =>
{
Console.WriteLine(
$"当前温度:{state.Temperature}"
);
Console.WriteLine(
$"当前转速:{state.Speed}"
);
};
以后每次收到新的设备数据:
monitor.Update(state);
都会触发 StateChanged。
整体过程变成:
设备上传数据
↓
解析 DeviceState
↓
DeviceStateMonitor.Update
↓
StateChanged
↓
界面刷新
如果使用 WPF,还可以继续结合:
INotifyPropertyChanged
或者 MVVM 数据绑定。
这样状态变化之后,界面可以自动刷新,不需要手动给每一个控件赋值。
五、如何判断设备是否掉线
设备状态监控除了显示温度、电压等数据之外,还有一个比较重要的功能:
判断设备是否在线。
例如设备正常情况下每隔2秒上传一次状态。
如果:
10秒没有收到任何设备数据
通常就可以认为设备已经离线。
前面 DeviceState 中已经保存:
LastUpdateTime
因此可以定时检查:
public bool CheckOnline()
{
TimeSpan timeout =
DateTime.Now -
currentState.LastUpdateTime;
if(timeout.TotalSeconds > 10)
{
currentState.IsOnline = false;
return false;
}
currentState.IsOnline = true;
return true;
}
例如:
正常:
12:00:01 收到数据
12:00:03 收到数据
12:00:05 收到数据
设备异常:
12:00:05 最后一次数据
12:00:15 仍然没有新数据
此时:
IsOnline = false
界面可以显示:
设备离线
这种方式比单纯判断:
TcpClient.Connected
更加可靠。
因为 TCP 连接对象显示 Connected,并不一定代表设备当前真的可以正常通信。
实际项目中通常会结合:
TCP连接状态
心跳数据
最后一次收到设备数据的时间
共同判断设备是否在线。
六、状态监控在实际上位机中的扩展
基础的状态监控完成以后,后续通常还会继续增加一些功能。
只通知真正发生变化的状态
例如设备每秒上传一次温度:
36.5
36.5
36.5
36.5
如果数据完全没有变化,没有必要每次都刷新整个界面。
可以比较新旧状态:
if(oldState.RunState != newState.RunState)
{
// 运行状态发生变化
}
对于部分状态,可以只在变化时通知。
保存一定时间的历史数据
例如:
10:01:01 35.2℃
10:01:02 35.5℃
10:01:03 35.7℃
10:01:04 36.0℃
可以保存最近:
100条
或者:
最近10分钟
的数据。
后续就可以用于:
实时曲线
趋势分析
状态回放
不过需要注意不要无限保存。
例如可以使用 Queue:
Queue
超过最大数量后删除最早的数据。
区分快速数据和普通状态
有些设备状态更新频率很高。
例如:
传感器采样数据:100ms一次
设备运行模式:几分钟才变化一次
可以分别管理:
RealtimeData
DeviceState
避免所有数据都使用一个大型 DeviceState 对象频繁刷新。
和报警模块进行配合
状态监控主要负责:
当前设备是什么状态
而异常报警负责:
这个状态是否正常
例如:
DeviceStateMonitor
↓
Temperature = 85℃
↓
AlarmChecker
↓
温度过高报警
这样状态监控和报警系统可以保持独立。
一个相对完整的 C# 上位机结构可以是:
通信模块
↓
协议解析
↓
状态监控
↓
业务逻辑
↓
界面显示
同时:
状态监控
↓
异常检测
↓
报警系统
这种设计在设备越来越复杂以后会比较容易维护,也方便以后增加新的设备参数、状态类型和监控功能。