2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > (4条消息)工控机上位机软件的开发历程(五)

(4条消息)工控机上位机软件的开发历程(五)

时间:2022-04-15 02:18:00

相关推荐

(4条消息)工控机上位机软件的开发历程(五)

思绪中断了,博客更新也中断了。现在补上。前面说了一些架构方面的事情,现在说一些具体一点的。

设备信息和因子信息

设备和因子是两个比较核心的概念,它们的结构设计,关系到整个系统。我们先来看设备信息的结构:

/// <summary>

/// 设备信息

/// </summary>

public class DeviceInfo

{

/// <summary>

/// 设备ID

/// </summary>

public int DeviceID { get; set; }

/// <summary>

/// 设备名称

/// </summary>

public string DeviceName { get; set; }

/// <summary>

/// 传输类型

/// </summary>

public TransferType TransferType { get; set; }

#region 串口信息

/// <summary>

/// 串口号

/// </summary>

public string PortName { get; set; }

/// <summary>

/// 波特率

/// </summary>

public int BaudRate { get; set; }

/// <summary>

/// 校验位

/// </summary>

public Parity Parity { get; set; }

/// <summary>

/// 停止位

/// </summary>

public StopBits StopBits { get; set; }

/// <summary>

/// 数据位

/// </summary>

public int DataBits { get; set; }

/// <summary>

/// 从机地址

/// </summary>

public int SlaveAddr { get; set; }

#endregion

#region 网络传输信息

/// <summary>

/// 服务端IP地址

/// </summary>

public string ServerIP { get; set; }

/// <summary>

/// 服务端端口号

/// </summary>

public int ServerPort { get; set; }

/// <summary>

/// 客户端IP

/// </summary>

public string ClientIP { get; set; }

/// <summary>

/// 客户端端口号

/// </summary>

public int ClientPort { get; set; }

#endregion

/// <summary>

/// 附加信息1

/// </summary>

public string Addition1 { get; set; }

/// <summary>

/// 附加信息2

/// </summary>

public string Addition2 { get; set; }

/// <summary>

/// 是否启用

/// </summary>

public bool IsEnable { get; set; }

/// <summary>

/// 使用协议

/// </summary>

public string UseProtocol { get; set; }

/// <summary>

/// 设备索引

/// </summary>

public int DeviceIndex { get; set; }

/// <summary>

/// 因子列表

/// </summary>

public List<FactorInfo> FactorList { get; set; }

}

传输类型是一个枚举类型,如下:

/// <summary>

/// 传输类型

/// </summary>

public enum TransferType

{

/// <summary>

/// 无传输

/// </summary>

None = 0,

/// <summary>

/// 串口传输

/// </summary>

Com = 1,

/// <summary>

/// TCP客户端

/// </summary>

Client = 11,

/// <summary>

/// TCP服务端

/// </summary>

Server = 12,

/// <summary>

/// UDP

/// </summary>

UDP = 22,

}

看到这里,可能有人会有疑问,为什么会有一个无传输的传输类型。实际上,在应用的时候有一种虚拟设备,只用来计算采集到的数据,它自己是不需要采集的。例如有一台流量计,它采集到的是瞬时流量。有时候我们需要累计流量的时候,就需要有这样一台不传输的设备。

串口信息应该不用解释。网络传输信息因为考虑到UDP,所以需要有两个IP和端口。

附加信息是一些特殊的设备,你根本想不到它还有一些什么属性,多两个这样的信息,可以避免不断修改类结构。

是否启用是在仪器维护的时候使用的。

设备索引是在设备表格里排序用的,实际上没什么作用。

在使用的时候,实际上很多时候我们要用到仪器的状态。例如串口断开了,我们应该有一个串口通信的状态。但我们可以看到,在上面的类结构里面,并没有这样的信息。其实,我们把所有状态都做成了因子,因子列表里总有一项代表仪器的某个状态。

接下来,我们看一下因子信息的设计:

/// <summary>

/// 因子信息

/// </summary>

public class FactorInfo

{

/// <summary>

/// 因子ID

/// </summary>

public int FactorID { get; set; }

/// <summary>

/// 因子名称

/// </summary>

public string FactorName { get; set; }

/// <summary>

/// 因子地址

/// </summary>

public string FactorAddr { get; set; }

/// <summary>

/// 表达式

/// </summary>

public string Expression { get; set; }

/// <summary>

/// 存储格式

/// </summary>

public StorageFormat StorageFormat { get; set; }

/// <summary>

/// 指令集

/// </summary>

public string CommandSet { get; set; }

/// <summary>

/// 因子值

/// </summary>

public double FactorValue { get; set; }

/// <summary>

/// 存储字段

/// </summary>

public string ValueField { get; set; }

/// <summary>

/// 数据库单位

/// </summary>

public short BaseUnit { get; set; }

/// <summary>

/// 显示单位

/// </summary>

public short DisplayUnit { get; set; }

/// <summary>

/// 显示小数位数

/// </summary>

public int DecimalDigit { get; set; }

/// <summary>

/// 报警上限

/// </summary>

public float AlarmUpper { get; set; }

/// <summary>

/// 报警下限

/// </summary>

public float AlarmLower { get; set; }

/// <summary>

/// 是否显示

/// </summary>

public bool IsDisplay { get; set; }

/// <summary>

/// 是否报表显示

/// </summary>

public bool IsReportDisplay { get; set; }

/// <summary>

/// 顺序索引

/// </summary>

public int FactorIndex { get; set; }

/// <summary>

/// 类型编码

/// </summary>

public string TypeCode { get; set; }

/// <summary>

/// 状态解析

/// </summary>

public string StateParser { get; set; }

/// <summary>

/// 附加信息1

/// </summary>

public string Addition1 { get; set; }

/// <summary>

/// 附加信息2

/// </summary>

public string Addition2 { get; set; }

}

因子信息其实更加复杂。在前面,我们提到过有五种因子,分别是实际因子、计算因子、状态因子、反控因子和模拟量因子。但在我们新的设计里,这样的概念变得模糊。我们遇到了很多新的问题,例如:

(1)实际因子并不一定是双寄存器浮点数。

(2)状态因子有可能也需要存储。

(3)在Modbus协议里面,离散量有两种,寄存器也有两种。

(4)没有固定的信息表示自动启动的类型。

(5)之前的状态解析需要额外加一个XML文件。

(6)量程并没有多大用处。

在说明之前,上面还有一个结构体需要列出来:

/// <summary>

/// 存储类型

/// </summary>

public enum StorageFormat

{

/// <summary>

/// 离散量只读

/// </summary>

BOOL_R = 1,

/// <summary>

/// 离散量只写

/// </summary>

BOOL_W = 2,

/// <summary>

/// 离散量读写

/// </summary>

BOOL_RW = 3,

/// <summary>

/// 单寄存器INT只读

/// </summary>

REGISTER_R = 301,

/// <summary>

/// 单寄存器INT只写

/// </summary>

REGISTER_W = 302,

/// <summary>

/// 单寄存器INT读写

/// </summary>

REGISTER_RW = 303,

/// <summary>

/// 单寄存器BOOL只读

/// </summary>

REGISTER_BOOL_R = 401,

/// <summary>

/// 单寄存器BOOL只写

/// </summary>

REGISTER_BOOL_W = 402,

/// <summary>

/// 单寄存器BOOL读写

/// </summary>

REGISTER_BOOL_RW = 403,

/// <summary>

/// 双寄存器REAL只读

/// </summary>

REAL_R = 201,

/// <summary>

/// 双寄存器REAL只写

/// </summary>

REAL_W = 202,

/// <summary>

/// 双寄存器REAL读写

/// </summary>

REAL_RW = 203,

/// <summary>

/// 其他

/// </summary>

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等。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。