DMS结构化数据订阅设计思考

1背景

   本文主要讲述SSIP(Signaling SCADA Integration Platform)人机界面与DMS信息之间的订阅问题。主要包括实时数据库表定义,怎样使用protobuf结构化数据作为订阅的单元?

   SCADA实时监控系统中,图形组态时可以按照常规的模拟量和数字量订阅方式。但是我们也会面临这样的实时结构化的数据,订阅时需要作为整体,那该怎么办呢。

   本文将使用一个DMS例子,构思这样的特殊订阅过程。

2 DMS结构定义

序号

内容

字节数

内容说明

1

帧类型

1

0x07

2

ATP时间

4

UNIX时间

3

服务器时间

4

UNIX时间

4

机车号

2

WORD

5

机车型号

2

WORD

6

车次

8

字符串直接显示

7

司机号

2

WORD

8

副司机号

2

WORD

9

输入交路号

1

 

10

实际交路号

1

 

11

车站号

2

WORD型,TMIS号,全路统一且唯一

12

机车运行方向

1

1:上行

2:下行

13

常用制动速度

2

WORD

14

紧急制动速度

2

WORD

15

目标速度

2

WORD

16

目标距离

2

WORD

17

最大限制速度

2

WORD

18

允许速度

2

WORD

19

临时限速速度

2

WORD

20

ATP等级

1

1CTCS-0 LKJ控车

2CTCS-1 LKJ控车

3CTCS-2 ATP控车

其他值预留

21

ATP模式

1

1FS【完全监控】

2PS【部分监控】

3R0【反向运行】

4C0【引导】

5OS【目视】

6SH【调车】

7SL休眠模式

8SB【备用】

9TR【冒进】

10PT【冒后】

11SF【系统故障】

12IS【隔离】

其他值预留

22

制动信息

1

1:一级常用制动

2:四级常用制动

3:七级常用制动

4:施加紧急制动

5:缓解紧急制动

6:施加常用制动

7:缓解常用制动

其他预留

23

DMI文本信息长度

1

 

24

DMI文本信息

n

字符串

25

司机对DMI操作信息长度

1

 

26

司机对DMI操作信息

n

既有线CTCS-2

包含司机对DMI的操作信息、隔离开关的状态信息、牵引手柄的位置信息和方向手柄的位置信息。

客运专线CTCS-3

包含司机输入数据信息、司机对列车数据的确认信息。

27

牵引手柄信息

1

1:牵引

2:零位

3:制动

4:异常

其他预留

28

方向手柄信息

1

1:向前

2:向后

其他:预留

29

轨道电路区段名称长度

1

 

30

轨道电路区段名称

n

 

31

入口电压

2

WORD

32

出口电压

2

WORD

33

最低电压

2

WORD

34

最高电压

2

WORD

35

载频编码

1

 

36

低频编码

1

 

37

载频

2

载频×10 (Hz)0xFFFF为未知)

38

低频

2

低频×10(Hz)0xFFFF为未知)

39

幅度

2

幅度×10(mv)0xFFFF为未知)

40

RBC报文信息

1

 

41

RBC报文信息

n

 

42

经度

4

GPS经度

43

纬度

4

GPS纬度

44

当前速度

2

GPS速度,WORD

45

动车编号

2

WORD

46

里程

4

动车组当前运行位置的公里标信息,浮点数FLOAT32

47

线路编号

2

当前运行的线路,全路统一且惟一

48

线路名称长度

1

 

49

线路名称

N

 

50

线路行别

1

1上行;2下行

51

前方车站

2

WORD

52

停发车信息

1

1:区间停车

2:区间发车

3:站内停车

4:站内发车

5:非正常发车

其他:预留

53

信号机种类制式

1

1:进出站

2:出站

3:进站

4:通过

5:预告

6:容许

7:分割

9:第1预告

10:第2预告

其他:预留

54

信号机编号

2

WORD

55

信号机灯位

2

Bit8=1:闪光

Bit7=1:白

Bit6=1:红

Bit5=1:红黄

Bit4=1:双黄

Bit3=1:黄2

Bit2=1:黄

Bit1=1:绿黄

Bit0=1:绿

56

前方信号种类制式

1

1:进出站

2:出站

3:进站

4:通过

5:预告

6:容许

7:分割

9:第1预告

10:第2预告

其他:预留

57

前方信号机编号

2

WORD

58

前方信号机灯位

1

Bit7=1:白

Bit6=1:红

Bit5=1:红黄

Bit4=1:双黄

Bit3=1:黄2

Bit2=1:黄

Bit1=1:绿黄

Bit0=1:绿

59

距前方信号机距离

2

WORD型,单位m

60

应答器编号

2

WORD

61

应答器报文信息长度

1

 

62

应答器报文信息

n

 

63

应答器监测结果

1

1OK

0:为异常

其他:预留

64

应有电容

4

FLOAT32

65

实有电容

4

FLOAT32

66

电容是否失效

1

1:失效

2:正常

其他:预留

 

3实时库定义

3.1 dms_data数据表

id                     

列车编号

char(8)

atp_time               

ATP时间

uint32

server_time            

服务器时间

uint32

train_id               

机车号

uint16

train_type             

机车型号

uint16

train_number           

车次

char(8)

pilot_no               

司机号

uint16

copilot_no             

副司机号

uint16

input_crossroadno      

输入交路号

uint8

real_crossroadno       

实际交路号

uint8

station_no             

车站号

uint16

dir                    

机车运行方向

uint8

normalspeed            

常用制动速度

uint16

breakspeed             

紧急制动速度

uint16

targetspeed            

目标速度

uint16

targetdistance         

目标距离

uint16

maxspeed               

最大限制速度

uint16

permitspeed            

允许速度

uint16

tempspeed              

临时限速速度

uint16

atp_priority           

ATP等级

uint8

atp_mode               

ATP模式

uint8

break_info             

制动信息

uint8

dmitextlen             

DMI文本信息长度

uint8

dmitext                

DMI文本信息

char(32)

driverdmilen           

司机对DMI操作信息长度

uint8

driverdmitext          

司机对DMI操作信息

char(32)

pullhandle             

牵引手柄信息

uint8

pullhandle2            

方向手柄信息

uint8

trackregionnamelen     

轨道电路区段名称长度

uint8

trackregionname        

轨道电路区段名称

char(32)

involtage              

入口电压

uint16

outvoltage             

出口电压

uint16

lowvoltage             

最低电压

uint16

maxvoltage             

最高电压

uint16

carryfrequencycode     

载频编码

uint8

lowfrequencycode       

低频编码

uint8

carryfrequency         

载频

uint16

lowfrequency           

低频

uint16

scope                  

幅度

uint16

rbc_text               

RBC报文信息

uint8

rbc_text2              

RBC报文信息

char(32)

longitude              

经度

int32

latitude               

纬度

int32

currentspeed           

当前速度

uint16

runcarcno              

动车编号

uint16

mileage                

里程

float32

lineno                 

线路编号

uint16

linenamelen            

线路名称长度

uint8

linename               

线路名称

char(32)

linelevel              

线路行别

uint16

forwardstation         

前方车站

uint16

stopcartext            

停发车信息

uint8

signaltype             

信号机种类制式

uint8

signalno               

信号机编号

uint16

signallampposition     

信号机灯位

uint16

forwardsignaltype      

前方信号种类制式

uint8

forwardsignalno        

前方信号机编号

uint16

forwardlampposition    

前方信号机灯位

uint8

forwardsignaldistance  

距前方信号机距离

uint16

apno                   

应答器编号

uint16

apgramlen              

应答器报文信息长度

uint8

apgramtext             

应答器报文信息

char(32)

propercap              

应有电容

float32

apresult               

应答器监测结果

uint16

realcap                

实有电容

float32

capenable              

电容是否失效

uint8

 

注:id主键。

3.2 dms_tempdata数据表

id                     

列车编号

char(8)

data

数据区

blob(512)

 

注:id主键。

 

4 protobuf接口定义

package SSIP;

 

message dmsData

{

   required string id = 1;

   optional int32 atp_time = 2;

   optional int32 server_time = 3;

   optional int32 train_id = 4;

   optional int32 train_type = 5;

   optional string train_number = 6;

   optional int32 pilot_no = 7;

   optional int32 copilot_no = 8;

   optional uint32 input_crossroadno = 9;

   optional uint32 real_crossroadno = 10;

   optional int32 station_no = 11;

   optional uint32 dir = 12;

   optional int32 normalspeed = 13;

   optional int32 breakspeed = 14;

   optional int32 targetspeed = 15;

   optional int32 targetdistance = 16;

   optional int32 maxspeed = 17;

   optional int32 permitspeed = 18;

   optional int32 tempspeed = 19;

   optional uint32 atp_priority = 20;

   optional uint32 atp_mode = 21;

   optional uint32 break_info = 22;

   optional string dmitext = 23;

   optional string driverdmitext = 24;

   optional uint32 pullhandle = 25;

   optional uint32 pullhandle2 = 26;

   optional string trackregionname = 27;

   optional int32 involtage = 28;

   optional int32 outvoltage = 29;

   optional int32 lowvoltage = 30;

   optional int32 maxvoltage = 31;

   optional uint32 carryfrequencycode = 32;

   optional uint32 lowfrequencycode = 33;

   optional int32 carryfrequency = 34;

   optional int32 lowfrequency = 35;

   optional int32 scope = 36;

   optional string rbc_text = 37;

   optional int32 longitude = 38;

   optional int32 latitude = 39;

   optional int32 currentspeed = 40;

   optional int32 runcarcno = 41;

   optional float mileage = 42;

   optional int32 lineno = 43;

   optional string linename = 44;

   optional uint32 linelevel = 45;

   optional int32 forwardstation = 46;

   optional uint32 stopcartext = 47;

   optional uint32 signaltype = 48;

   optional int32 signalno = 49;

   optional int32 signallampposition = 50;

   optional uint32 forwardsignaltype = 51;

   optional int32 forwardsignalno = 52;

   optional uint32 forwardlampposition = 53;

   optional int32 forwardsignaldistance = 54;

   optional int32 apno = 55;

   optional string apgramtext = 56;

   optional int32 apresult = 57;

   optional float propercap = 58;

   optional float realcap = 59;

   optional uint32 capenable = 60;

}

 

message dmsDataSet

{

   repeated dmsData dms = 1;

}

5构造dms_tempdata数据区

        表“dms_tempdata”的字段id与表“dms_data”的字段成一一映射关系。表“dms_tempdata”的字段data主要由两块组成:header+bodyHeader包含当前data数据体的长度,类型以及描述;body是由表“dms_data”中的字段组合,使用protobuf序列成数据体。

  DMS结构化数据订阅设计思考_第1张图片


下面的过程为实时库表dms_data结构,序列化到dmsDataSet接口中。

::SSIP::dmsDataSet dms;

   ::SSIP::dmsData* data = dms.add_dms();

   data->set_dms_id(d.dms_id);

   data->set_atp_time(d.atp_time);

   data->set_server_time(d.server_time);

   data->set_train_id(d.train_id);

   data->set_train_type(d.train_type);

   data->set_train_number(d.train_number);

   data->set_pilot_no(d.pilot_no);

   data->set_copilot_no(d.copilot_no);

   data->set_input_crossroadno(d.input_crossroadno);

   data->set_real_crossroadno(d.real_crossroadno);

   data->set_station_no(d.station_no);

   data->set_dir(d.dir);

   data->set_normalspeed(d.normalspeed);

   data->set_breakspeed(d.breakspeed);

   data->set_targetspeed(d.targetspeed);

   data->set_targetdistance(d.targetdistance);

   data->set_maxspeed(d.maxspeed);

   data->set_permitspeed(d.permitspeed);

   data->set_tempspeed(d.tempspeed);

   data->set_atp_priority(d.atp_priority);

   data->set_atp_mode(d.atp_mode);

   data->set_break_info(d.break_info);

   data->set_dmitext(d.dmitext);

   data->set_driverdmitext(d.driverdmitext);

   data->set_pullhandle(d.pullhandle);

   data->set_pullhandle2(d.pullhandle2);

   data->set_trackregionname(d.trackregionname);

   data->set_involtage(d.involtage);

   data->set_outvoltage(d.outvoltage);

   data->set_lowvoltage(d.lowvoltage);

   data->set_maxvoltage(d.maxvoltage);

   data->set_carryfrequencycode(d.carryfrequencycode);

   data->set_lowfrequencycode(d.lowfrequencycode);

   data->set_carryfrequency(d.carryfrequency);

   data->set_lowfrequency(d.lowfrequency);

   data->set_scope(d.scope);

   data->set_rbc_text(d.rbc_text);

   data->set_longitude(d.longitude);

   data->set_latitude(d.latitude);

   data->set_currentspeed(d.currentspeed);

   data->set_runcarcno(d.runcarcno);

   data->set_mileage(d.mileage);

   data->set_lineno(d.lineno);

   data->set_linename(d.linename);

   data->set_linelevel(d.linelevel);

   data->set_forwardstation(d.forwardstation);

   data->set_stopcartext(d.stopcartext);

   data->set_signaltype(d.signaltype);

   data->set_signalno(d.station_no);

   data->set_signallampposition(d.signallampposition);

   data->set_forwardsignaltype(d.forwardsignaltype);

   data->set_forwardsignalno(d.forwardsignalno);

   data->set_forwardlampposition(d.forwardlampposition);

   data->set_forwardsignaldistance(d.forwardsignaldistance);

   data->set_apno(d.apno);

   data->set_apgramtext(d.apgramtext);

   data->set_apresult(d.apresult);

   data->set_propercap(d.propercap);

   data->set_realcap(d.realcap);

   data->set_capenable(d.capenable);

 

   std::string strOut = dms.SerializeAsString();

   int nlen = dms.ByteSize();

 

   BLOB_HEADER hd;

   hd.type = NET_BITS_PROTOBUF;

   hd.len = strOut.length();

   strncpy(hd.desc,"SSIP.dmsDataSet",sizeof(hd.desc));

   。。。

dms_tempdata d;

   memset(&d, 0,sizeof(tempdata));   

   strncpy(d.id, d.dms_id.c_str(),sizeof(d.id));

   assert(strOut.length() <= (sizeof(d.data) -sizeof(BLOB_HEADER)));

   memcpy(d.data, &hd,sizeof(BLOB_HEADER));

   memcpy(d.data +sizeof(BLOB_HEADER), strOut.c_str(), strOut.length());

        。。。

这里的strOut就是表“dms_tempdata”的protobufnlen就是Header信息体的长度。毫无疑问Protobuf结构化数据已经跨平台了,BLOB_HEADER定义的结构也应考虑跨平台封装。

6订阅dms_tempdata数据

订阅规则表达式:

1)        key                       [a-z0-9A-Z]模拟量和数字量

2)        [email protected]         自定义表和字段

3)        [email protected][;field.type]适用blob少量字段分解

4)        [email protected][field.xml] 适用blob批量字段分解

1003列车信息数据产生变化,data数据将主动把这块数据(blob)推送给人机界面。然后HMI使用一个标准动态链接库解析器接口,把data元素进行相应的分解。在这里我们将封装一个接口程序,这个解析器可以回调人机界面处理函数,也可以使用getValue接口分别调用处理。

例:

1003@dms_tempdata.data.blob[;atp_time.uint32;server_time.uint32]

列车编号:1003

实时库表:dms_tempdata

字段域:data

类型:blob

        考虑软件工程中的封闭开放原则,能够集成不同的业务范围,不影响整个HMI软件重构。因此,我们使用动态链接库封装此接口显示调用方式。随着业务范围的变化,我们可以对libblob.dll扩展业务功能,而不需要编译HMI软件。

 

l libblob.dll接口

extern"C"__declspec(dllexport)

bool

parserBlob(void* buf,constint& type,const int& len,

   const std::string& typedesc, LPFUNC_PARSELUA lpfnPaser)

{

。。。

}

 

extern"C"__declspec(dllexport)

bool

getValue(const std::string& object, CBaseValue& value)

{

。。。

}

 

l HMI调用接口

   typedefbool (*LPFUNC_PARSELUA)(const std::string& data);

   typedefbool (*PARSEBLOB)(void* buf,constint& type,const int& len,

const std::string& desc, LPFUNC_PARSELUA lpfnParser);

   typedefbool (*GETVALUE)(const std::string& object, CBaseValue& value);

   PARSEBLOB lpfnParseBlob;

   GETVALUE lpfnGetValue;

     hInst = LoadLibrary(_T("libblob.dll"));

 

   lpfnParseBlob = (PARSEBLOB)GetProcAddress(hInst,"parserBlob");

   lpfnGetValue = (GETVALUE)GetProcAddress(hInst,"getValue");

   。。。

   FreeLibrary(hInst);

l HMI订阅接口

。。。

int nBlobSize;

void* lpBlobData;

CBaseValue value;

LPFUNC_PARSELUA lpfnParser = (LPFUNC_PARSELUA)parser;

。。。

if (nBlobSize > sizeof(BLOB_HEADER))

{

   BLOB_HEADER* hd = (BLOB_HEADER*)lpBlobData;

   if (hd->len <= (nBlobSize - sizeof(BLOB_HEADER)))

   {

       void* buf = (char*)lpBlobData + sizeof(BLOB_HEADER);                           

       if (lpfnParseBlob && lpfnGetValue)

       {

           (*lpfnParseBlob)(buf, hd->type, hd->len, hd->desc, lpfnParser);

           //下面可能循环处理,把data分解的内容释放到不同的动画对象中。

           。。。

           (*lpfnGetValue)(data->obj, value);

           。。。

       }

   }

}

。。。

 

 

7总结

        省略。

你可能感兴趣的:(protobuf)