DLT645-97/07通信规约 | 报文解析 | 组织报文与解析报文(C++)

news2024/10/7 9:27:46

文章目录

  • 一、DLT645通信规约
    • 1.DLT645-1997通信规约
    • 2.DLT645-2007通讯规约
    • 3.DLT645-1997与DLT645-2007的区别
  • 二、DLT645通信规约报文解析
    • 1.DLT645-1997通信规约报文解析
    • 2.DLT645-2007通信规约报文解析
  • 三、C++代码组织报文与解析报文

一、DLT645通信规约

  DLT645协议,包括DLT645-1997和DLT645-2007,是一种问答式(主从式)通信规约。在这种通信模式下,通常存在一个主站和一个或多个从站。主站负责发起通信请求,从站则根据主站的请求提供相应的响应。

问答式规约的通信过程通常包括以下几个步骤:

  • 主站发起请求:主站发送一个请求报文给特定的从站,请求报文中包含了需要从站执行的操作,如读取电能量、读取实时参数等。
  • 从站接收请求:从站接收到主站的请求后,根据请求的内容进行处理。
  • 从站准备响应:从站根据请求的内容准备相应的数据,并构造一个响应报文。
  • 从站发送响应:从站将构造好的响应报文发送回主站。
  • 主站接收响应:主站接收到从站的响应后,根据响应内容进行相应的处理。

1.DLT645-1997通信规约

  DLT645-1997是中国电力行业标准,全称为《多功能电能表通信协议》,主要用于电力系统中电能表的通信。这个标准定义了电能表与数据终端设备之间进行数据交换时的物理层、链路层以及应用层的通信协议。DLT645-1997协议采用主-从结构的半双工通信模式,硬件接口通常使用RS-485。

  • 适用范围:适用于本地系统中多功能表的费率装置与手持单元(HHU)或其他数据终端设备进行点对点的或一主多从的数据交换方式。
  • 物理层:采用RS-485标准串行电气接口
  • 链路层:规定了通信链路及应用技术规范。
  • 应用层:涉及到电能表的数据交换,如读取电能量、功率等信息

字节格式
  每字节含8位二进制码,传输时加上一个起始位(0)、一个偶校验位和一个停止位(1)共11位。其传输序列如下。D0是字节的最低有效位,D7是字节的最高有效位。先传低位,后传高位。
在这里插入图片描述
报文帧格式
  帧是传送信息的基本单元。帧格式如下:

说明代码
帧起始符(1个字节)68H
地址域(6个字节)A0~A5
帧起始符(1个字节)68H
控制码(1个字节)C
数据长度域(1个字节)L
数据域 (变长)DATA
校验码(1个字节)CS
结束符(1个字节)16H
  • 帧起始符68H:标识一帧信息的开始,其值为68H=01101000B

  • 地址域A0∽A5:地址域由6个字节构成,每字节2位BCD码。地址长度为12位十进制数,可以为表号、资产号、用户号、设备号等。通常作为电能表表号字段,如下。具体使用可由用户自行决定。当使用的地址码长度不足6字节时,用十六进制AAH补足6字节。低地址位在先,高地址位在后。当地址为999999999999H时,为广播地址。
    iterators

  • 控制码C:控制码格式如下
    在这里插入图片描述

    • D7

      • D7=0:由主站发出的命令帧
      • D7=1:由从站发出的应答帧
    • D6

      • D6=0:从站正确应答
      • D6=1:从站对异常信息的应答
    • D5

      • D5=0:无后续数据帧
      • D5=1:有后续数据帧
    • D4∽D0:请求及应答功能码

       00000:保留
       00001:读数据
       00010:读后续数据(在第一次读取之后,继续读取剩余的数据。并不是一次读取多个点位的数据)
       00011:重读数据
       00100:写数据
       01000:广播校时
       01010:写设备地址
       01100:更改通信速率
       01111:修改密码
       10000:最大需量清零
      
  • 数据长度L:L为数据域的字节数。读数据时L≤200,写数据时L≤50,L=0 表示无数据域。

  • 数据域DATA:数据域包括数据标识和数据、密码等,其结构随控制码的功能而改变(变长)。传输时发送方按字节进行加33H处理,接收方按字节进行减33H处理

    数据标识

      本规约采用四级树状结构的标识法来表示这些数据。用2个字节的4个字段分别标识数据的类型和属性,这2个字节为DI1和DI0,4个字段分别为DI1H、DI1L、DI0H、DI0L,其中DI0L为最低级标识字段,DI1H为最高级标识段。
    在这里插入图片描述

    • DI1H标识数据的类型
    • DI1L、DI0H、DI0L标识数据的不同属性

    对于电能量和最大需量数据,由于其具有多个属性,如时域性(当前值、上月值、上上月值等)、分类属性(有功、无功)、供电方向属性(正向、反向)、费率属性(总量、不同费率的量)等,它们的标识如下:
    电能量数据标识
    在这里插入图片描述
    最大需量数据标识
    在这里插入图片描述

  • 校验码CS:从帧起始符开始到校验码之前的所有各字节的模256的和, 即各字节二进制算术和,不计超过256的溢出值。

  • 结束符号16H:标识一帧信息的结束,其值为16H=00010110B。

传输事项

  • 前导字节:在发送帧信息之前,先发送1-4个字节FEH,以唤醒接收方。
  • 传输次序:所有数据项均先传送低位字节,后传送高位字节。
  • 传输响应:每次通信都是由主站按信息帧地址域选择的从站发出请求命令帧开始,被请求的从站根据命令帧中控制码的要求作出响应。
  • 差错控制:字节校验为偶校验,帧校验为纵向信息校验和,按收方无论检测到偶校验出错或纵向信息校验和出错,均放该信息帧,不予响应。

2.DLT645-2007通讯规约

  DLT645-2007是中国电力行业标准,全称为《多功能电能表通信协议》,主要用于电力系统中电能表的通信。这个标准定义了电能表与数据终端设备之间进行数据交换时的物理层、链路层以及应用层的通信协议。DLT645-2007协议采用主-从结构的半双工通信模式,硬件接口通常使用RS-485。
  本协议为主-从结构的半双工通信方式。手持单元或其它数据终端为主站,多功能电能表为从站。每个多功能电能表均有各自的地址编码通信链路的建立与解除均由主站发出的信息帧来控制。每帧由帧起始符、从站地址域、控制码、数据域长度、数据域、帧信息纵向校验码及帧结束符7个域组成。每部分由若干字节组成。
字节格式
  每字节含8位二进制码,传输时加上一个起始位(0)、一个偶校验位和一个停止位(1), 共 11位。其传输序列如下。D0 是字节的最低有效位,D7 是字节的最高有效位。先传低位,后传高位。
在这里插入图片描述
报文帧格式
  帧是传送信息的基本单元。帧格式如下:

说明代码
帧起始符(1个字节)68H
地址域(6个字节)A0~A5
帧起始符(1个字节)68H
控制码(1个字节)C
数据长度域(1个字节)L
数据域 (变长)DATA
校验码(1个字节)CS
结束符(1个字节)16H
  • 帧起始符68H:标识一帧信息的开始,其值为68H=01101000B

  • 地址域 A0~A5:地址域由 6 个字节构成,每字节 2 位 BCD 码,地址长度可达12位十进制数。每块表具有唯一的通信地址,且与物理层信道无关。当使用的地址码长度不足 6 字节时,高位用“0”补足 6 字节。

    • 通信地址999999999999H为广播地址,只针对特殊命令有效,如广播校时、广播冻结等。广播命令不要求从站应答。
    • 地址域支持缩位寻址,即从若干低位起,剩余高位补AAH作为通配符进行读表操作,从站应答帧的地址域返回实际通信地址。
    • 地址域传输时低字节在前,高字节在后
  • 控制码 C
    控制码格式如下:
    在这里插入图片描述

  • 数据域长度L
    L 为数据域的字节数。读数据时 L≤200,写数据时 L≤50,L=0 表示无数据域。

  • 数据域DATA
    数据域包括数据标识、密码、操作者代码、数据、帧序号等,其结构随控制码的功能而改变。传输时发送方按字节进行加33H处理,接收方按字节进行减33H处理

    数据标识说明
      数据标识编码用四个字节区分不同数据项,四字节分别用DI3、DI2、DI1和DI0代表,每字节采用十六进制编码。数据类型分为七类:电能量、最大需量及发生时间、变量、事件记录、参变量、冻结量、负荷记录。
    在这里插入图片描述

    • DI3

      DI3标识符对应数据类型
      00电能量
      01最大需量及发生时间
      02变量数据 (遥测等)
      03事件记录
      04参变量数据
      05冻结量
      06负荷记录

    可以参考DLT645-2007多功能电能表通信协议数据标识编码表来确定每个标识的具体含义。

  • 校验码 CS
    从第一个帧起始符开始到校验码之前的所有各字节的模 256 的和,即各字节二进制算术和,不计超过 256 的溢出值。

  • 结束符 16H
    标识一帧信息的结束,其值为 16H=00010110B。

传输事项

  • 前导字节:在主站发送帧信息之前,先发送4个字节FEH,以唤醒接收方。
  • 传输次序:所有数据项均先传送低位字节,后传送高位字节。数据传输的举例:电能量值为123456.78kWh,其传输次序如下
    在这里插入图片描述
  • 传输响应:每次通信都是由主站向按信息帧地址域选择的从站发出请求命令帧开始,被请求的从站接收到命令后作出响应。
  • 差错控制:字节校验为偶校验,帧校验为纵向信息校验和,接收方无论检测到偶校验出错或纵向信息校验和出错,均放弃该信息帧,不予响应。

3.DLT645-1997与DLT645-2007的区别

  DLT645-1997与DLT645-2007都是中国电力行业标准,用于规范多功能电能表的通信协议。尽管两者在很多方面相似,它们之间还是存在一些差异。
DLT645-1997与DLT645-2007之间的主要区别

  • 控制码不同:DLT645-1997和DLT645-2007的控制码有所不同。例如,在DLT645-1997中,控制码为0x01(发送)和0x81(接收),而在DLT645-2007中,相应的控制码为0x11(发送)和0x91(接收)。

  • 标识码不同:在DLT645-1997中,用两个字节四个字段来区分不同的数据项。而在DLT645-2007中,用四个字节来区分不同的数据项。

  • 前导码不同:DLT645-2007在传输帧信息前,一般需要有4个0xFE作为前导字节,而DLT645-1997发送1-4个字节0xFE作为前导字节

    实际上,不同厂家不同型号的表前导码FE的个数是不同的,还有些厂家不会发送前导码FE,我们解析接收数据的一般方法是忽略前面N个FE,寻找数据帧中的68帧头作为一帧的起始位置

二、DLT645通信规约报文解析

1.DLT645-1997通信规约报文解析

读取当前反向有功总电量(发电量)–请求帧

帧起始符地址域帧起始符控制码数据长度域数据域校验码结束符
6861 45 69 00 00 0068010253 C3 0116
  • 68:标识一帧信息的开始

  • 61 45 69 00 00 00 :电表地址,翻转后00 00 00 69 45 61,得到实际电表地址694561

  • 01:对应格式为00000001,即主站发出的命令帧、从站正确应答、无后续数据帧,且读数据

  • 02:数据域的字节数为2

  • 53 C3:数据项标识部分,这一部分先传送低位字节,后传送高位字节,传输时发送方按字节进行加33H处理,接收方按字节进行减33H处理

    `53 C3`  --减33H--> `20 90`   --逆序-->  `90 20` ---> `10010000 00100000`
    

    对应于当前反向有功总电能

  • F8:它的值是它前面从第一个68起始符开始到最后一字节数据段的单字节累加和(单字节累加,忽略溢出)
    iterators

  • 16:标识一帧信息的结束

读取当前反向有功总电量(发电量)–返回帧

帧起始符地址域帧起始符控制码数据长度域数据域校验码结束符
6861 45 69 00 00 0068810653 C3 33 78 34 66CE16
  • 68:标识一帧信息的开始

  • 61 45 69 00 00 00 :电表地址,翻转后00 00 00 69 45 61,得到实际电表地址694561

  • 81:对应格式为10000001`,即从站发出的应答帧、从站正确应答、无后续数据帧,且读数据

  • 06:数据域的字节数为6

  • 53 C3:数据项标识部分,这一部分先传送低位字节,后传送高位字节,传输时发送方按字节进行加33H处理,接收方按字节进行减33H处理

    `53 C3`  --减33H--> `20 90`   --逆序-->  `90 20` ---> `10010000 00100000`
    

    对应于当前反向有功总电能

  • 33 78 34 66 :数据项对应的数值部分

    处理方法:统一减去33H,再翻转,在指定位置加入小数点,即可得到当前正向有功电能(电量)

    33 78 34 66   --------减33-------->  00 45 01 33
    00 45 01 33   --------反转-------->  33 01 45 00
    33 01 45 00   ------加小数点----->  33 01 45.00
    

    结果就是:330145.00 kWh (电表总反相有功电能量,也就是“发电量”。耗电量是“正相有功电能量”)

  • CE:它的值是它前面从第一个68起始符开始到最后一字节数据段的单字节累加和(单字节累加,忽略溢出)
    iterators

  • 16:标识一帧信息的结束

2.DLT645-2007通信规约报文解析

读取当前正向有功总电量(耗电量)–请求帧

帧起始符地址域帧起始符控制码数据长度域数据域校验码结束符
68AA AA AA AA AA AA68110433 33 34 33AE16
  • 68:标识一帧信息的开始

  • AA AA AA AA AA AA :地址域支持缩位寻址,即从若干低位起,剩余高位补AAH作为通配符进行读表操作

  • 11:对应格式为00010001,即主站发出的命令帧、从站正确应答、无后续数据帧,且读数据

  • 04:数据域的字节数为4

  • 33 33 34 33:数据项标识部分,这一部分先传送低位字节,后传送高位字节,传输时发送方按字节进行加33H处理,接收方按字节进行减33H处理

    `33 33 34 33`  --减33H--> `00 00 01 00`   --逆序-->  `00 01 00 00`
    

    对应于当前正向有功总电能
    在这里插入图片描述

  • AE:它的值是它前面从第一个68起始符开始到最后一字节数据段的单字节累加和(单字节累加,忽略溢出)
    iterators

  • 16:标识一帧信息的结束

读取当前正向有功总电量(耗电量)–返回帧

帧起始符地址域帧起始符控制码数据长度域数据域校验码结束符
6872 00 32 09 17 2068910833 33 34 33 B9 34 33 33 6D16
  • 68:标识一帧信息的开始

  • 72 00 32 09 17 20 :表号字段,翻转后20 17 09 32 00 72,得到实际表号
    在这里插入图片描述

  • 91:对应格式为10010001,即从站发出的应答帧、从站正确应答、无后续数据帧,且读数据

  • 08:数据域的字节数为8

  • 33 33 34 33:数据项标识部分,这一部分先传送低位字节,后传送高位字节,传输时发送方按字节进行加33H处理,接收方按字节进行减33H处理

    `33 33 34 33`  --减33H--> `00 00 01 00`   --逆序-->  `00 01 00 00`
    

    对应于当前正向有功总电能
    在这里插入图片描述

  • B9 34 33 33 :数据项对应的数值部分
    处理方法:统一减去33H,再翻转,在指定位置加入小数点,即可得到当前正向有功电能(电量)

    B9 34 33 33   --------减33-------->  86 01 00 00 
    86 01 00 00   --------反转-------->  00 00 01 86 
    00 00 01 86   ------加小数点----->   00 00 01.86 
    

    小数点位置由DLT645-2007通信规约决定
    在这里插入图片描述

  • 6E:它的值是它前面从第一个68起始符开始到最后一字节数据段的单字节累加和(单字节累加,忽略溢出)
    iterators

  • 16:标识一帧信息的结束

三、C++代码组织报文与解析报文

  • 首先,从数据库表中获取规约的编号,如:ptl_645_serial_cj

  • 然后,加载规约动态库,得到该规约对应的组织数据与解析数据的函数

  • 通道通信发送数据处理
    调用规约报文组织函数组织报文

    PTL_API void buildData(S_RecvSendData &sRecvSendData, CUnitBase* pUnit)  //组织报文
    {
        S_PtlVariant sVariant;
        memset(&sVariant, 0, sizeof(S_PtlVariant));
        if(pUnit != NULL)
        {
            sVariant.pUnit = pUnit;
            sVariant.pBySendBuffer = sRecvSendData.pSendBuf;
            sVariant.uwSendDataLen = sRecvSendData.uiSendLen;
            sVariant.bMainChannel = sRecvSendData.bMainChannel;
            sVariant.byChannelMode = sRecvSendData.byChannelMode;
            sVariant.byChannelIndex = sRecvSendData.byChannelIndex;
            sVariant.pPtlFlag = (S_PtlFlag*)(pUnit->getTUFlagBuffer());  // 获取规约标志位使用的缓冲区
    
            sendFrame(sVariant); //组织数据
            sRecvSendData.uiSendLen = sVariant.uwSendDataLen;
        }
    }
    

    基于数据库表

    • 通讯装置表中的地址,确定报文帧的地址域
      enum E_COLID_TU //通讯装置表
      {
          COL_TU_ID = 1, //主键ID
          COL_TU_TUNAME, //装置名称
          COL_TU_TUMODELID, //装置型号
          COL_TU_TUMODELSN, //装置编号
          COL_TU_TUTYPE, //装置类型
          COL_TU_CHANNELGROUPID, //通道组ID
          COL_TU_TUADDR, //装置通讯地址
          COL_TU_LOCALADDR, //装置通讯地址
          COL_TU_TUADDR6, //地址6
          COL_TU_TUADDR5, //地址5
          COL_TU_TUADDR4, //地址4
          COL_TU_TUADDR3, //地址3
          COL_TU_TUADDR2, //地址2
          COL_TU_TUADDR1, //地址1
          COL_TU_TUADDR0, //地址0
          COL_TU_UPCOMSTATE, //上行通讯状态
          COL_TU_DOWNCOMSTATE, //下行通讯状态
          COL_TU_YKCMDTIMEOUT, //遥控命令超时时间
          COL_TU_DZCMDTIMEOUT, //定值命令超时时间
          COL_TU_COMTRADETIMEOUT, //故障滤波超时时间
          COL_TU_OTHRTCMDTIMEOUT, //其他命令超时时间
          COL_TU_byISPublicZFTable, //是否共用转发表
          COL_TU_uiPublicZFTUID,  //共用转发装置
          COL_TU_uiRelationReturnYK,//关联复归遥控
          COL_TU_MQTTTUID,    //MQTT设备ID
      
          COL_TU_MAX
      };
      
    • 645规约表确定规约版本与前导字节数
      struct      S_Ptl645 //645规约表
      {
          quint32     uiID; //主键ID
          quint32     uiPtlID; //通讯规约ID
          quint8      byVersion; //规约版本
          quint32     uiPreByteNum; //前导字节数
          quint32     uiPollingInterval; //轮询时间间隔
          bool       bIfCheckTime; //是否启用校时
      
          S_Ptl645()
          {
              memset(this, 0, sizeof(S_Ptl645));
          }
      
      }ALIGNS;
      
    • 645数据信息表确定控制码、数据表示、数据长度等
      struct      S_Ptl645Data //645数据信息表
      {
          quint32     uiID; //主键ID
          quint32     uiTUModelID; //装置型号
          quint32     uiDataIndex; //数据点号,对应遥测遥信遥脉等点位的序号
          quint32     uiDICID; //数据标识ID;
          quint8      byCTRLCode; //控制码
          quint8      byDI3; //DI3
          quint8      byDI2; //DI2
          quint8      byDI1; //DI1
          quint8      byDI0; //DI0
          quint8      byLength; //数据长度
          char        chDataName[SIZE_NAME]; //数据名称
      
          S_Ptl645Data()
          {
              memset(this, 0, sizeof(S_Ptl645Data));
          }
      
      }ALIGNS;
      

    组织97与07报文帧

    struct S_Sendframe07
    {
        quint8  byStart1; //启动字符
        quint8  byA0; //地址域 每字节2位BCD码
        quint8  byA1; //地址域
        quint8  byA2; //地址域
        quint8  byA3; //地址域
        quint8  byA4; //地址域
        quint8  byA5; //地址域
        quint8  byStart2; //启动字符
        quint8  bycontrol; //功能码
        quint8  byDataLen; //数据域
        quint8  byDI0; //数据标识
        quint8  byDI1; //数据标识
        quint8  byDI2; //数据标识
        quint8  byDI3; //数据标识
        quint8  byCS; //校验码
        quint8  byEnd; //结束字符
    
        S_Sendframe07()
        {
            memset(this, 0, sizeof (S_Sendframe07));
            byStart1 = 0x68;
            byStart2 = 0x68;
            byEnd = 0x16;
        }
    }ALIGNS;
    
    
    
    struct S_Sendframe97
    {
        quint8  byStart1; //启动字符
        quint8  byA0; //地址域 每字节2位BCD码
        quint8  byA1; //地址域
        quint8  byA2; //地址域
        quint8  byA3; //地址域
        quint8  byA4; //地址域
        quint8  byA5; //地址域
        quint8  byStart2; //启动字符
        quint8  bycontrol; //功能码
        quint8  byDataLen; //数据域
        quint8  byDI0; //数据标识
        quint8  byDI1; //数据标识
        quint8  byCS; //校验码
        quint8  byEnd; //结束字符
    
        S_Sendframe97()
        {
            memset(this, 0, sizeof (S_Sendframe97));
            byStart1 = 0x68;
            byStart2 = 0x68;
            byEnd = 0x16;
        }
    }ALIGNS;
    
    void buildData(S_PtlVariant &sVariant)
    {
        quint8* pBySendBuffer = sVariant.pBySendBuffer + sVariant.uwSendDataLen;
        quint8* pSumCRC = pBySendBuffer;
        S_Sendframe07 sSendFrame07;
        S_Sendframe97 sSendFrame97;
        quint8 byA0 = 0;
        quint8 byA1 = 0;
        quint8 byA2 = 0;
        quint8 byA3 = 0;
        quint8 byA4 = 0;
        quint8 byA5 = 0;
    
        quint32 uiID = sVariant.pUnit->getTUID();
        sVariant.pUnit->getValue(S_DataID(TABLE_TU, uiID, COL_TU_TUADDR0), &byA0);
        sVariant.pUnit->getValue(S_DataID(TABLE_TU, uiID, COL_TU_TUADDR1), &byA1);
        sVariant.pUnit->getValue(S_DataID(TABLE_TU, uiID, COL_TU_TUADDR2), &byA2);
        sVariant.pUnit->getValue(S_DataID(TABLE_TU, uiID, COL_TU_TUADDR3), &byA3);
        sVariant.pUnit->getValue(S_DataID(TABLE_TU, uiID, COL_TU_TUADDR4), &byA4);
        sVariant.pUnit->getValue(S_DataID(TABLE_TU, uiID, COL_TU_TUADDR5), &byA5);
    
        sSendFrame07.byA0 = valueToBCD(byA0);
        sSendFrame07.byA1 = valueToBCD(byA1);
        sSendFrame07.byA2 = valueToBCD(byA2);
        sSendFrame07.byA3 = valueToBCD(byA3);
        sSendFrame07.byA4 = valueToBCD(byA4);
        sSendFrame07.byA5 = valueToBCD(byA5);
    
        sSendFrame97.byA0 = valueToBCD(byA0);
        sSendFrame97.byA1 = valueToBCD(byA1);
        sSendFrame97.byA2 = valueToBCD(byA2);
        sSendFrame97.byA3 = valueToBCD(byA3);
        sSendFrame97.byA4 = valueToBCD(byA4);
        sSendFrame97.byA5 = valueToBCD(byA5);
       
    	
        if(PTL645_VERSION_97 == sVariant.pPtlFlag->pSPtl645->pPtl645->byVersion)
        { 
            if(sVariant.pPtlFlag->byQueryNum >= sVariant.pPtlFlag->pSPtl645->pPtl645DICVec.size())
            {
                sVariant.pPtlFlag->byQueryNum = 0;
            }
            sSendFrame97.bycontrol = sVariant.pPtlFlag->pSPtl645->pPtl645DICVec.at(sVariant.pPtlFlag->byQueryNum)->byCTRLCode;
            sSendFrame97.byDataLen = 2; /*sVariant.pPtlFlag->pSPtl645->pPtl645DICVec.at(sVariant.pPtlFlag->byQueryNum)->byLength;*/
            sSendFrame97.byDI0 = sVariant.pPtlFlag->pSPtl645->pPtl645DICVec.at(sVariant.pPtlFlag->byQueryNum)->byDI0 + 0x33;
            sSendFrame97.byDI1 = sVariant.pPtlFlag->pSPtl645->pPtl645DICVec.at(sVariant.pPtlFlag->byQueryNum)->byDI1 + 0x33;
            sVariant.pPtlFlag->byQueryNum++;
    		memcpy(pSumCRC, &sSendFrame97, 12);
            sSendFrame97.byCS = getCheckCumlationSum(pSumCRC, 12);
    		memcpy(pBySendBuffer, &sSendFrame97, sizeof(S_Sendframe97));
    		sVariant.uwSendDataLen += sizeof(S_Sendframe97);
    		
        }
        else
        {
            if(sVariant.pPtlFlag->byQueryNum >= sVariant.pPtlFlag->pSPtl645->pPtl645DICVec.size())
            {
                sVariant.pPtlFlag->byQueryNum = 0;
            }
    
            sSendFrame07.bycontrol = sVariant.pPtlFlag->pSPtl645->pPtl645DICVec.at(sVariant.pPtlFlag->byQueryNum)->byCTRLCode;
            sSendFrame07.byDataLen = sVariant.pPtlFlag->pSPtl645->pPtl645DICVec.at(sVariant.pPtlFlag->byQueryNum)->byLength;
            sSendFrame07.byDI0 = sVariant.pPtlFlag->pSPtl645->pPtl645DICVec.at(sVariant.pPtlFlag->byQueryNum)->byDI0 + 0x33;
            sSendFrame07.byDI1 = sVariant.pPtlFlag->pSPtl645->pPtl645DICVec.at(sVariant.pPtlFlag->byQueryNum)->byDI1 + 0x33;
            sSendFrame07.byDI2 = sVariant.pPtlFlag->pSPtl645->pPtl645DICVec.at(sVariant.pPtlFlag->byQueryNum)->byDI2 + 0x33;
            sSendFrame07.byDI3 = sVariant.pPtlFlag->pSPtl645->pPtl645DICVec.at(sVariant.pPtlFlag->byQueryNum)->byDI3 + 0x33;
            sVariant.pPtlFlag->byQueryNum++;
            memcpy(pSumCRC, &sSendFrame07, 14);
            sSendFrame07.byCS = getCheckCumlationSum(pBySendBuffer, 14);
            memcpy(pBySendBuffer, &sSendFrame07, sizeof (S_Sendframe07));
            sVariant.uwSendDataLen += sizeof (S_Sendframe07);
        }
    
        sVariant.pPtlFlag->bWaitDataReturn = 1;
        sVariant.pPtlFlag->t_LastSendTime = qGetCurrentTime();;
    }
    

    组织好报文后,基于通道通讯类型,分别调用串口或网络的发送数据接口

    switch(m_pChannel->byChannelCOMType)
    {
        case CHANNEL_TYPE_TCP_CLIENT:
        case CHANNEL_TYPE_TCP_SERVER:
        case CHANNEL_TYPE_UDP_CLIENT:
        case CHANNEL_TYPE_UDP_SERVER:
            m_SocketMutex.lock();
            m_SPTLRecvSenddata.iSendedLen = m_pNetComm->sendData(m_pSocketHandle, m_SPTLRecvSenddata.pSendBuf, m_SPTLRecvSenddata.uiSendLen);
            m_SocketMutex.unlock();
            if(m_SPTLRecvSenddata.iSendedLen > 0)
            {
                if(m_qsPtlLibName.contains("_zf"))
                {
                    //ljx debug
                    qMSleep(50);
                }
                else
                {
                    //                qMSleep(30);
                }
            }
            break;
        case CHANNEL_TYPE_COM_485:
        case CHANNEL_TYPE_COM_232:
            {
                m_SPTLRecvSenddata.iSendedLen = m_pSerial->sendData(m_SPTLRecvSenddata.pSendBuf,static_cast<int>(m_SPTLRecvSenddata.uiSendLen));
            }
            break;
        case CHANNEL_TYPE_CAN:
    
            break;
    
        default:
            break;
    }
    
  • 通道通信接收数据处理
    基于通道类型,采用不同的处理方法将接收到的数据存放到接收缓存区中

    switch(m_pChannel->byChannelCOMType)   //通道类型
    {
        case CHANNEL_TYPE_TCP_CLIENT:
        case CHANNEL_TYPE_TCP_SERVER:
        case CHANNEL_TYPE_UDP_CLIENT:
        case CHANNEL_TYPE_UDP_SERVER:
            m_NewRecvedDataMutex.lock();
            while(m_sNewRecvedDataList.size() > 0)
            {
                S_NewRecvedData& sNewRecvedData = m_sNewRecvedDataList.first();
                quint32 uiFreeRecvBufLen = MAX_BUF_SIZE - m_SPTLRecvSenddata.uiRecvedLen;   //剩余的接收缓冲区长度
                if(uiFreeRecvBufLen >= sNewRecvedData.uiRecvedLen)  //若剩余的缓冲区长度大于新接收的缓冲区长度,则新接收到的数据全部复制 
                {
                    memcpy(m_SPTLRecvSenddata.pRecvBuf + m_SPTLRecvSenddata.uiRecvedLen, sNewRecvedData.pRecvBuf, sNewRecvedData.uiRecvedLen);
                    m_SPTLRecvSenddata.uiRecvedLen += sNewRecvedData.uiRecvedLen;
                    m_sNewRecvedDataList.removeFirst();
                }
                else if(uiFreeRecvBufLen > 0)  //若剩余的接收缓冲区长度大于0,则只复制剩余的接收缓冲区长度
                {
                    memcpy(m_SPTLRecvSenddata.pRecvBuf + m_SPTLRecvSenddata.uiRecvedLen, sNewRecvedData.pRecvBuf, uiFreeRecvBufLen);
                    m_SPTLRecvSenddata.uiRecvedLen += uiFreeRecvBufLen;
                    sNewRecvedData.uiRecvedLen -= uiFreeRecvBufLen;
                    memmove(sNewRecvedData.pRecvBuf, sNewRecvedData.pRecvBuf + uiFreeRecvBufLen, sNewRecvedData.uiRecvedLen);
                    break;
                }
                else
                {
                    break;
                }
            }
            m_NewRecvedDataMutex.unlock();
            break;
        case CHANNEL_TYPE_COM_485:
        case CHANNEL_TYPE_COM_232:
            m_uiMaxRecvLen = MAX_BUF_SIZE - m_SPTLRecvSenddata.uiRecvedLen;
            if(m_uiMaxRecvLen > 0)
            {
                m_SPTLRecvSenddata.iRecvedLen = m_pSerial->recvData(m_SPTLRecvSenddata.pRecvBuf + m_SPTLRecvSenddata.uiRecvedLen,
                                                                    static_cast<int>(m_uiMaxRecvLen));  //从串口中接收数据
                if(m_SPTLRecvSenddata.iRecvedLen > 0)
                {
                    m_tRecvedTime = time(NULL);
                    addCommMsg(true, static_cast<quint16>(m_SPTLRecvSenddata.iRecvedLen), m_SPTLRecvSenddata.pRecvBuf + m_SPTLRecvSenddata.uiRecvedLen);  //添加通信报文
                    setUpCommFault(false);
                    setDownCommFault(false);
                    m_SPTLRecvSenddata.uiRecvedLen += static_cast<quint32>(m_SPTLRecvSenddata.iRecvedLen);
                }
                else if(m_SPTLRecvSenddata.iRecvedLen < 0)
                {
                    setCommState(COMM_ERROR);
                }
            }
            break;
        case CHANNEL_TYPE_CAN:
    
            break;
    
        default:
            break;
    }
    

    调用规约报文解析函数来解析报文

    PTL_API bool parseData(S_RecvSendData &sRecvSendData, CUnitBase* pUnit) //解析报文
    {
        S_PtlVariant sVariant;
        memset(&sVariant, 0, sizeof(S_PtlVariant));
        if(pUnit != NULL)
        {
            sVariant.pUnit = pUnit;
            sVariant.pByReadBuffer = sRecvSendData.pRecvBuf;
            sVariant.uwRecvDataLen = sRecvSendData.uiRecvedLen;
            sVariant.bMainChannel = sRecvSendData.bMainChannel;
            sVariant.byChannelMode = sRecvSendData.byChannelMode;
            sVariant.byChannelIndex = sRecvSendData.byChannelIndex;
            memset(sVariant.pParsedCommMsg, 0, MAX_BUF_SIZE);
            sVariant.uwParsedCommMsgLen = 0;
            sVariant.pPtlFlag = (S_PtlFlag*)(pUnit->getTUFlagBuffer());
    
            if(readFrame(sVariant)) //解析数据
            {
                sRecvSendData.uiRecvedLen = sVariant.uwRecvDataLen;
                return true;
            }
        }
        sRecvSendData.uiRecvedLen = sVariant.uwRecvDataLen;
        return false;
    }
    

    基于 控制码调用不同的解析函数。解析时,首先计算得到数据标识码,条件判断调用电度量、最大需量还是遥测量的解析函数

    void ParseTUBackFrame(S_PtlVariant &sVariant)
    {
        if(NULL == sVariant.pPtlFlag->pSPtl645->pPtl645)
        {
            return;
        }
        int Ret = RECV_PROCRET_NO;
        //检测校验和
        quint8 byCtlCode = sVariant.pByReadBuffer[8]; //控制码
        quint8 byDataLen = sVariant.pByReadBuffer[9]; //数据长度
        quint8 byCS = getCheckCumlationSum(sVariant.pByReadBuffer, byDataLen + 10);
        if(byCS != sVariant.pByReadBuffer[byDataLen + 10])
        {
            return;
        }
    
        if(sVariant.pPtlFlag->pSPtl645->pPtl645->byVersion == PTL645_VERSION_07)
        {
            //获取数据标识码、检测数据标识
            quint8 byDI0 = sVariant.pByReadBuffer[10] - 0x33;
            quint8 byDI1 = sVariant.pByReadBuffer[11] - 0x33;
            quint8 byDI2 = sVariant.pByReadBuffer[12] - 0x33;
            quint8 byDI3 = sVariant.pByReadBuffer[13] - 0x33;
    //        for(int i = 0; i < sVariant.pPtlFlag->pSPtl645->pPtl645DataVec.size(); ++i)
    //        {
                if(byDI0 != sVariant.pPtlFlag->pSPtl645->pPtl645DICVec.at(sVariant.pPtlFlag->byQueryNum - 1 )->byDI0 ||
                   byDI1 != sVariant.pPtlFlag->pSPtl645->pPtl645DICVec.at(sVariant.pPtlFlag->byQueryNum - 1 )->byDI1 ||
                   byDI2 != sVariant.pPtlFlag->pSPtl645->pPtl645DICVec.at(sVariant.pPtlFlag->byQueryNum - 1 )->byDI2 ||
                   byDI3 != sVariant.pPtlFlag->pSPtl645->pPtl645DICVec.at(sVariant.pPtlFlag->byQueryNum - 1 )->byDI3)
                {
                    return;
                }
    
                //检验控制字
                if(0x91 == byCtlCode|| 0x92 == byCtlCode || 0xB1 == byCtlCode || 0xB2 == byCtlCode)
                {
                    switch (byDI3)
                    {
                    case 0x00: // 电度量
                        ParseDDData(sVariant, sVariant.pByReadBuffer + FRAME_DATA_START, sVariant.pPtlFlag->pSPtl645->pPtl645DataVec.at(sVariant.pPtlFlag->byQueryNum -1));
                        Ret = RECV_PROCRET_DDFRAME;
                        break;
                    case 0x01: //最大需量
                        ParseMDData(sVariant, sVariant.pByReadBuffer + FRAME_DATA_START, sVariant.pPtlFlag->pSPtl645->pPtl645DataVec.at(sVariant.pPtlFlag->byQueryNum -1));
                        Ret = RECV_PROCRET_MAXDATA;
                        break;
                    case 0x02: //遥测量
                        ParseYCData(sVariant, sVariant.pByReadBuffer + FRAME_DATA_START, sVariant.pPtlFlag->pSPtl645->pPtl645DataVec.at(sVariant.pPtlFlag->byQueryNum -1));
                        Ret = RECV_PROCRET_YCFRAME;
                        break;
                    default:
                        Ret = RECV_PROCRET_NORMAL;
                        break;
                    }
                    sVariant.pPtlFlag->uwSearchSyncTimes = 0;
                }
                else if (byCtlCode == 0xD1 || byCtlCode == 0xD2) //返回异常
                {
                    sVariant.pPtlFlag->uwSearchSyncTimes++;
                    Ret = RECV_PROCRET_ABNORMAL;
                }
                Ret = RECV_PROCRET_NORMAL;
    //        }
        }
        else if (PTL645_VERSION_97 == sVariant.pPtlFlag->pSPtl645->pPtl645->byVersion)
        {
            //获取数据标识码、检测数据标识
            quint8 byDI0 = sVariant.pByReadBuffer[10] - 0x33;
            quint8 byDI1 = sVariant.pByReadBuffer[11] - 0x33;
    		
    //        for(int i = 0; i < sVariant.pPtlFlag->pSPtl645->pPtl645DataVec.size(); ++i)
    //        {
                if(byDI0 != sVariant.pPtlFlag->pSPtl645->pPtl645DICVec.at(sVariant.pPtlFlag->byQueryNum - 1)->byDI0
                    || byDI1 != sVariant.pPtlFlag->pSPtl645->pPtl645DICVec.at(sVariant.pPtlFlag->byQueryNum -1)->byDI1)
                {
                    return;
                }
                //检验控制字
                if(0x81 == byCtlCode  || 0x82 == byCtlCode)
                {
                    quint8 byCtl1 = byDI1 >> 4;
                    switch (byCtl1)
                    {
                    case 0x09:  //电度量
                        ParseDDData(sVariant, sVariant.pByReadBuffer + FRAME_DATA_START -2, sVariant.pPtlFlag->pSPtl645->pPtl645DataVec.at(sVariant.pPtlFlag->byQueryNum -1));
                        Ret = RECV_PROCRET_DDFRAME;
                        break;
                    case 0x0a:
                        ParseMDData(sVariant, sVariant.pByReadBuffer + FRAME_DATA_START - 2, sVariant.pPtlFlag->pSPtl645->pPtl645DataVec.at(sVariant.pPtlFlag->byQueryNum -1));
                        Ret = RECV_PROCRET_MAXDATA;
                        break;
                    case 0x0b: // 遥测量
                    {
                        if(0xb6 == byDI1)
                        {
                            ParseYCData(sVariant, sVariant.pByReadBuffer + FRAME_DATA_START - 2, sVariant.pPtlFlag->pSPtl645->pPtl645DataVec.at(sVariant.pPtlFlag->byQueryNum -1));
                            Ret = RECV_PROCRET_YCFRAME;
                        }
                    }
                        break;
                    default: // 对于其它类型的查询暂不处理
                        Ret = RECV_PROCRET_NORMAL;
                        break;
                    }
    
                    sVariant.pPtlFlag->uwSearchSyncTimes = 0;
    
                }
    
                else if (byCtlCode == 0xc1 || byCtlCode == 0xc2) //返回异常
                {
                    sVariant.pPtlFlag->uwSearchSyncTimes++;
                    Ret = RECV_PROCRET_ABNORMAL;
    
                }
                else
                {
                    Ret = RECV_PROCRET_NORMAL;
                }
        }
    
    }
    

    解析电度量帧

    void ParseDDData(S_PtlVariant &sVariant, quint8 *pbyData, S_Ptl645Data *PInfoData)
    {
        quint64 ullDDData = 0;
        double dDDData = 0.0;
        quint8 byTempLength = 0;
        sVariant.pUnit->getValue(S_DataID(TABLE_PTL645DIC, PInfoData->uiDICID, COL_PTL645DIC_LENGTH), &byTempLength);
        ullDDData = ParseRecvData(pbyData, byTempLength);
        dDDData = ullDDData / 1.0;
        bool bIsCalc = false;
    
        sVariant.pUnit->getValue(S_DataIndex(TABLE_PREYM, PInfoData->uiDataIndex, COL_PREYM_ISCALC), &bIsCalc);
        if(!bIsCalc)
        {
            sVariant.pUnit->setValue(S_DataIndex(TABLE_PREYM, PInfoData->uiDataIndex, COL_PREYM_YMVALUE),&dDDData);
        }
    }
    

    解最需量帧

    //解最大需量帧
    void ParseMDData(S_PtlVariant &sVariant, quint8 *pbyData, S_Ptl645Data *PInfoData)
    {
        qDebug()<<"jinru jiexi zuida xliang";
        quint64 ullDdData = 0;
    
        double fMDdata = 0.0;
        ullDdData = ParseRecvData(pbyData, 3);
        qDebug()<<"jirxiwancehng1111";
        fMDdata = (float) (ullDdData / 1.0);
        bool bIsCalc = false;
    
        sVariant.pUnit->getValue(S_DataIndex(TABLE_PREYM, PInfoData->uiDataIndex, COL_PREYM_ISCALC), &bIsCalc);
        if(!bIsCalc)
        {
             qDebug()<<"22222222222222222222222"<<PInfoData->uiDataIndex;
            sVariant.pUnit->setValue(S_DataIndex(TABLE_PREYM, PInfoData->uiDataIndex, COL_PREYM_YMVALUE), &fMDdata);
            qDebug()<<"3333333333333333";
        }
    }
    

    解析遥测帧

    //解析遥测帧
    void ParseYCData(S_PtlVariant &sVariant, quint8 *pbyData, S_Ptl645Data *PInfoData)
    {
        quint64 ullYCData = 0;
        double dYCData = 0.0;
        
        quint8 byTempLength = 0;
        sVariant.pUnit->getValue(S_DataID(TABLE_PTL645DIC, PInfoData->uiDICID, COL_PTL645DIC_LENGTH), &byTempLength);
        ullYCData = ParseRecvData(pbyData, byTempLength);
    	dYCData = (double)(ullYCData / 1.0);
        
        bool bIsCalc = false;
        sVariant.pUnit->getValue(S_DataIndex(TABLE_PREYC, PInfoData->uiDataIndex, COL_PREYC_ISCALC), &bIsCalc);
        if(!bIsCalc)
        {
            sVariant.pUnit->setValue(S_DataIndex(TABLE_PREYC, PInfoData->uiDataIndex, COL_PREYC_YCVALUE), &dYCData);
        }
    
    }
    

    将解帧所得参变量的字符值转换成十进制原始值

    //将解帧所得参变量的字符值转换成十进制原始值
    quint64 ParseRecvData(quint8 *pbyData, int nlen)
    {
        quint64 ullTmpData = 0;
        quint8 byDataBuffer[256] = "0";
        quint8 byTempData = 0;
        for(int i = 0; i < nlen; ++i)
        {
            byDataBuffer[i] = pbyData[i] - 0x33;
            byTempData = pbyData[i] - 0x33;
        }
    
        switch (nlen)
        {
        case 0x02: //每个信息占两个字节
            ullTmpData = TwoBytesToUShort(byDataBuffer);
            break;
    
        case 0x03: //每个信息占三个字节
            ullTmpData = ThreeByteToLong(byDataBuffer);
            break;
    
        case 0x04: //每个信息占4个字节
            ullTmpData = FourBytesToLong(byDataBuffer);
            break;
        default:
            break;
        }
    
        return ullTmpData;
    }
    

参考文献:

  • DLT645-97/07通信规约
  • DLT645协议解析(二)—07协议数据帧结构解析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1690341.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode 131题详解:高效分割回文串的递归与动态规划方法

❤️❤️❤️ 欢迎来到我的博客。希望您能在这里找到既有价值又有趣的内容&#xff0c;和我一起探索、学习和成长。欢迎评论区畅所欲言、享受知识的乐趣&#xff01; 推荐&#xff1a;数据分析螺丝钉的首页 格物致知 终身学习 期待您的关注 导航&#xff1a; LeetCode解锁100…

每日一题(1)

在看一本08年出版的书的时候&#xff0c;看到了这样一个问题&#xff0c;感觉答案很奇怪&#xff1a; public class demo_p22 {public static void main(String args[]){int sCook1,sFish2;//各技能标记character ch1new character();if(ch1.haveSkill(sCook))System.out.print…

大数据量上传FTP

背景 笔者有一个需求是把将近一亿条数据上传到FTP服务器中&#xff0c;这些数据目前是存储在mysql中&#xff0c;是通过关联几张表查询出来的&#xff0c;查询出来的数据结果集一共是6个字段。要求传输的时候拆分成一个个小文件&#xff0c;每个文件大小不能超过500M。我的测试…

谷歌蜘蛛池是什么?

或称为谷歌爬虫池&#xff0c;是一项专门针对谷歌搜索引擎优化&#xff08;SEO&#xff09;的先进技术&#xff0c;这种技术的主要目的是通过建立庞大的网站群体和复杂的链接结构来吸引和维持谷歌的爬虫程序的注意力&#xff0c;其核心是通过这种结构优化&#xff0c;增强特定网…

使用B2M 算法批量将可执行文件转为灰度图像

参考论文 基于二进制文件的 C 语言编译器特征提取及识别 本实验使用 B2M 算法将可执行文件转为灰度图像&#xff0c;可执行文件转为灰度图的流程如图 4-3 所示。将 可执行文件每 8 位读取为一个无符号的的整型常量&#xff0c;一个可执行文件得到一个一维向量&#xff0c; …

mac版本Phpstudy本地环境安装Discuz教程【2024】

此方法适用于m1版本的mac版本Phpstudy本地环境安装Discuz&#xff0c;当然同样使用更高版本的mac端。网上各种安装教程参差不齐&#xff0c;根本解决不了小白的入门需求&#xff0c;以下是最新且直接明了的安装教程。 Phpstudy本地环境安装Discuz教程&#xff1a; 1、安装Phps…

1075: 求最小生成树(Prim算法)

解法&#xff1a; 总结起来&#xff0c;Prim算法的核心思想是从一个顶点开始&#xff0c;一步一步地选择与当前最小生成树相邻的且权值最小的边&#xff0c;直到覆盖所有的顶点&#xff0c;形成一个最小生成树。 #include<iostream> #include<vector> using names…

【管理篇 / 升级】❀ 14. FortiOS 7.4固件升级新规后的7.4.4版本的升级 ❀ FortiGate 防火墙

【简介】相信看过上一篇文章的都已经知道了&#xff0c;FortiOS 7.4版本的固件升级和降级都增加了条件&#xff0c;那就是需要设备在服务期内&#xff0c;但是小版本的升级是允许的&#xff0c;例如7.4.0升级到7.4.1、7.4.2&#xff0c;现在最新版本7.4.4来了。 获得FortiOS 7.…

【C++】牛客——OR64 求和

✨题目链接&#xff1a; OR64 求和 ✨题目描述 输入两个整数 n 和 m&#xff0c;从数列1&#xff0c;2&#xff0c;3.......n 中随意取几个数,使其和等于 m ,要求将其中所有的可能组合列出来 ✨输入描述: 每个测试输入包含2个整数,n和m ✨输出描述: 按每个组合的字典序排列…

【系统架构师】-案例篇(十五)SOA、微服务与数据库

1、可复用构件应具备哪些属性 可用性&#xff1a;构件必须易于理解和使用。 质量&#xff1a;构件及其变形必须能正确工作。 适应性&#xff1a;构件应该易于通过参数化等方式在不同语境中进行配置。 可移植性&#xff1a;构件应能在不同的硬件运行平台和软件环境中工作。 可变…

数组-区间合并

一、题目描述 二、题目思路 这里提供满足基本要求的解题思路&#xff1a; 1.先对列表内按照start大小升序排序&#xff0c;这里创建Comparator接口的实现类&#xff0c;重写compare方法。 2.遍历intervals&#xff0c;设置laststart、lastend两个变量与当前区间相比较&#…

LVGL显示中文字体及其它语言文字详细笔记教程

在前面几篇博客文章中&#xff0c;已经对LVGL的移植和使用进行了较为详细的介绍&#xff0c;在本文中将讲解如何在LVGL的控件中显示中文字体及其它语言字体。 LVGL在VScode中安装模拟器运行配置笔记教程_vscode lvgl-CSDN博客 LVGL移植到STM32 MCU平台详细经验笔记教程-CSDN博…

Day36 代码随想录打卡|二叉树篇---翻转二叉树

题目&#xff08;leecode T226&#xff09;&#xff1a; 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 方法&#xff1a; 迭代法 翻转二叉树&#xff0c;即从根节点开始&#xff0c;一一交换每个节点的左右孩子节点&#xff0c;然后…

【Linux】-Zookeeper安装部署[17]

简介 apache ZooKeeper是一个分布式的&#xff0c;开放源码的分布式应用程序协调服务&#xff0c;是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件&#xff0c;提供的功能包括&#xff1a;配置维护、域名服务、分布式同步、组服务等。 除了为Hadoop和H…

程序员做推广?我劝你别干

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 这是卢松松会员专区&#xff0c;一位会员朋友的咨询&#xff0c;如果你也有自研产品&#xff0c;但不知道如何推广&#xff0c;一定要阅读本文!强烈建议收藏关注&#xff0c;因为你关注的人&#xff0c;决定你看到的…

【spring】@ControllerAdvice注解学习

ControllerAdvice介绍 ControllerAdvice 是 Spring 框架提供的一个注解&#xff0c;用于定义一个全局的异常处理类或者说是控制器增强类&#xff08;controller advice class&#xff09;。这个特性特别适用于那些你想应用于整个应用程序中多个控制器的共有行为&#xff0c;比…

VeraCrypt设置步骤操作说明

一、VeraCrypt软件说明 VeraCrypt是一个开源的磁盘加密软件&#xff0c;是TrueCrypt的继承者和改进版。它提供了强大的数据加密功能&#xff0c;可以对整个磁盘、操作系统分区或移动存储设备进行加密。 二、VeraCrypt软件操作步骤 01&#xff09;首先在电脑中创建一个新的空…

【论文笔记】| 微调LLM晶体生成

【论文笔记】| 微调LLM晶体生成 Fine-Tuned Language Models Generate Stable Inorganic Materials as Text NYU, ICLR 2024 Theme&#xff1a;Material Generation Main work&#xff1a; 微调大型语言模型以生成稳定的材料 可靠性&#xff1a;在样本结构中&#xff0c;90% …

Python | Leetcode Python题解之第97题交错字符串

题目&#xff1a; 题解&#xff1a; class Solution:def isInterleave(self, s1: str, s2: str, s3: str) -> bool:n1 len(s1)n2 len(s2)if len(s3) ! n1 n2: return Falsedp [[False] * (n2 1) for _ in range(n1 1)] # dp[i][j]表示s1[0:i)和s2[0:j)是否能够构成s…

设计模式5——抽象工厂模式

写文章的初心主要是用来帮助自己快速的回忆这个模式该怎么用&#xff0c;主要是下面的UML图可以起到大作用&#xff0c;在你学习过一遍以后可能会遗忘&#xff0c;忘记了不要紧&#xff0c;只要看一眼UML图就能想起来了。同时也请大家多多指教。 抽象工厂模式&#xff08;Abst…