本文是如何用jxTMS进行数据采集的第二部分,整个系列的文章请查看:docker版jxTMS使用指南:4.4版升级内容
docker版本的使用,请查看:docker版jxTMS使用指南
4.0版jxTMS的说明,请查看:4.0版升级内容
4.2版jxTMS的说明,请查看:4.2版升级内容
设备对数据的处理
设备接收到站点递交的消息后,会依次完成如下的工作:
1、根据配置用相应的策略来解析所接收到的消息
不同的设备有自己独特的编码方式,所以相应的解码工作单独抽离出来由该设备的策略来实现。当然,有的解码工作已经由站点在提取设备标识时就已经完成了,如使用自定义协议包传送数据的站点。
解析策略一般单独编写,然后注册到系统中。代码文件应放到app的相应目录中,并依次在各目录的__init.py__文件中引用。
main.py文件的加载顺序是先import app和module,然后再执行站点的初始化【加载站点和设备】,这就可以在站点加载时正确引用到相应的策略了。
2、触发数据接收事件来对数据做需要的处理
这里所说的处理主要指数据的转换、修正等针对数据本身的处理,如潮位仪收到测得的潮位数据后,要根据潮位仪安装点的标高换算成潮水的实时水位。
在业务上,很多时候我们还需要利用设备数据来启动业务处理。但笔者认为,为了提高系统的稳定性,最好将业务处理与此处所说的数据处理分隔开来,以最大化的降低代码的耦合度。
那么,不在此处启动业务处理,那在什么时候呢?!请参考之前的【本地数据总线】一文,笔者就是基于此种考虑,才增加了数据总线机制。
在需要执行业务处理时,通过向数据总线注册一个兴趣点来抄收设备接收并处理好的数据,然后完成需要的业务操作,从而将业务处理代码从整个设备数据处理流中分离出来,不会对设备的数据处理与保存过程产生任何干扰与影响。
尤其是在业务频繁变动的场景中,此种方案更具稳定性与可靠性。
3、设备数据的保存
接收并处理完毕的设备数据会被保存到数据库中,而这就需要完成两个动作:
-
根据配置将处理后的设备数据保存到数据库中的哪个数据表中
-
由于设备数据的采集频率可能会很高,如果数据的短期变化又不太重要,可以必要综合考虑是实时保存所有设备数据,还是周期性保存最新的设备数据
当给出了设备数据的保存间隔【saveDataInterval】参数时,该设备的数据将以saveDataInterval【单位:分钟】为间隔进行保存,如果该参数为0,则实时保存每一个新数据。
此外,由于设备的数据采集频次一般都很高,所以大多数情况下,都需要进行分表。如果需要分表的话,对于启动了jxTMS主系统的主站来说,只要在data文件中相应的数据类定义时指定rename属性即可。
注:如果是不启动jxTMS主系统的从站,如用于现场modbus设备采集,这些数据如果也需要保存到现场的数据库中,并同样需要分表,也很简单,一是【app/data/DieselGenerator.py】中展示的,提供一个_sql_createTable建表语句【通过查询主站所创建的该表的建表语句获得】;二是将此建表语句注册到jxTMS中:
ORM.registerSQL_createTable('DieselGenerator',_sql_createTable,renameType='day')
最后,在main.py中调用:
#启动创建需要分表的调度器
ORM.startRenameTableScheduler()
从站即会同样完成所有注册建表语句时指定了renameType的分表工作。
原则上,我们建议尽可能的使用jxTMS主系统来完成分表工作,原因很简单,风险有点高。python侧来执行分表工作,就意味着,真有需要调整data中的数据类定义的时候,必须同时完成:
-
修改data文件中的数据类定义,否则java的jxTMS主系统在管理时会出错
-
修改mysql中该数据库表的定义【新的表会自动更新】
-
修改python侧的建表语句,并重启python侧的代码
前两者都比较简单,尤其是mysql比较熟悉的情况下;但后者,必须进行相关的全业务测试,尤其是还需要重启服务器,所以风险高了很多。
分表数据的查询,可以利用前面文章中讲解过的query对象【参考:数据查询】,非常的简便。
4、站点综合
一般情况下,站点都是管理方面的作用大于数据处理方面的作用,如开通、停运、对设备的管理/配置等。但有时也确实需要站点综合下属所有设备的数据以提供业务上的支撑。
如,配电系统的告警处理,管理方关心的是母线电压、储电系统的SOC等配电系统的综合性健康指标。但这些指标却是分别采集自下属的各个设备的某几个数据点。
这种情况下,以站点来统一过滤并综合这些关键指标就是必然的选择了。
因此,站点在接收并提取出设备信息后,调用distribute函数来将接收到的数据分发给相应的设备:
def distribute(self, d, data):
其使用一般是在站点的receive中,如site_multiDev_push类型的站点:
def receive(self, bsMsg):
s = str(bsMsg,"utf8")
js = json.loads(s)
dn = js.get('dn')
if not dn is None:
d = self.getDev(dn)
if not d is None:
self.distribute(d,js)
distribute在向下属设备分发数据后,会将设备回传的数据回传给站点内部的receiveData函数,以完成站点的数据综合处理框架。该框架完成:
-
设备对站点的通知,目前主要是跟踪状态改变以完成相应的告警处理
-
站点触发onReceive事件来对设备回送的数据进行必要的综合
-
如果必要【重载newOrmData函数并返回站点数据对象】,保存【实时或周期性】站点综合后的数据
5、数据联动
数据处理完毕,设备会通过数据总线发送自己接收并处理完毕的数据,所有对该设备的数据感兴趣者,都可以注册到数据总线上,接收到此新收数据事件并执行自己的处理。
前文说过,jxTMS以此种方式实现数据处理与业务处理的隔离,以提供更好的灵活性、弹性。比如,不停机重启的动态升级业务功能。
所以,使用jxTMS时应尽可能的将数据方面的处理与业务方面的处理区分出来,在设备与站点的onReceive事件【需要的话】中只完成数据转换、调整、综合、加工等工作。和业务相关的处理则通过监听数据总线的方式放到另外的代码模块中完成。
这种方式虽然看起来比较繁琐,而且效率有些低,但由于充分的隔离了数据处理与业务处理,所以业务的变动不会干扰到数据处理框架的正常工作,使得不管什么样情况下,总能稳定而可靠的接收、处理并保存正确的数据。
目前,数据总线还只是本地的数据总线,有需要的话,数据总线可以基于MQ实现异机联动,就如jxTMS的java侧管控平台那样,从而进一步的提高数据处理的稳定性与可靠性。
6、告警
目前主要是针对超时未接收到设备数据发出告警。
现版本是将告警放到了设备处,考虑到收不到数据一是设备故障、二是线路故障,所以新版本会将告警收到站点处进行,以兼顾这二者。
新版本的故障告警机制是:
-
某站点处于正常状态时【刚开机或当下属所有设备都正常工作时】第一次收到失联,则发送故障告警
-
当出现失联后,每半个小时发送一次当前的故障综述,即当前失联设备的列表与第一次故障的时间点
-
当所有失联设备都恢复正常后,发送一个全站恢复的通知
目前,jxTMS只有一个默认的钉钉告警策略,但如果服务器网络中断,则依然无法发出告警,所以如果对告警非常敏感,还应增加一个网络之外的告警通道或机制。
参考资料:
下面的系列文章讲述了如何用jxTMS开发一个实用的业务功能:
下面的系列文章讲述了jxTMS的一些基本开发能力: