From 590662d7a5a20c2030c9e9b06a458d6581d990e7 Mon Sep 17 00:00:00 2001 From: zhaojingkui <1553836110@qq.com> Date: Thu, 14 Nov 2024 18:22:58 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E8=AF=BB=E5=8F=96=E7=9A=84=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=B9=B6?= =?UTF-8?q?=E4=B8=94=E6=B7=BB=E5=8A=A0=E4=BA=86python=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 3 +- src/pPowerManger/CMakeLists.txt | 1 + src/pPowerManger/PowerManger.cpp | 59 +++++- src/pPowerManger/PowerManger.h | 4 + src/pPowerManger/ccuUdpMsg.h | 336 +++++++++++++++---------------- src/pPowerManger/udpComm.cpp | 102 ++++++++++ src/pPowerManger/udpComm.h | 24 +++ src/pPowerManger/udpMsg.cpp | 13 -- src/pPowerManger/udpMsg.h | 24 --- test/model/ccu.slx | Bin 78952 -> 82346 bytes test/model/controlFlow.slx | Bin 0 -> 55125 bytes test/scripts/test.py | 44 ++++ test/scripts/udpmsg.py | 224 +++++++++++++++++++++ 13 files changed, 622 insertions(+), 212 deletions(-) create mode 100644 src/pPowerManger/udpComm.cpp create mode 100644 src/pPowerManger/udpComm.h delete mode 100644 src/pPowerManger/udpMsg.cpp delete mode 100644 src/pPowerManger/udpMsg.h create mode 100644 test/model/controlFlow.slx create mode 100644 test/scripts/test.py create mode 100644 test/scripts/udpmsg.py diff --git a/.vscode/settings.json b/.vscode/settings.json index dd08119..b526429 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -53,5 +53,6 @@ "streambuf": "cpp", "cinttypes": "cpp", "typeinfo": "cpp" - } + }, + "C_Cpp.errorSquiggles": "disabled" } \ No newline at end of file diff --git a/src/pPowerManger/CMakeLists.txt b/src/pPowerManger/CMakeLists.txt index 581568f..bb5cd07 100644 --- a/src/pPowerManger/CMakeLists.txt +++ b/src/pPowerManger/CMakeLists.txt @@ -18,6 +18,7 @@ endif (${WIN32}) SET(SRC PowerManger.cpp PowerManger_Info.cpp + udpComm.cpp main.cpp ) diff --git a/src/pPowerManger/PowerManger.cpp b/src/pPowerManger/PowerManger.cpp index 2a13780..acf5390 100644 --- a/src/pPowerManger/PowerManger.cpp +++ b/src/pPowerManger/PowerManger.cpp @@ -10,6 +10,7 @@ #include "ACTable.h" #include "PowerManger.h" #include "MOOS/libMOOS/Utils/ConsoleColours.h" +#include using namespace std; @@ -184,7 +185,61 @@ bool PowerManger::ListenLoopCCU() { int nRead = m_pCcuUdpComm->iRecieveMessage(Buff,sizeof(Buff)); cout< 0) + { + //判断消息 + char *header = new char[sizeof(m_header)]; + unsigned short id; + memcpy(header,Buff,sizeof(m_header)); + if(!m_udpComm.getMsgId(header,id)) + { + cout << "------------------" << endl; + cout << "id:" << id << endl; + cout << "getMsgId faile" << endl; + } + delete []header; + //处理消息 + switch (id) + { + case 0x0051: //CCU_STATE_FB 0x0051 + msg_CcuStateFbMsg s; + if(m_udpComm.getCcuState((char *)Buff,s)) + { + cout << "------------------" << endl; + cout << "id:" << s.header.id << endl; + cout << "currentWorkCondition:" << (double)s.currentWorkCondition << endl; + cout << "fcuState:" << s.fcuState << endl; + cout << "fcuFaultLeve:" << s.fcuFaultLeve << endl; + cout << "powerLimit:" << s.powerLimit << endl; + } + else + { + for(int i = 0;i < m_udpComm.sError.size();i++) + cout << m_udpComm.sError[i] << endl; + m_udpComm.sError.clear(); + } + break; + case 0x0040: //CCU_SET_PARM_FB 0x0040 + msg_CcuSetParmFbMsg p; + if(m_udpComm.getCcuSetParmFb((char *)Buff,p)) + { + cout << "------------------" << endl; + cout << "id:" << p.header.id << endl; + cout << "Flas:" << p.flag << endl; + cout << "FailCode" << p.failureCode << endl; + } + else + { + for(int i = 0;i < m_udpComm.sError.size();i++) + cout << m_udpComm.sError[i] << endl; + m_udpComm.sError.clear(); + } + break; + default: + break; + } + + } } - return true; + return true; } diff --git a/src/pPowerManger/PowerManger.h b/src/pPowerManger/PowerManger.h index dc6a0a6..b699020 100644 --- a/src/pPowerManger/PowerManger.h +++ b/src/pPowerManger/PowerManger.h @@ -23,6 +23,8 @@ #include "MOOS/libMOOS/Utils/MOOSThread.h" #include "MOOS/libMOOS/Comms/XPCUdpSocket.h" #include "ccuUdpMsg.h" +#include "json/json.h" +#include "udpComm.h" #define IPORT 5000 #define MAX_UDP_PKT_SIZE 65535 @@ -50,6 +52,8 @@ class PowerManger : public AppCastingMOOSApp long m_lPort; unsigned int m_nReceiveBufferSizeKB; unsigned int m_nSendBufferSizeKB; + + udpComm m_udpComm; XPCUdpSocket *m_pCcuUdpComm; diff --git a/src/pPowerManger/ccuUdpMsg.h b/src/pPowerManger/ccuUdpMsg.h index 7835303..43e7b64 100644 --- a/src/pPowerManger/ccuUdpMsg.h +++ b/src/pPowerManger/ccuUdpMsg.h @@ -1,189 +1,181 @@ #ifndef CCUUDPMSG__H #define CCUUDPMSG__H -typedef unsigned int Checksum; +typedef unsigned int Checksum; -typedef struct -{ - unsigned char start1; - unsigned char start2; - unsigned short id; - unsigned short length; +typedef struct { + unsigned char start1; + unsigned char start2; + unsigned short id; + unsigned short length; } m_header; -typedef struct -{ - unsigned short year; - unsigned short month; - unsigned short day; - unsigned short hour; - unsigned short minute; - unsigned short second; +typedef struct { + unsigned short year; + unsigned short month; + unsigned short day; + unsigned short hour; + unsigned short minute; + unsigned short second; } m_date; -//复合管控器操作指令 -typedef struct -{ - m_header header; - m_date date; - unsigned char workCondition; - unsigned char fcuCmd; - unsigned char fcuPowerEfficiency; - unsigned char fcuInsEfficiency; - unsigned char insBatCmd; - unsigned char powerBatCmd; - unsigned char powerBatEfficiency; - unsigned char heartbeat; - unsigned char masterState; - Checksum checkCode; +// 复合管控器操作指令 +typedef struct { + m_header header; + m_date date; + unsigned char workCondition; + unsigned char fcuCmd; + unsigned char fcuPowerEfficiency; + unsigned char fcuInsEfficiency; + unsigned char insBatCmd; + unsigned char powerBatCmd; + unsigned char powerBatEfficiency; + unsigned char heartbeat; + unsigned char masterState; + Checksum checkCode; } msg_CcuColCmdMsg; -//复合管控器参数设定指令 -typedef struct -{ - m_header header; - unsigned char insSocHold1; - unsigned char insSocHold2; - unsigned char insSocHold3; - unsigned char fcuToInsLimit1; - unsigned char fcuToinsLimit2; - unsigned char powerSocHold1; - unsigned char powerSocHold2; - unsigned char powerSocHold3; - unsigned char fcuToPowerLimit1; - unsigned char fcuToPowerLimit2; - Checksum checkCode; +// 复合管控器参数设定指令 +typedef struct { + m_header header; + unsigned char insSocHold1; + unsigned char insSocHold2; + unsigned char insSocHold3; + unsigned char fcuToInsLimit1; + unsigned char fcuToinsLimit2; + unsigned char powerSocHold1; + unsigned char powerSocHold2; + unsigned char powerSocHold3; + unsigned char fcuToPowerLimit1; + unsigned char fcuToPowerLimit2; + Checksum checkCode; } msg_CcuSetParmMsg; -//配电指令 -typedef struct -{ - m_header header; - unsigned char dcb1; - unsigned char dcb2; - unsigned char dcb3; - unsigned char dcb4; - unsigned char dcb5; - unsigned char sw1; - unsigned char sw2; - unsigned char qf1; - unsigned char qf2; - unsigned char qf3; - unsigned char qf4; - unsigned char qf5; - unsigned char qf6; - unsigned char qf7; - unsigned char qf8; - unsigned char qf9; - unsigned char qf10; - unsigned char qf11; - unsigned char qf12; - unsigned char qf13; - unsigned char qf14; - unsigned char qf15; - unsigned char qf16; - unsigned char qf17; - unsigned char qf18; - unsigned char qf19; - unsigned char qf20; - unsigned char qf21; - unsigned char qf22; - unsigned char qf23; - unsigned char qf24; - unsigned char qf25; - unsigned char qf26; - Checksum checkCode; +// 配电指令 +typedef struct { + m_header header; + unsigned char dcb1; + unsigned char dcb2; + unsigned char dcb3; + unsigned char dcb4; + unsigned char dcb5; + unsigned char sw1; + unsigned char sw2; + unsigned char qf1; + unsigned char qf2; + unsigned char qf3; + unsigned char qf4; + unsigned char qf5; + unsigned char qf6; + unsigned char qf7; + unsigned char qf8; + unsigned char qf9; + unsigned char qf10; + unsigned char qf11; + unsigned char qf12; + unsigned char qf13; + unsigned char qf14; + unsigned char qf15; + unsigned char qf16; + unsigned char qf17; + unsigned char qf18; + unsigned char qf19; + unsigned char qf20; + unsigned char qf21; + unsigned char qf22; + unsigned char qf23; + unsigned char qf24; + unsigned char qf25; + unsigned char qf26; + Checksum checkCode; } msg_disSysCmdMsg; -//复合管控参数设定反馈指令 -typedef struct -{ - m_header header; - unsigned char flag; - unsigned char failureCode; - Checksum checkCode; +// 复合管控参数设定反馈指令 +typedef struct { + m_header header; + unsigned char flag; + unsigned char failureCode; + Checksum checkCode; } msg_CcuSetParmFbMsg; -//复合管控状态反馈 -typedef struct -{ - m_header header; - unsigned char currentWorkCondition; - unsigned char fcuState; - unsigned char fcuFaultLeve; - float powerLimit; - float totalPowerGeneration; - float ch4o; - float o2; - float fcuVoltage; - float fcuCurrent; - float o2Level; - float placeholder1; - float exchangerTemperature; - float fcuOutPower; - float fcuOutIns; - float fcuVoltageIns; - float fcuVoltagePower; - float fcuCurrentIns; - float fcuCurrentPower; - float h2Concentration1; - float h2Concentration2; - float o2Concentration1; - float o2Concentration2; - float ch4oConcentration1; - float ch4oConcentration2; - float backpressure1; - float backpressure2; - unsigned char flameDetection; - unsigned char fcuGZ_1L; - unsigned char fcuGZ_1H; - unsigned char fcuGZ_2L; - unsigned char fcuGZ_2H; - unsigned char fcuGZ_3L; - unsigned char fcuGZ_3H; - unsigned char insPosRelayState; - unsigned char insNegRelayState; - unsigned char powerPosRelayState; - unsigned char powerNegRelayState; - unsigned short insBatLimit; - unsigned short powerBatLimit; - unsigned char insBatSoc; - unsigned char powerBatSoc; - unsigned short insBatTotalElectricity; - unsigned short powerBatTotalElectricity; - unsigned short insBatCurrentElectricity; - unsigned short powerBatCurrentElectricity; - unsigned char insChargeState; - unsigned char powerChargeState; - unsigned short insLinkVoltage; - unsigned short insPackVoltage; - unsigned short insCurrent; - unsigned short insPosInsulationResistance; - unsigned short insNegInsulationResistance; - unsigned short powerLinkVoltage; - unsigned short powerPackVoltage; - unsigned short powerCurrent; - unsigned short powerPosInsulationResistance; - unsigned short powerNegInsulationResistance; - unsigned char insEmergencyStatusWord; - unsigned char powerEmergencyStatusWord; - unsigned char insAlarmIdentificationWord; - unsigned char powerAlarmIdentificationWord; - unsigned char insCurrentSocHold1; - unsigned char insCurrentSocHold2; - unsigned char insCurrentSocHold3; - unsigned char fcuCurrentToInsLimit1; - unsigned char fcuCurrentToinsLimit2; - unsigned char powerCurrentSocHold1; - unsigned char powerCurrentSocHold2; - unsigned char powerCurrentSocHold3; - unsigned char powerCurrentToInsLimit1; - unsigned char powerCurrentToinsLimit2; - float placeholder2; - float placeholder3; - float placeholder4; - float placeholder5; - Checksum checkCode; +// 复合管控状态反馈 +typedef struct { + m_header header; + unsigned char currentWorkCondition; + unsigned char fcuState; + unsigned char fcuFaultLeve; + float powerLimit; + float totalPowerGeneration; + float ch4o; + float o2; + float fcuVoltage; + float fcuCurrent; + float o2Level; + float placeholder1; + float exchangerTemperature; + float fcuOutPower; + float fcuOutIns; + float fcuVoltageIns; + float fcuVoltagePower; + float fcuCurrentIns; + float fcuCurrentPower; + float h2Concentration1; + float h2Concentration2; + float o2Concentration1; + float o2Concentration2; + float ch4oConcentration1; + float ch4oConcentration2; + float backpressure1; + float backpressure2; + unsigned char flameDetection; + unsigned char fcuGZ_1L; + unsigned char fcuGZ_1H; + unsigned char fcuGZ_2L; + unsigned char fcuGZ_2H; + unsigned char fcuGZ_3L; + unsigned char fcuGZ_3H; + unsigned char insPosRelayState; + unsigned char insNegRelayState; + unsigned char powerPosRelayState; + unsigned char powerNegRelayState; + unsigned short insBatLimit; + unsigned short powerBatLimit; + unsigned char insBatSoc; + unsigned char powerBatSoc; + unsigned short insBatTotalElectricity; + unsigned short powerBatTotalElectricity; + unsigned short insBatCurrentElectricity; + unsigned short powerBatCurrentElectricity; + unsigned char insChargeState; + unsigned char powerChargeState; + unsigned short insLinkVoltage; + unsigned short insPackVoltage; + unsigned short insCurrent; + unsigned short insPosInsulationResistance; + unsigned short insNegInsulationResistance; + unsigned short powerLinkVoltage; + unsigned short powerPackVoltage; + unsigned short powerCurrent; + unsigned short powerPosInsulationResistance; + unsigned short powerNegInsulationResistance; + unsigned char insEmergencyStatusWord; + unsigned char powerEmergencyStatusWord; + unsigned char insAlarmIdentificationWord; + unsigned char powerAlarmIdentificationWord; + unsigned char insCurrentSocHold1; + unsigned char insCurrentSocHold2; + unsigned char insCurrentSocHold3; + unsigned char fcuCurrentToInsLimit1; + unsigned char fcuCurrentToinsLimit2; + unsigned char powerCurrentSocHold1; + unsigned char powerCurrentSocHold2; + unsigned char powerCurrentSocHold3; + unsigned char powerCurrentToInsLimit1; + unsigned char powerCurrentToinsLimit2; + float placeholder2; + float placeholder3; + float placeholder4; + float placeholder5; + Checksum checkCode; } msg_CcuStateFbMsg; - -#endif \ No newline at end of file +#endif diff --git a/src/pPowerManger/udpComm.cpp b/src/pPowerManger/udpComm.cpp new file mode 100644 index 0000000..dfa732f --- /dev/null +++ b/src/pPowerManger/udpComm.cpp @@ -0,0 +1,102 @@ +#include "udpComm.h" +#include + +udpComm::udpComm() {} + +udpComm::~udpComm() {} +bool udpComm::getMsgId(char *m,unsigned short &id) +{ + m_header h; + h = *reinterpret_cast(m); + // 判断消息是否有效 + if (h.start1!= 0x20 || h.start2!= 0x20) { + // sError.push_back("Msg Header errar"); + return false; + } + id = h.id; + return true; +} +bool udpComm::getCcuState(char *m, msg_CcuStateFbMsg &s) { + m_header h; + Checksum sum; + msg_CcuStateFbMsg *p; + p = reinterpret_cast(m); + + h = p->header; + + // 判断消息是否有效 + if (h.start1 != 0x20 || h.start2 != 0x20) { + sError.push_back("CcuState Msg Header errar"); + return false; + } + if (h.id != 0x0051) { + sError.push_back("CcuState Msg ID error"); + // std::cout << "-------" <currentWorkCondition + + p->fcuState + p->fcuFaultLeve + p->powerLimit + p->totalPowerGeneration + + p->ch4o + p->o2 + p->fcuVoltage + p->fcuCurrent + p->o2Level + + p->placeholder1 + p->exchangerTemperature + p->fcuOutPower + + p->fcuOutIns + p->fcuVoltageIns + p->fcuVoltagePower + p->fcuCurrentIns + + p->fcuCurrentPower + p->h2Concentration1 + p->h2Concentration2 + + p->o2Concentration1 + p->o2Concentration2 + p->ch4oConcentration1 + + p->ch4oConcentration2 + p->backpressure1 + p->backpressure2 + + p->flameDetection + p->fcuGZ_1L + p->fcuGZ_1H + p->fcuGZ_2L + + p->fcuGZ_2H + p->fcuGZ_3L + p->fcuGZ_3H + p->insPosRelayState + + p->insNegRelayState + p->powerNegRelayState + p->powerPosRelayState + + p->insBatLimit + p->powerBatLimit + p->insBatSoc + p->powerBatSoc + + p->insBatTotalElectricity + p->powerBatTotalElectricity + + p->insBatCurrentElectricity + p->powerBatCurrentElectricity + + p->insChargeState + p->powerChargeState + p->insLinkVoltage + + p->insPackVoltage + p->insCurrent + p->insPosInsulationResistance + + p->insNegInsulationResistance + p->powerLinkVoltage + + p->powerPackVoltage + p->powerCurrent + p->powerPosInsulationResistance + + p->powerNegInsulationResistance + p->insEmergencyStatusWord + + p->powerEmergencyStatusWord + p->insAlarmIdentificationWord + + p->powerAlarmIdentificationWord + p->insCurrentSocHold1 + + p->insCurrentSocHold2 + p->insCurrentSocHold3 + p->fcuCurrentToInsLimit1 + + p->fcuCurrentToinsLimit2 + p->powerCurrentSocHold1 + + p->powerCurrentSocHold2 + p->powerCurrentSocHold3 + + p->powerCurrentToInsLimit1 + p->powerCurrentToinsLimit2 + + p->placeholder2 + p->placeholder3 + p->placeholder5; + if (sum != p->checkCode) { + sError.push_back("CcuStat Msg Check error"); + //return false; TODO: 暂时不校验 + } + s = *p; + return true; +} + +bool udpComm::getCcuSetParmFb(char *m, msg_CcuSetParmFbMsg &s) +{ + m_header h; + Checksum sum; + msg_CcuSetParmFbMsg *p; + p = reinterpret_cast(m); + + h = p->header; + + // 判断消息是否有效 + if (h.start1!= 0x20 || h.start2!= 0x20) { + sError.push_back("CcuSetParm Msg Header errar"); + return false; + } + + if (h.id!= 0x0040) + { + sError.push_back("CcuSetParm Msg ID error"); + return false; + } + + sum = h.id + h.length + h.start1 + h.start2 + p->flag + p->failureCode; + + if (sum!= p->checkCode) { + sError.push_back("CcuSetParm Msg Check error"); + return false; + } + + s = *p; + return true; +} \ No newline at end of file diff --git a/src/pPowerManger/udpComm.h b/src/pPowerManger/udpComm.h new file mode 100644 index 0000000..04af433 --- /dev/null +++ b/src/pPowerManger/udpComm.h @@ -0,0 +1,24 @@ +#ifndef UDPCOMM__H +#define UDPCOMM__H +#include "ccuUdpMsg.h" +#include +#include +class udpComm { +private: + /* data */ +public: + udpComm(); + ~udpComm(); + bool getMsgId(char *m,unsigned short &id); + bool getCcuState(char *m, msg_CcuStateFbMsg &s); + bool getCcuSetParmFb(char *m, msg_CcuSetParmFbMsg &s); + bool sendCcuColCmd(const msg_CcuColCmdMsg m); + bool sendCcuSetParmCmd(const msg_CcuSetParmMsg m); + std::vector sError; + +private: + msg_CcuColCmdMsg m_ccuColCmd; + msg_CcuSetParmMsg m_ccuSetParmCmd; +}; + +#endif diff --git a/src/pPowerManger/udpMsg.cpp b/src/pPowerManger/udpMsg.cpp deleted file mode 100644 index 4575f03..0000000 --- a/src/pPowerManger/udpMsg.cpp +++ /dev/null @@ -1,13 +0,0 @@ - -#include "udpMsg.h" - - -udpMsg::udpMsg() -{ - -} - -udpMsg::~udpMsg() -{ - -} \ No newline at end of file diff --git a/src/pPowerManger/udpMsg.h b/src/pPowerManger/udpMsg.h deleted file mode 100644 index c7709cd..0000000 --- a/src/pPowerManger/udpMsg.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef UDPMSG__H -#define UDPMSG__H - -class udpMsg -{ -private: - /* data */ -public: - udpMsg(/* args */); - ~udpMsg(); -}; - -udpMsg::udpMsg(/* args */) -{ - -} - -udpMsg::~udpMsg() -{ - -} - - -#endif \ No newline at end of file diff --git a/test/model/ccu.slx b/test/model/ccu.slx index c58d788bd55b5cf24ac5e5eb17a4323ffc9243f9..dc0770de94b4b5fc9860634482fa6dd2b2cf5810 100644 GIT binary patch delta 11679 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 literal 0 HcmV?d00001 diff --git a/test/scripts/test.py b/test/scripts/test.py new file mode 100644 index 0000000..33798fd --- /dev/null +++ b/test/scripts/test.py @@ -0,0 +1,44 @@ +import socket +import time +import struct +from udpmsg import msg_CcuStateFbMsg +from udpmsg import msg_CcuSetParamFbMsg +# 目标IP地址和端口 +target_ip = '127.0.0.1' +target_port = 5000 + + +mTest = msg_CcuStateFbMsg() +mTest2 = msg_CcuSetParamFbMsg() + +valid_msg = mTest.pack() +valid_msg2 = mTest2.pack() +print(valid_msg) + + +# 创建UDP套接字 +sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + +# 要发送的信息 +# message = "Hello, this is a test message." +message = valid_msg +# 发送信息的次数 +num_messages = 10 + +# 发送信息的时间间隔(秒) +interval = 1 + +for i in range(num_messages): + # 发送信息 + sock.sendto(message, (target_ip, target_port)) + + # 打印发送的信息和目标地址 + print(f"Sent message to {target_ip}:{target_port}: {message}") + + # 等待一段时间 + time.sleep(interval) + sock.sendto(valid_msg2, (target_ip, target_port)) + time.sleep(interval) + +# 关闭套接字 +sock.close() diff --git a/test/scripts/udpmsg.py b/test/scripts/udpmsg.py new file mode 100644 index 0000000..dae6c35 --- /dev/null +++ b/test/scripts/udpmsg.py @@ -0,0 +1,224 @@ +import struct + +class msg_CcuStateFbMsg: + def __init__(self): + self.start1 = 0x20 + self.start2 = 0x20 + self.id = 0x0051 + self.length = 0x5A + self.currentWorkCondition = 0x01 + self.fcuState = 0x01 + self.fcuFaultLeve = 0x01 + self.powerLimit = 0x01 + self.totalPowerGeneration = 0x01 + self.ch4o = 0x01 + self.o2 = 0x01 + self.fcuVoltage = 0x01 + self.fcuCurrent = 0x01 + self.o2Level = 0x01 + self.placeholder1 = 0x01 + self.exchangerTemperature = 0x01 + self.fcuOutPower = 0x01 + self.fcuOutIns = 0x01 + self.fcuVoltageIns = 0x01 + self.fcuVoltagePower = 0x01 + self.fcuCurrentIns = 0x01 + self.fcuCurrentPower = 0x01 + self.h2Concentration1 = 0x01 + self.h2Concentration2 = 0x01 + self.o2Concentration1 = 0x01 + self.o2Concentration2 = 0x01 + self.ch4oConcentration1 = 0x01 + self.ch4oConcentration2 = 0x01 + self.backpressure1 = 0x01 + self.backpressure2 = 0x01 + self.flameDetection = 0x01 + self.fcuGZ_1L = 0x01 + self.fcuGZ_1H = 0x01 + self.fcuGZ_2L = 0x01 + self.fcuGZ_2H = 0x01 + self.fcuGZ_3L = 0x01 + self.fcuGZ_3H = 0x01 + self.insPosRelayState = 0x01 + self.insNegRelayState = 0x01 + self.powerPosRelayState = 0x01 + self.powerNegRelayState = 0x01 + self.insBatLimit = 0x01 + self.powerBatLimit = 0x01 + self.insBatSoc = 0x01 + self.powerBatSoc = 0x01 + self.insBatTotalElectricity = 0x01 + self.powerBatTotalElectricity = 0x01 + self.insBatCurrentElectricity = 0x01 + self.powerBatCurrentElectricity = 0x01 + self.insChargeState = 0x01 + self.powerChargeState = 0x01 + self.insLinkVoltage = 0x01 + self.insPackVoltage = 0x01 + self.insCurrent = 0x01 + self.insPosInsulationResistance = 0x01 + self.insNegInsulationResistance = 0x01 + self.powerLinkVoltage = 0x01 + self.powerPackVoltage = 0x01 + self.powerCurrent = 0x01 + self.powerPosInsulationResistance = 0x01 + self.powerNegInsulationResistance = 0x01 + self.insEmergencyStatusWord = 0x01 + self.powerEmergencyStatusWord = 0x01 + self.insAlarmIdentificationWord = 0x01 + self.powerAlarmIdentificationWord = 0x01 + self.insCurrentSocHold1 = 0x01 + self.insCurrentSocHold2 = 0x01 + self.insCurrentSocHold3 = 0x01 + self.fcuCurrentToInsLimit1 = 0x01 + self.fcuCurrentToinsLimit2 = 0x01 + self.powerCurrentSocHold1 = 0x01 + self.powerCurrentSocHold2 = 0x01 + self.powerCurrentSocHold3 = 0x01 + self.powerCurrentToInsLimit1 = 0x01 + self.powerCurrentToinsLimit2 = 0x01 + self.placeholder2 = 0x01 + self.placeholder3 = 0x01 + self.placeholder4 = 0x01 + self.placeholder5 = 0x01 + self.checkCode = 0x01 + + + + + def pack(self): + # 初始化数据 + self.length = 0x0000 + + self.checkCode = self.start1 + self.start2 + self.id + self.length + + self.currentWorkCondition + self.fcuState + self.fcuFaultLeve + + self.powerLimit + self.totalPowerGeneration + self.ch4o + self.o2 + + self.fcuVoltage + self.fcuCurrent + self.o2Level + self.placeholder1 + + self.exchangerTemperature + self.fcuOutPower + self.fcuOutIns + self.fcuVoltageIns + + self.fcuVoltagePower + self.fcuCurrentIns + self.fcuCurrentPower + self.h2Concentration1 + + self.h2Concentration2 + self.o2Concentration1 + self.o2Concentration2 + self.ch4oConcentration1 + + self.ch4oConcentration2 + self.backpressure1 + self.backpressure2 + self.flameDetection + self.fcuGZ_1L + + self.fcuGZ_1H + self.fcuGZ_2L + self.fcuGZ_2H + self.fcuGZ_3L + self.fcuGZ_3H + self.insPosRelayState + + self.insNegRelayState + self.powerPosRelayState + self.powerNegRelayState + self.insBatLimit + + self.powerBatLimit + self.insBatSoc + self.powerBatSoc + self.insBatTotalElectricity + + self.powerBatTotalElectricity + self.insBatCurrentElectricity + self.powerBatCurrentElectricity + + self.insChargeState + self.powerChargeState + self.insLinkVoltage + self.insPackVoltage + + self.insCurrent + self.insPosInsulationResistance + self.insNegInsulationResistance + + self.powerLinkVoltage + self.powerPackVoltage + self.powerCurrent + self.powerPosInsulationResistance + + self.powerNegInsulationResistance + self.insEmergencyStatusWord + self.powerEmergencyStatusWord + + self.insAlarmIdentificationWord + self.powerAlarmIdentificationWord + self.insCurrentSocHold1 + self.insCurrentSocHold2 + + self.insCurrentSocHold3 + self.fcuCurrentToInsLimit1 + self.fcuCurrentToinsLimit2 + self.powerCurrentSocHold1 + + self.powerCurrentSocHold2 + self.powerCurrentSocHold3 + self.powerCurrentToInsLimit1 + self.powerCurrentToinsLimit2 + + self.placeholder2 + self.placeholder3 + self.placeholder4 + self.placeholder5 + + data = struct.pack('