思绪中断了,博客更新也中断了。现在补上。前面说了一些架构方面的事情,现在说一些具体一点的。
设备和因子是两个比较核心的概念,它们的结构设计,关系到整个系统。我们先来看设备信息的结构:
///
/// 设备信息
///
public class DeviceInfo
{
///
/// 设备ID
///
public int DeviceID { get; set; }
///
/// 设备名称
///
public string DeviceName { get; set; }
///
/// 传输类型
///
public TransferType TransferType { get; set; }
#region 串口信息
///
/// 串口号
///
public string PortName { get; set; }
///
/// 波特率
///
public int BaudRate { get; set; }
///
/// 校验位
///
public Parity Parity { get; set; }
///
/// 停止位
///
public StopBits StopBits { get; set; }
///
/// 数据位
///
public int DataBits { get; set; }
///
/// 从机地址
///
public int SlaveAddr { get; set; }
#endregion
#region 网络传输信息
///
/// 服务端IP地址
///
public string ServerIP { get; set; }
///
/// 服务端端口号
///
public int ServerPort { get; set; }
///
/// 客户端IP
///
public string ClientIP { get; set; }
///
/// 客户端端口号
///
public int ClientPort { get; set; }
#endregion
///
/// 附加信息1
///
public string Addition1 { get; set; }
///
/// 附加信息2
///
public string Addition2 { get; set; }
///
/// 是否启用
///
public bool IsEnable { get; set; }
///
/// 使用协议
///
public string UseProtocol { get; set; }
///
/// 设备索引
///
public int DeviceIndex { get; set; }
///
/// 因子列表
///
public List FactorList { get; set; }
}
传输类型是一个枚举类型,如下:
///
/// 传输类型
///
public enum TransferType
{
///
/// 无传输
///
None = 0,
///
/// 串口传输
///
Com = 1,
///
/// TCP客户端
///
Client = 11,
///
/// TCP服务端
///
Server = 12,
///
/// UDP
///
UDP = 22,
}
看到这里,可能有人会有疑问,为什么会有一个无传输的传输类型。实际上,在应用的时候有一种虚拟设备,只用来计算采集到的数据,它自己是不需要采集的。例如有一台流量计,它采集到的是瞬时流量。有时候我们需要累计流量的时候,就需要有这样一台不传输的设备。
串口信息应该不用解释。网络传输信息因为考虑到UDP,所以需要有两个IP和端口。
附加信息是一些特殊的设备,你根本想不到它还有一些什么属性,多两个这样的信息,可以避免不断修改类结构。
是否启用是在仪器维护的时候使用的。
设备索引是在设备表格里排序用的,实际上没什么作用。
在使用的时候,实际上很多时候我们要用到仪器的状态。例如串口断开了,我们应该有一个串口通信的状态。但我们可以看到,在上面的类结构里面,并没有这样的信息。其实,我们把所有状态都做成了因子,因子列表里总有一项代表仪器的某个状态。
接下来,我们看一下因子信息的设计:
///
/// 因子信息
///
public class FactorInfo
{
///
/// 因子ID
///
public int FactorID { get; set; }
///
/// 因子名称
///
public string FactorName { get; set; }
///
/// 因子地址
///
public string FactorAddr { get; set; }
///
/// 表达式
///
public string Expression { get; set; }
///
/// 存储格式
///
public StorageFormat StorageFormat { get; set; }
///
/// 指令集
///
public string CommandSet { get; set; }
///
/// 因子值
///
public double FactorValue { get; set; }
///
/// 存储字段
///
public string ValueField { get; set; }
///
/// 数据库单位
///
public short BaseUnit { get; set; }
///
/// 显示单位
///
public short DisplayUnit { get; set; }
///
/// 显示小数位数
///
public int DecimalDigit { get; set; }
///
/// 报警上限
///
public float AlarmUpper { get; set; }
///
/// 报警下限
///
public float AlarmLower { get; set; }
///
/// 是否显示
///
public bool IsDisplay { get; set; }
///
/// 是否报表显示
///
public bool IsReportDisplay { get; set; }
///
/// 顺序索引
///
public int FactorIndex { get; set; }
///
/// 类型编码
///
public string TypeCode { get; set; }
///
/// 状态解析
///
public string StateParser { get; set; }
///
/// 附加信息1
///
public string Addition1 { get; set; }
///
/// 附加信息2
///
public string Addition2 { get; set; }
}
因子信息其实更加复杂。在前面,我们提到过有五种因子,分别是实际因子、计算因子、状态因子、反控因子和模拟量因子。但在我们新的设计里,这样的概念变得模糊。我们遇到了很多新的问题,例如:
(1)实际因子并不一定是双寄存器浮点数。
(2)状态因子有可能也需要存储。
(3)在Modbus协议里面,离散量有两种,寄存器也有两种。
(4)没有固定的信息表示自动启动的类型。
(5)之前的状态解析需要额外加一个XML文件。
(6)量程并没有多大用处。
在说明之前,上面还有一个结构体需要列出来:
///
/// 存储类型
///
public enum StorageFormat
{
///
/// 离散量只读
///
BOOL_R = 1,
///
/// 离散量只写
///
BOOL_W = 2,
///
/// 离散量读写
///
BOOL_RW = 3,
///
/// 单寄存器INT只读
///
REGISTER_R = 301,
///
/// 单寄存器INT只写
///
REGISTER_W = 302,
///
/// 单寄存器INT读写
///
REGISTER_RW = 303,
///
/// 单寄存器BOOL只读
///
REGISTER_BOOL_R = 401,
///
/// 单寄存器BOOL只写
///
REGISTER_BOOL_W = 402,
///
/// 单寄存器BOOL读写
///
REGISTER_BOOL_RW = 403,
///
/// 双寄存器REAL只读
///
REAL_R = 201,
///
/// 双寄存器REAL只写
///
REAL_W = 202,
///
/// 双寄存器REAL读写
///
REAL_RW = 203,
///
/// 其他
///
OTHER = 101,
}
我们不再区分实际因子、计算因子和模拟量因子。在之前,这些因子默认的存储类型是双寄存器REAL只读,但实际上类型很多。之前的解决办法是先添加一个状态因子,然后再添加一个计算因子去读这个状态因子的值。这样实在有点累赘。在新的设计里面,因子默认类型是双寄存器REAL只读,当然这个类型是可以修改的。
实际因子和计算因子的区分,就是有没有填写表达式。如果表达式为空,则使用因子地址。否则,就使用表达式去计算。
量程这一概念,其实就是模拟量因子才用的。我们去掉了模拟量因子,同时去掉了量程。只需要在表达式里填入公式,就能实现模拟量因子。
我们来看看指令集CommandSet的设计。它解决了两个问题:
(1)有些设备用保持寄存器(03读),有些设备用输入寄存器(04)读。
(2)有些设备写一个要用06,有些一定要用10。
CommandSet默认是这样的:01,03,06,代表了离散量默认用01,寄存器读默认用03,寄存器写默认用06。遇到上面的问题,修改这里的值就行。
在之前的设计里面,只有实际因子、计算因子和模拟量因子是会存到数据库里面的,状态因子是一个过眼云烟。实际上一些状态因子也需要保存。这次设计引入了是否保存的概念。如果ValueField不为空,即是会存到数据库里的因子。
状态解析StateParser解决了每次都要外加一个XML文件的问题。我们知道,状态读取回来,是1、2、3、4这样的数字,它代表了什么呢?可能1代表待机,2代表测试中,3代表停止中,等等。这些文字我们之前是保存在一个XML文件里面,每次读取到数据时,用过这个文件去解析。有了StateParser,维护就方便多了。StateParser的格式大概如下:1:待机;2:测试中;3:停止中。
类型编码TypeCode是比较复杂的。它需要表示下面几种内容的东西:
(1)状态的类型,有些状态是普通的状态,有些是需要报警的状态,有些是设备的主要状态。通过在TypeCode里面输入T1、T2、T3完成。
(2)自动启动类型,在反控因子里使用。例如流程在运行的时候,到了整点,需要自动按下水样测试的按钮。那哪个反控因子是水样测试呢?通过名字去找?很多时候填写名字并没有那么认真。所以我们需要在TypeCode里输入:S1(水样测试)、S2(停止测试)、S3(标样核查)等等。
(3)发送什么数据,在反控因子里使用。我们点击按钮的时候,一般是向某个地址发送1的。但有些设备并不是1。那么我们可以在TypeCode里输入发送的内容,例如是N206、N0等。