SP型驱动编写

我们推荐使用代码生成工具进行驱动的编写,并在生成的代码上进行微调。

SP型驱动是使用最广泛的驱动,覆盖了所有的智能设备。

为FsuOS编写SP型驱动,首先要理解程序的工作方式是异步的,串口、网络本身发送和接收就是2个动作,我们在写SP型驱动时,发送后,不能期望直接得到返回值,必须等待底层接口回调传递过来回复的数据,整个过程类似select/poll和libevent,但是接受数据的动作被封装了,更容易使用了。

在驱动编写中,千万不要使用可能造成休眠的函数,比如read,sleep,因为驱动的接口实际是被FsuOS App的线程池异步调度的,一旦休眠卡住,调度线程资源就会耗尽,协议驱动就不能工作了。

SP型驱动的工作流程: RefreshStatus -> process -> process_payload(如果有) 即每轮调用都是从RefreshStatus开始,驱动下发命令,设置状态,在process里进行处理,根据状态进行数据的提取。

RefreshStatus和process的返回值,返回true要继续进行,接收设备的回复数据。返回false表示本驱动已经完成处理,可以让出总线,不要再调度数据给我

由于SP型驱动稍有些复杂,我们用3个例子来演示驱动的编写:

  1. 自定义协议设备 需要自行处理原始数据
  2. Modbus标准设备 FsuOS已经提供的modbus基类封装,难度大大降低
  3. 电总类设备 FsuOS已经提供的PMBusProtocol基类封装,难度大大降低

1. 自定义协议设备

海虹协议,使用的是一种自定义协议,校验和采用Modbus16Crc
Haihong_interface.h

#ifndef HAIHONG_INTERFACE_H
#define HAIHONG_INTERFACE_H

#include "common_interface.h"

#pragma pack(push)
#pragma pack(1)


//0x30:
struct Haihong_30_t {
    float Ua;  //3,4
    float Ub;  //5,6
    float Uc;  //7,8
    float Ia;  //9,10
    float Ib;  //11,12
    float Ic;  //13,14
    float activePowerRatio;   //15,16,17,18,有功功率
    float activePower;        //19,20,21,22,有功电能
};
//电压,电流保留一位小数,功率保留一位小数,单位w;电能保留1位小数,单位Kwh

//0x31
struct Haihong_31_t {
    float disactivePowerRatio;    //3,4,5,6,无功功率
    float disactivePower;         //7,8,9,10,无功电能
    float IA_needed;            //11,12
    float IB_needed;            //13,14
    float IC_needed;            //15,16
    float IA_MaxNeeded;         //17,18
    float IB_MaxNeeded;         //19,20
    float IC_MaxNeeded;         //21,22
};
//所有数据保留一位有效数据

//0x32
struct Haihong_32_t {
    float activePowerNeeded;      //3,4,5,6
    float maxActivePowerNeeded;   //7,8,9,10
    float fre;                  //11,12
    float ratio;                //13,14
    unsigned char inputSwitchStatus;    //0:合,1:分
    unsigned char lightningProtection;  //0:正常,1:告警
};
//有功需量保留两位小数,单位是KW,最大有功需量保留2为小数,单位是KW.频率保留两位小数,功率因数保留三位小数.


//0x33
struct Haihong_33_t {
    unsigned char AOverVoltage; //3
    unsigned char ADownVoltage; //4

    unsigned char BOverVoltage; //5
    unsigned char BDownVoltage; //6

    unsigned char COverVoltage; //7
    unsigned char CDownVoltage; //8


    unsigned char IA_OverCurrent;   //9
    unsigned char IB_OverCurrent;   //10
    unsigned char IC_OverCurrent;   //11

    unsigned char lostPhase;        //12
    unsigned char overFre;          //13
    unsigned char downFre;          //14
    unsigned char OverPower;        //15
};

struct HaiHong_Data_t {
    unsigned int data_id;
    Haihong_30_t Haihong_30;
    Haihong_31_t Haihong_31;
    Haihong_32_t Haihong_32;
    Haihong_33_t Haihong_33;
    Haihong_30_t Haihong_40;
    Haihong_31_t Haihong_41;
    Haihong_32_t Haihong_42;
    Haihong_33_t Haihong_43;
    tele_c_time update_time;
};

#pragma pack(pop)
#endif

Haihong.h

#ifndef HAIHONG_H
#define HAIHONG_H
#include "SMDSPDevice.hpp"
#include "Haihong_interface.h"
#include "UniDataDevice.h"

enum HaiHongStatus_et {
    HAIHONG_IDLE = 0,
    HAIHONG_30,
    HAIHONG_31,
    HAIHONG_32,
    HAIHONG_33,
    HAIHONG_40,
    HAIHONG_41,
    HAIHONG_42,
    HAIHONG_43
};

/****** 编号14_海红通信列头柜--图片带协议 *********/
#define RT_HAIHONG 5061
class HaiHong: public UniDataDevice<HaiHong_Data_t, SMDSPDevice, RT_HAIHONG>
{
public:
    HaiHong();
    ~HaiHong();
    bool process(unsigned char *data, size_t len) override;
    bool process_payload(unsigned char *data, size_t len);
    bool RefreshStatus() override;
    float Get_Value(uint32_t data_id, const std::string &var_name) const;
    void RunCheckThreshold();
    void SendCmd(int tCmd);
private:
    unsigned char cmd[8];
};

PLUMA_INHERIT_PROVIDER(HaiHong, SMDSPDevice);
#endif

注意看 HaiHong这个类继承于 SMDSPDevice

Haihong.cpp

#include "Haihong.h"
#include "common_define.h"
#include "UniDataDevice.cpp"

#define DATA_LEN  0xFF   /******* test data's len *********/

static const unsigned char ModbusCRCHi[] = {
    0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41, 0x01, 0xc0,
    0x80, 0x41, 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
    0x00, 0xc1, 0x81, 0x40, 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0,
    0x80, 0x41, 0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40,
    0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1,
    0x81, 0x40, 0x01, 0xc0, 0x80, 0x41, 0x01, 0xc0, 0x80, 0x41,
    0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1,
    0x81, 0x40, 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
    0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41, 0x01, 0xc0,
    0x80, 0x41, 0x00, 0xc1, 0x81, 0x40, 0x00, 0xc1, 0x81, 0x40,
    0x01, 0xc0, 0x80, 0x41, 0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1,
    0x81, 0x40, 0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40,
    0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41, 0x01, 0xc0,
    0x80, 0x41, 0x00, 0xc1, 0x81, 0x40, 0x00, 0xc1, 0x81, 0x40,
    0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0,
    0x80, 0x41, 0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40,
    0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41, 0x01, 0xc0,
    0x80, 0x41, 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
    0x00, 0xc1, 0x81, 0x40, 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0,
    0x80, 0x41, 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
    0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0,
    0x80, 0x41, 0x00, 0xc1, 0x81, 0x40, 0x00, 0xc1, 0x81, 0x40,
    0x01, 0xc0, 0x80, 0x41, 0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1,
    0x81, 0x40, 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
    0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41, 0x01, 0xc0,
    0x80, 0x41, 0x00, 0xc1, 0x81, 0x40
};

static const unsigned char ModbusCRCLo[] = {
    0x00, 0xc0, 0xc1, 0x01, 0xc3, 0x03, 0x02, 0xc2, 0xc6, 0x06,
    0x07, 0xc7, 0x05, 0xc5, 0xc4, 0x04, 0xcc, 0x0c, 0x0d, 0xcd,
    0x0f, 0xcf, 0xce, 0x0e, 0x0a, 0xca, 0xcb, 0x0b, 0xc9, 0x09,
    0x08, 0xc8, 0xd8, 0x18, 0x19, 0xd9, 0x1b, 0xdb, 0xda, 0x1a,
    0x1e, 0xde, 0xdf, 0x1f, 0xdd, 0x1d, 0x1c, 0xdc, 0x14, 0xd4,
    0xd5, 0x15, 0xd7, 0x17, 0x16, 0xd6, 0xd2, 0x12, 0x13, 0xd3,
    0x11, 0xd1, 0xd0, 0x10, 0xf0, 0x30, 0x31, 0xf1, 0x33, 0xf3,
    0xf2, 0x32, 0x36, 0xf6, 0xf7, 0x37, 0xf5, 0x35, 0x34, 0xf4,
    0x3c, 0xfc, 0xfd, 0x3d, 0xff, 0x3f, 0x3e, 0xfe, 0xfa, 0x3a,
    0x3b, 0xfb, 0x39, 0xf9, 0xf8, 0x38, 0x28, 0xe8, 0xe9, 0x29,
    0xeb, 0x2b, 0x2a, 0xea, 0xee, 0x2e, 0x2f, 0xef, 0x2d, 0xed,
    0xec, 0x2c, 0xe4, 0x24, 0x25, 0xe5, 0x27, 0xe7, 0xe6, 0x26,
    0x22, 0xe2, 0xe3, 0x23, 0xe1, 0x21, 0x20, 0xe0, 0xa0, 0x60,
    0x61, 0xa1, 0x63, 0xa3, 0xa2, 0x62, 0x66, 0xa6, 0xa7, 0x67,
    0xa5, 0x65, 0x64, 0xa4, 0x6c, 0xac, 0xad, 0x6d, 0xaf, 0x6f,
    0x6e, 0xae, 0xaa, 0x6a, 0x6b, 0xab, 0x69, 0xa9, 0xa8, 0x68,
    0x78, 0xb8, 0xb9, 0x79, 0xbb, 0x7b, 0x7a, 0xba, 0xbe, 0x7e,
    0x7f, 0xbf, 0x7d, 0xbd, 0xbc, 0x7c, 0xb4, 0x74, 0x75, 0xb5,
    0x77, 0xb7, 0xb6, 0x76, 0x72, 0xb2, 0xb3, 0x73, 0xb1, 0x71,
    0x70, 0xb0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
    0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9c, 0x5c,
    0x5d, 0x9d, 0x5f, 0x9f, 0x9e, 0x5e, 0x5a, 0x9a, 0x9b, 0x5b,
    0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4b, 0x8b,
    0x8a, 0x4a, 0x4e, 0x8e, 0x8f, 0x4f, 0x8d, 0x4d, 0x4c, 0x8c,
    0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
    0x43, 0x83, 0x41, 0x81, 0x80, 0x40
};

static unsigned short  ModbusCRC16( unsigned char buf[], int len)
{
    unsigned char hi = 0xff;
    unsigned char lo = 0xff;
    unsigned char i;
    unsigned short crc;
    while(len--) {
        i = hi ^ *buf++;
        hi = lo ^ ModbusCRCHi [ i ];
        lo = ModbusCRCLo [ i ];
    }
    crc = hi;
    crc <<= 8;
    crc += lo;
    return crc;
}


HaiHong::HaiHong()
{
    device_type_ = "haihong";
    baud_rate_ = 9600;
    addr_ = 1;
    memset(cmd, 0x00, sizeof(cmd));
}

HaiHong::~HaiHong()
{
}


void HaiHong::SendCmd(int tCmd)
{
    cmd[0] = addr_;
    cmd[1] = tCmd;
    cmd[2] = 0x30;
    cmd[3] = 0x30;
    cmd[4] = 0x30;
    cmd[5] = 0x30;

    unsigned short crc = ModbusCRC16(cmd, 6);
    cmd[7] = ((unsigned char*)&crc)[0];
    cmd[6] = ((unsigned char*)&crc)[1];

    SendData(cmd, sizeof(cmd));
}

bool HaiHong::RefreshStatus()
{
    SMDSPDevice::RefreshStatus();
    state = HAIHONG_30;
    SendCmd(0x30);
    return true;
}

bool HaiHong::process_payload(unsigned char *data, size_t len)
{
    if(HAIHONG_30 == state) {
        if(data[1] == 0x30) {
            cData.Haihong_30.Ua = data[3] << 8 | data[2];
            cData.Haihong_30.Ub = data[5] << 8 | data[4];
            cData.Haihong_30.Uc = data[7] << 8 | data[6];

            cData.Haihong_30.Ia = data[9] << 8 | data[8];
            cData.Haihong_30.Ib = data[11] << 8 | data[10];
            cData.Haihong_30.Ic = data[13] << 8 | data[12];

            cData.Haihong_30.activePowerRatio = ((float)(*(int*)(&data[14]))) / 10;
            cData.Haihong_30.activePower = ((float)(*(int*)&data[18])) / 10;

            state = HAIHONG_31;
            SendCmd(0x31);
        } else {
            return false;
        }
    } else if(HAIHONG_31 == state) {
        if(data[1] == 0x31) {
            cData.Haihong_31.disactivePowerRatio = (float)(*(int *)&data[2]) / 10;
            cData.Haihong_31.disactivePower = (float)(*(int *)&data[6]) / 10;
            cData.Haihong_31.IA_needed = data[11] << 8 | data[10];
            cData.Haihong_31.IB_needed = data[11] << 8 | data[10];
            cData.Haihong_31.IC_needed = data[11] << 8 | data[10];

            cData.Haihong_31.IA_MaxNeeded = data[13] << 8 | data[12];
            cData.Haihong_31.IB_MaxNeeded = data[15] << 8 | data[14];
            cData.Haihong_31.IC_MaxNeeded = data[17] << 8 | data[15];

            state = HAIHONG_32;
            SendCmd(0x32);
        } else {
            return false;
        }
    } else if(HAIHONG_32 == state) {
        if(data[1] == 0x32) {
            cData.Haihong_32.activePowerNeeded = (float)(*(int *)&data[2]) / 10;
            cData.Haihong_32.maxActivePowerNeeded = (float)(*(int *)&data[6]) / 10;

            cData.Haihong_32.fre = data[11] << 8 | data[10];
            cData.Haihong_32.ratio = data[13] << 8 | data[12];
            cData.Haihong_32.inputSwitchStatus = data[14] & 1 << 3;
            cData.Haihong_32.lightningProtection = data[14] & 1;

            state = HAIHONG_33;
            SendCmd(0x33);
        } else {
            return false;
        }
    } else if(HAIHONG_33 == state) {
        if(data[1] == 0x33) {
            memcpy(&cData.Haihong_33, &data[2], sizeof(Haihong_33_t));
            state = HAIHONG_40;
            SendCmd(0x40);
        } else {
            return false;
        }
    } else if(HAIHONG_40 == state) {
        if(data[1] == 0x40) {
            cData.Haihong_40.Ua = data[3] << 8 | data[2];
            cData.Haihong_40.Ub = data[5] << 8 | data[4];
            cData.Haihong_40.Uc = data[7] << 8 | data[6];

            cData.Haihong_40.Ia = data[9] << 8 | data[8];
            cData.Haihong_40.Ib = data[11] << 8 | data[10];
            cData.Haihong_40.Ic = data[13] << 8 | data[12];

            cData.Haihong_40.activePowerRatio = ((float)(*(int*)(&data[14]))) / 10;
            cData.Haihong_40.activePower = ((float)(*(int*)&data[18])) / 10;

            state = HAIHONG_41;
            SendCmd(0x41);
        } else {
            return false;
        }
    } else if(HAIHONG_41 == state) {
        if(data[1] == 0x41) {
            cData.Haihong_41.disactivePowerRatio = (float)(*(int *)&data[2]) / 10;
            cData.Haihong_41.disactivePower = (float)(*(int *)&data[6]) / 10;
            cData.Haihong_41.IA_needed = data[11] << 8 | data[10];
            cData.Haihong_41.IB_needed = data[11] << 8 | data[10];
            cData.Haihong_41.IC_needed = data[11] << 8 | data[10];

            cData.Haihong_41.IA_MaxNeeded = data[13] << 8 | data[12];
            cData.Haihong_41.IB_MaxNeeded = data[15] << 8 | data[14];
            cData.Haihong_41.IC_MaxNeeded = data[17] << 8 | data[15];

            state = HAIHONG_42;
            SendCmd(0x42);
        } else {
            return false;
        }
    } else if(HAIHONG_42 == state) {
        if(data[1] == 0x42) {
            cData.Haihong_42.activePowerNeeded = (float)(*(int *)&data[2]) / 10;
            cData.Haihong_42.maxActivePowerNeeded = (float)(*(int *)&data[6]) / 10;

            cData.Haihong_42.fre = data[11] << 8 | data[10];
            cData.Haihong_42.ratio = data[13] << 8 | data[12];
            cData.Haihong_42.inputSwitchStatus = data[14] & 1 << 3;
            cData.Haihong_42.lightningProtection = data[14] & 1;

            state = HAIHONG_43;
            SendCmd(0x43);
        } else {
            return false;
        }
    } else if(HAIHONG_43 == state) {
        if(data[1] == 0x43) {
            memcpy(&cData.Haihong_43, &data[2], sizeof(Haihong_33_t));
            RoundDone();
        }
        return false;
    }
    return true;
}

bool HaiHong::process(unsigned char *data, size_t len)
{
    if( (msg_index + len) > 4096) {
        Reset();
        return false;
    }

    bool ret = true;
    if(msg_index == 0) {
        for(size_t i = 0; i < len ; i++) {
            if(data[i] == addr_) {
                memcpy(msg_buf, data + i, len - i);
                msg_index += len - i;
                break;
            }
        }
    } else {
        memcpy(msg_buf + msg_index, data, len);
        msg_index += len;
    }
    if(msg_index >= 26) {
        unsigned short crc = ModbusCRC16(msg_buf, 24);
        if(msg_buf[24] == ((uint8_t*)&crc)[0] && msg_buf[25] == ((uint8_t*)&crc)[1]) {
            ret = process_payload(msg_buf, 26);
        }
        msg_index = 0;
    }
    return ret;
}

void HaiHong::RunCheckThreshold()
{
    CheckThresholdBool(LEVEL_1,"Haihong_0", SIGID_16012, "防雷告警", "防雷告警", cData.Haihong_32.lightningProtection==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_1", SIGID_16006, SIGNAME_16006, SIGNAME_16006, cData.Haihong_33.AOverVoltage==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_2", SIGID_16007, SIGNAME_16007, SIGNAME_16007, cData.Haihong_33.ADownVoltage==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_3", SIGID_16008, SIGNAME_16008, SIGNAME_16008, cData.Haihong_33.BOverVoltage==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_4", SIGID_16009, SIGNAME_16009, SIGNAME_16009, cData.Haihong_33.BDownVoltage==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_5", SIGID_16010, SIGNAME_16010, SIGNAME_16010, cData.Haihong_33.COverVoltage==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_6", SIGID_16011, SIGNAME_16011, SIGNAME_16011, cData.Haihong_33.CDownVoltage==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_7", SIGID_16012, "A项过流", "A项过流", cData.Haihong_33.IA_OverCurrent==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_8", SIGID_16012, "B项过流", "B项过流", cData.Haihong_33.IB_OverCurrent==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_9", SIGID_16012, "C项过流", "C项过流", cData.Haihong_33.IC_OverCurrent==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_10", SIGID_16012, "交流输入缺相", "交流输入缺相", cData.Haihong_33.lostPhase==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_11", SIGID_16004, SIGNAME_16004, SIGNAME_16004, cData.Haihong_33.overFre==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_12", SIGID_16005, SIGNAME_16005, SIGNAME_16005, cData.Haihong_33.downFre==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_13", SIGID_16012, "功率过高", "功率过高", cData.Haihong_33.OverPower==1, signal_index_++);


    CheckThresholdBool(LEVEL_1,"Haihong_1_0", SIGID_16012, "防雷告警", "第二路防雷告警", cData.Haihong_42.lightningProtection==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_1_1", SIGID_16006, SIGNAME_16006, "第二路" SIGNAME_16006, cData.Haihong_43.AOverVoltage==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_1_2", SIGID_16007, SIGNAME_16007, "第二路" SIGNAME_16007, cData.Haihong_43.ADownVoltage==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_1_3", SIGID_16008, SIGNAME_16008, "第二路" SIGNAME_16008, cData.Haihong_43.BOverVoltage==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_1_4", SIGID_16009, SIGNAME_16009, "第二路" SIGNAME_16009, cData.Haihong_43.BDownVoltage==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_1_5", SIGID_16010, SIGNAME_16010, "第二路" SIGNAME_16010, cData.Haihong_43.COverVoltage==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_1_6", SIGID_16011, SIGNAME_16011, "第二路" SIGNAME_16011, cData.Haihong_43.CDownVoltage==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_1_7", SIGID_16012, "A项过流", "第二路A项过流", cData.Haihong_43.IA_OverCurrent==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_1_8", SIGID_16012, "B项过流", "第二路B项过流", cData.Haihong_43.IB_OverCurrent==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_1_9", SIGID_16012, "C项过流", "第二路C项过流", cData.Haihong_43.IC_OverCurrent==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_1_10", SIGID_16012, "交流输入缺相", "第二路交流输入缺相", cData.Haihong_43.lostPhase==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_1_11", SIGID_16004, SIGNAME_16004, "第二路" SIGNAME_16004, cData.Haihong_43.overFre==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_1_12", SIGID_16005, SIGNAME_16005, "第二路" SIGNAME_16005, cData.Haihong_43.downFre==1, signal_index_++);
    CheckThresholdBool(LEVEL_1,"Haihong_1_13", SIGID_16012, "功率过高", "第二路功率过高", cData.Haihong_43.OverPower==1, signal_index_++);
}

float HaiHong::Get_Value(uint32_t data_id, const std::string & var_name) const
{
    if(!bIsDataReady_)
        throw std::out_of_range("数据未就绪");

    boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
    boost::posix_time::time_duration diff = now - lastTime;
    if( diff.total_seconds() > 60) {
        throw std::out_of_range("数据已超时");
    }
    throw std::out_of_range("不支持变量");
}

#ifdef USE_SEPERATE_DRIVER

extern "C"
std::vector<std::shared_ptr<Provider>> get_providers()
{
    std::vector<std::shared_ptr<Provider>> providerVec;
    providerVec.push_back(std::make_shared<HaiHongProvider>());
    return std::move(providerVec);
}
#endif

注意

  1. RefreshStatus里,设置状态,发送自定义命令
    state = HAIHONG_30;
    SendCmd(0x30);
  2. process 里收到设备的回复,这个接收到的数据长度是不确定的,所以我们需要先缓存下来,验证长度和校验,成功后再发到process_payload处理。
  3. process_payload里根据状态提取数据,并设置下一个状态和发送命令,直到结束,调用RoundDone

2. Modbus标准设备

DTSD3366D是比较常用的雅达电表

dtsd3366d_interface.h

#ifndef DTSD3366D_INTERFACE_H
#define DTSD3366D_INTERFACE_H

#include "common_interface.h"

#pragma pack(push)
#pragma pack(1)

struct DTSD3366D_REG_8_A_t
{
   uint16_t Addr;      //设备地址; 1~247;0为广播地址
   uint16_t NC;        //预留数据网页不用显示
   uint16_t Ur;        //Ur 电压变比; 1-5000;
   uint16_t Ir;        //Ir 电流变比; 1~5000  ;
};

struct DTSD3366D_REG_16E_199_t
{
    uint32_t Ua;  
    uint32_t Ub;  
    uint32_t Uc;    //三相相电电压       单位0.0001V   网页/10000

    uint32_t Ia;  
    uint32_t Ib;  
    uint32_t Ic;    //三相电流数据单位0.0001A 网页/10000

    int32_t Psum;     //瞬时总有功功率 单位KW        网页/10000
    int32_t Pa; 
    int32_t Pb;  
    int32_t Pc;

    int32_t Qsum;     //瞬时总无功有功功率 单位KVAR网页/10000
    int32_t Qa; 
    int32_t Qb;  
    int32_t Qc;

    uint32_t Ssum;   //瞬时总视在功率      单位KVA 网页/10000
    uint32_t Sa; 
    uint32_t Sb;  
    uint32_t Sc;

    int16_t PF;       //功率因数        单位0.001         网页/1000
    int16_t PFA;      //A项功率因数  单位0.001            网页/1000
    int16_t PFB;      //B项功率因数单位0.001            网页/1000
    int16_t PFC;      //C项功率因数单位0.001            网页/1000

    int16_t Acos;      //A项相角  单位0.1°            网页/1000
    int16_t Bcos;      //B项相角单位0.1°            网页/1000
    int16_t Ccos;      //C项相角单位0.1°            网页/1000

    int16_t F;         //频率    0.01Hz          网页/100
};

struct DTSD3366D_REG_1000_1061_t
{
    int32_t Eq;            //组合有功电能  单位KWH
    uint32_t EqPositive;    //正向有功电能单位KWH
    uint32_t EqReverse;     //反向无功电能单位KWH    
};

struct DTSD3366D_Data_t
{
    unsigned int data_id;
    DTSD3366D_REG_8_A_t DTSD3366D_REG_8_A;
    DTSD3366D_REG_16E_199_t DTSD3366D_REG_16E_199;
    DTSD3366D_REG_1000_1061_t DTSD3366D_REG_1000_1061;
    tele_c_time update_time;
};

#pragma pack(pop)
#endif

dtsd3366d.h

#ifndef  DTSD3366D_H
#define  DTSD3366D_H
#include "SPModbus.h"
#include "dtsd3366d_interface.h"
#include "UniDataDevice.h"


enum DTSD3366D_Status {
    DTSD3366D_IDLE,
    DTSD3366D_REG_8_A,
    DTSD3366D_REG_16E_199,
    DTSD3366D_REG_1000,
    DTSD3366D_REG_1030,
    DTSD3366D_REG_1060
};

#define RT_DTSD3366D 5046
class DTSD3366D: public UniDataDevice<DTSD3366D_Data_t, SPModbus, RT_DTSD3366D>
{
public:
    DTSD3366D();
    ~DTSD3366D();

    bool process_payload(enum tab_type type, size_t len) override;
    bool InitSetting(const Json::Value &settingRoot) override;
    bool RefreshStatus() override;
    float Get_Value(uint32_t data_id, const std::string &var_name) const override;
    void RunCheckThreshold() ;
private:
    short ct = 1;
    bool hasA = true, hasB = true, hasC = true;
};

PLUMA_INHERIT_PROVIDER(DTSD3366D, SMDSPDevice);
#endif

注意

  1. 继承于SPModbus

dtsd3366d.cpp

#include "dtsd3366d.h"
#include "UniDataDevice.cpp"


DTSD3366D::DTSD3366D()
{
	device_type_ = "dtsd3366d";
	baud_rate_ = 9600;
	addr_ = 1;
    //parity_ = EnumParity::EVEN;
}

DTSD3366D::~DTSD3366D()
{
}


bool DTSD3366D::InitSetting(const Json::Value &settingRoot)
{
	cData.data_id = data_id_;
	if(settingRoot["appSetting"] != Json::nullValue) {
		if(settingRoot["appSetting"].type() == Json::objectValue) {
			if(settingRoot["appSetting"]["ct"] != Json::nullValue) {
				ct = atoi(settingRoot["appSetting"]["ct"].asString().c_str());
			}
            if(settingRoot["appSetting"]["has_a"] != Json::nullValue) {
				hasA = atoi(settingRoot["appSetting"]["has_a"].asString().c_str());
			}
            if(settingRoot["appSetting"]["has_b"] != Json::nullValue) {
				hasB = atoi(settingRoot["appSetting"]["has_b"].asString().c_str());
			}
            if(settingRoot["appSetting"]["has_c"] != Json::nullValue) {
				hasC = atoi(settingRoot["appSetting"]["has_c"].asString().c_str());
			}
		}
	}
	return UniDataDevice<DTSD3366D_Data_t, SPModbus, RT_DTSD3366D>::InitSetting(settingRoot);;
}

bool DTSD3366D::RefreshStatus()
{
	SMDSPDevice::RefreshStatus();
	state = DTSD3366D_REG_8_A;
	modbus_read_registers(0x07, 4);
	return true;
}
static void copy_to_float(uint16_t* buf, uint8_t *pFloat)
{
	memcpy(pFloat, buf+1, 2);
	memcpy(pFloat+2, buf, 2);
}

bool DTSD3366D::process_payload(enum tab_type type, size_t len)
{
    printf("DTSD3366D::process_payload len\n");
	if(DTSD3366D_REG_8_A == state && type == TAB_REG) {
		memcpy(&cData.DTSD3366D_REG_8_A, tab_reg, sizeof(DTSD3366D_REG_8_A_t));
		state = DTSD3366D_REG_16E_199;
		modbus_read_registers(0x16E, 44);
	} else if(DTSD3366D_REG_16E_199 == state && type == TAB_REG) {
		uint32_t *pDATA = (uint32_t*)&cData.DTSD3366D_REG_16E_199.Ua;
		for(int i=0; i<18; i++) {
			copy_to_float(tab_reg + 2*i, (uint8_t*)pDATA++);
		}
		memcpy(&cData.DTSD3366D_REG_16E_199.PF, &tab_reg[36], 16);
		state = DTSD3366D_REG_1000;
		modbus_read_registers(0x1000, 2);
	} else if(DTSD3366D_REG_1000 == state && type == TAB_REG) {
		cData.DTSD3366D_REG_1000_1061.Eq = tab_reg[0] << 16 | tab_reg[1];
		state = DTSD3366D_REG_1030;
		modbus_read_registers(0x1030, 2);
	} else if(DTSD3366D_REG_1030 == state && type == TAB_REG) {
		cData.DTSD3366D_REG_1000_1061.EqPositive = tab_reg[0] << 16 | tab_reg[1];
		state = DTSD3366D_REG_1060;
		modbus_read_registers(0x1060, 2);
	} else if(DTSD3366D_REG_1060 == state && type == TAB_REG) {
		cData.DTSD3366D_REG_1000_1061.EqReverse = tab_reg[0] << 16 | tab_reg[1];
		//multiple ct ratio
		cData.DTSD3366D_REG_16E_199.Ia *= ct;
		cData.DTSD3366D_REG_16E_199.Ib *= ct;
		cData.DTSD3366D_REG_16E_199.Ic *= ct;

		cData.DTSD3366D_REG_16E_199.Psum *= ct;
		cData.DTSD3366D_REG_16E_199.Pa *= ct;
		cData.DTSD3366D_REG_16E_199.Pb *= ct;
		cData.DTSD3366D_REG_16E_199.Pc *= ct;

		cData.DTSD3366D_REG_16E_199.Qsum *= ct;
		cData.DTSD3366D_REG_16E_199.Qa *= ct;
		cData.DTSD3366D_REG_16E_199.Qb *= ct;
		cData.DTSD3366D_REG_16E_199.Qc *= ct;

		cData.DTSD3366D_REG_16E_199.Ssum *= ct;
		cData.DTSD3366D_REG_16E_199.Sa *= ct;
		cData.DTSD3366D_REG_16E_199.Sb *= ct;
		cData.DTSD3366D_REG_16E_199.Sc *= ct;

		cData.DTSD3366D_REG_1000_1061.Eq *= ct;
		cData.DTSD3366D_REG_1000_1061.EqPositive *= ct;
		cData.DTSD3366D_REG_1000_1061.EqReverse *= ct;
		RoundDone();
		return false;
	}

	return true;
}

void DTSD3366D::RunCheckThreshold()
{
	CheckThresholdBool(1, "p41_44_0","321415001", "输入电源中断", "输入电源中断 UA:" + boost::lexical_cast<std::string>(((float)cData.DTSD3366D_REG_16E_199.Ua)/10000) + " UB:" + boost::lexical_cast<std::string>(((float)cData.DTSD3366D_REG_16E_199.Ub)/10000) + " UC:" + boost::lexical_cast<std::string>(((float)cData.DTSD3366D_REG_16E_199.Uc)/10000), (hasA && (((float)cData.DTSD3366D_REG_16E_199.Ua)/10000 < 120) ) || 
                    ( hasB && (((float)cData.DTSD3366D_REG_16E_199.Ub)/10000 < 120 )) || ( hasC && (((float)cData.DTSD3366D_REG_16E_199.Uc)/10000 < 120 )), 1);
}

float DTSD3366D::Get_Value(uint32_t data_id, const std::string &var_name) const
{
	if(!bIsDataReady_)
		throw std::out_of_range("数据未就绪");

	boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
	boost::posix_time::time_duration diff = now - lastTime;
	if( diff.total_seconds() > 60) {
		throw std::out_of_range("数据已超时");
	}

	if(var_name == "Ua") {
		return ((float)cData.DTSD3366D_REG_16E_199.Ua)/10000;
	} else if(var_name == "Ub") {
		return ((float)cData.DTSD3366D_REG_16E_199.Ub)/10000;
	} else if(var_name == "Uc") {
		return ((float)cData.DTSD3366D_REG_16E_199.Uc)/10000;
	} else if(var_name == "Ia") {
		return ((float)cData.DTSD3366D_REG_16E_199.Ia)/10000;
	} else if(var_name == "Ib") {
		return ((float)cData.DTSD3366D_REG_16E_199.Ib)/10000;
	} else if(var_name == "Ic") {
		return ((float)cData.DTSD3366D_REG_16E_199.Ic)/10000;
	}
	throw std::out_of_range("不支持变量");
}

#ifdef USE_SEPERATE_DRIVER

extern "C"
std::vector<std::shared_ptr<Provider>> get_providers()
{
	std::vector<std::shared_ptr<Provider>> providerVec;
	providerVec.push_back(std::make_shared<DTSD3366DProvider>());
	return std::move(providerVec);
}

#endif

注意

  1. 构造函数里可以设置波特率,通信地址,奇偶校验
    device_type_ = “dtsd3366d”;
    baud_rate_ = 9600;
    addr_ = 1;
    //parity_ = EnumParity::EVEN;
  2. 发送modbus命令 modbus_read_registers(0x07, 4); 和libmodbus库兼容
  3. process_payload 被调用的时候,根据前面召测命令的不同,分别从tab_reg或者tab_bit里提取数据

3. 电总类设备

格力空调电总协议

Gree_interface.h

#ifndef GREE_INTERFACE_H
#define GREE_INTERFACE_H

#include <stdint.h>



#pragma pack(push)
#pragma pack(1)


struct Gree_Data_t {
    uint32_t data_id;
    //4.5   获取空调机开关量,60-43
    uint8_t state[9];            //1 空调机工作状态   2(13\14)    00H—开机; 01H—关机
    //4.6   获取空调机告警量,60-44
    uint8_t alarm[31];
    //60_47
    uint16_t setting_temp;//温度设定值
    uint16_t temp;//环境温度
    tele_c_time update_time;
};

#pragma pack(pop)
#endif

Gree.h

#ifndef GREE_H
#define GREE_H
#include "PMBusProtocol.h"
#include "Gree_interface.h"
#include "UniDataDevice.h"
/**
 * @class HisenseS
 * @author marship
 * @date 04/08/23
 * @file Hisense.h
 * @brief 格力基站空调通信协议v3.1 (1).doc
 */

#define RT_GREE  5256
class Gree: public UniDataDevice<Gree_Data_t, PMBusProtocol, RT_GREE>
{
public:
    Gree();

    bool RefreshStatus() override;
    bool process_payload() override;
    float Get_Value ( uint32_t data_id, const std::string &var_name ) const override;
    void RunCheckThreshold() override;

    int DeviceIoControl(int ioControlCode, const void* inBuffer, int inBufferSize, void* outBuffer, int outBufferSize, int& bytesReturned) override;
protected:
    enum HISENSE_RTN {
        NORMAL       = 0x00,
        VER_MISMATCH = 0x01,
        CHKSUM_ERR   = 0x02,
        LCHKSUM_ERR  = 0x03,
        CID2_INVALID = 0x04,
        CMD_ERR      = 0x05,
        DATA_INVALID = 0x06,
        OTHER_ERROR  = 0x80
    };
    typedef enum {
        DATAMATE_IDLE = 0,
        RST_ALARM  = 0x40,
        GET_AI     = 0x42,
        GET_STATUS = 0x43,
        GET_ALARM  = 0x44,
        REMOTE_CTL = 0x45,
        GET_PARAMS = 0x47,
        SET_PARAMS = 0x49,
        GET_ADDR   = 0x50
    } HISENSE_CMD;

    enum { STATE_IDLE, STATE_60_43, STATE_60_44, STATE_60_47};

private:
    bool skipNext;
    int cmd_result_ = -1;

    uint8_t last_on_off_ = 255;//开机:00;关机01H
};

PLUMA_INHERIT_PROVIDER ( Gree, SMDSPDevice );

#endif

注意

  1. 继承于 PMBusProtocol

Gree.cpp

#include "Gree.h"
#include "common_define.h"
#include "UniDataDevice.cpp"


Gree::Gree()
{
    device_type_ = "gree";
    baud_rate_ = 4800;
    addr_ = 1;
    version_ = 0x21;
}

bool Gree::RefreshStatus()
{
    SMDSPDevice::RefreshStatus();
    state = STATE_60_43;
    write_pmbus_cmd ( 0x60, 0x43 );
    return true;
}

bool Gree::process_payload()
{
    switch ( state ) {
    case 300:
    case 310:
    case 320:
    {
        //SET TIME
        cmd_result_ = 1;
        return false;
    }
    case STATE_60_43: {
        memcpy(cData.state, pPMBus->data, 9);	
        state = STATE_60_44;
        write_pmbus_cmd ( 0x60, 0x44 );
        break;
    }
    case STATE_60_44: {
        memcpy(cData.alarm, pPMBus->data, 31);
        state = STATE_60_47;
        write_pmbus_cmd ( 0x60, 0x47);
        break;
    }
    case STATE_60_47: {
        uint16_t t;
        memcpy(&t, pPMBus->data + 12, sizeof(uint16_t));
        cData.setting_temp = ntohs(t);
        memcpy(&t, pPMBus->data + 15, sizeof(uint16_t));
        cData.temp = ntohs(t);
        RoundDone();
        return false;
    }
    }
    return true;
}


void Gree::RunCheckThreshold()
{
}

int Gree::DeviceIoControl(int ioControlCode, const void* inBuffer, int inBufferSize, void* outBuffer, int outBufferSize, int& bytesReturned)
{
    return 0;
}

float Gree::Get_Value ( uint32_t data_id, const std::string& var_name ) const
{
    if ( !bIsDataReady_ )
        throw std::out_of_range ( "数据未就绪" );

    boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
    boost::posix_time::time_duration diff = now - lastTime;
    if ( diff.total_seconds() > 60 ) {
        throw std::out_of_range ( "数据已超时" );
    }
    throw std::out_of_range ( "不支持变量" );
}


#ifdef USE_SEPERATE_DRIVER

extern "C"
std::vector<std::shared_ptr<Provider>> get_providers()
{
    std::vector<std::shared_ptr<Provider>> providerVec;
    providerVec.push_back ( std::make_shared<GreeProvider>() );
    return std::move ( providerVec );
}
#endif

注意

  1. 构造函数可以陪自豪波特率,地址,电总协议版本号
  2. write_pmbus_cmd ( 0x60, 0x43 ); 可以直接发出电总命令包
  3. process_payload 中,可以从pPMBus->data提取电总包的负载数据,这个时候校验都是通过的

问题?