iec104masterqt.cpp 98 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808
  1. #include "iec104masterqt.h"
  2. #include <QDateTime>
  3. #include <QHostAddress>
  4. #include <QtEndian>
  5. #include <cstdlib>
  6. #include <cstring>
  7. IEC104MasterQt::IEC104MasterQt(QObject *parent)
  8. : QObject(parent), m_socket(new QTcpSocket(this)), m_connected(false),
  9. m_rxCounter(0), m_txCounter(0), m_lastAckRx(0), m_lastAckTx(0), m_t0(30), m_t1(15), m_t2(10), m_t3(20), m_k(12), m_w(8),
  10. nasdu(1)
  11. {
  12. // Setup timers
  13. m_timerT1 = new QTimer(this);
  14. m_timerT2 = new QTimer(this);
  15. m_timerT3 = new QTimer(this);
  16. // m_timerT1->setSingleShot(true);
  17. // m_timerT2->setSingleShot(true);
  18. // m_timerT3->setSingleShot(false);
  19. // Connect socket signals
  20. connect(m_socket, &QTcpSocket::connected, this, &IEC104MasterQt::onSocketConnected);
  21. connect(m_socket, &QTcpSocket::disconnected, this, &IEC104MasterQt::onSocketDisconnected);
  22. connect(m_socket, &QTcpSocket::readyRead, this, &IEC104MasterQt::onSocketReadyRead);
  23. // 兼容不同Qt版本的错误信号连接
  24. #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
  25. // Qt 5.15+ 使用 errorOccurred
  26. connect(m_socket, &QTcpSocket::errorOccurred, this, &IEC104MasterQt::onSocketError);
  27. #else
  28. // Qt 5.15 之前使用 error
  29. connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)),
  30. this, SLOT(onSocketError(QAbstractSocket::SocketError)));
  31. #endif
  32. // Connect timer signals
  33. bool status=connect(m_timerT1, SIGNAL(timeout()), this, SLOT(onTimerT1Timeout()));
  34. status = connect(m_timerT2, SIGNAL(timeout()), this, SLOT(onTimerT2Timeout()));
  35. status = connect(m_timerT3, SIGNAL(timeout()), this, SLOT(onTimerT3Timeout()));
  36. status = false;
  37. //m_timerT3->start(m_t3 * 1000);
  38. }
  39. IEC104MasterQt::~IEC104MasterQt()
  40. {
  41. closeLink();
  42. }
  43. bool IEC104MasterQt::openLink()
  44. {
  45. if (m_connected)
  46. {
  47. emit logMessage("Already connected");
  48. return true;
  49. }
  50. emit logMessage(QString("Connecting to %1:%2").arg(m_serverIP).arg(m_port));
  51. m_socket->connectToHost(m_serverIP, m_port);
  52. return true;
  53. }
  54. void IEC104MasterQt::closeLink()
  55. {
  56. if (m_connected)
  57. {
  58. sendStopDT();
  59. }
  60. m_socket->disconnectFromHost();
  61. m_connected = false;
  62. emit connectionStatusChanged(false);
  63. m_timerT1->stop();
  64. m_timerT2->stop();
  65. m_timerT3->stop();
  66. }
  67. void IEC104MasterQt::setConnectionParameters(const QString &serverIP, int port)
  68. {
  69. m_serverIP = serverIP;
  70. m_port = port;
  71. }
  72. bool IEC104MasterQt::sendStartDT()
  73. {
  74. return sendUMessage(0x07); // STARTDT ACT
  75. }
  76. bool IEC104MasterQt::sendStopDT()
  77. {
  78. return sendUMessage(0x13); // STOPDT ACT
  79. }
  80. bool IEC104MasterQt::sendTestFR()
  81. {
  82. return sendUMessage(0x43); // TESTFR ACT
  83. }
  84. bool IEC104MasterQt::sendInterrogation(int asdu, int group)
  85. {
  86. QByteArray data;
  87. data.append(static_cast<char>(0x64)); // type
  88. data.append(static_cast<char>(0x01)); // qual
  89. data.append(static_cast<char>(0x06)); // cause
  90. data.append(static_cast<char>(0x00));
  91. data.append(static_cast<char>(asdu & 0xFF));
  92. data.append(static_cast<char>((asdu >> 8) & 0xFF));
  93. data.append(static_cast<char>(0x00));
  94. data.append(static_cast<char>(0x00));
  95. data.append(static_cast<char>(0x00));
  96. data.append(static_cast<char>(20 + group));
  97. return sendIMessage(data);
  98. }
  99. bool IEC104MasterQt::sendASDUMessage(const QByteArray &data)
  100. {
  101. return sendIMessage(data);
  102. }
  103. bool IEC104MasterQt::sendUMessage(int command)
  104. {
  105. if (!m_connected)
  106. {
  107. emit logMessage("Not connected, cannot send U message");
  108. return false;
  109. }
  110. APCI apci;
  111. apci.start = 0x68;
  112. apci.len = 0x04;
  113. apci.field1 = 0x03 | command;
  114. apci.field2 = 0;
  115. apci.field3 = 0;
  116. apci.field4 = 0;
  117. QByteArray data(reinterpret_cast<const char *>(&apci), sizeof(apci));
  118. qint64 bytesWritten = m_socket->write(data);
  119. if (bytesWritten == sizeof(apci))
  120. {
  121. emit dataSent(data);
  122. emit logMessage(QString("Sent U message: %1").arg(toHexString(data).constData()));
  123. return true;
  124. }
  125. else
  126. {
  127. emit logMessage("Failed to send U message");
  128. return false;
  129. }
  130. }
  131. bool IEC104MasterQt::sendSMessage()
  132. {
  133. if (!m_connected)
  134. {
  135. return false;
  136. }
  137. APCI apci;
  138. apci.start = 0x68;
  139. apci.len = 0x04;
  140. apci.field1 = 0x01;
  141. apci.field2 = 0;
  142. apci.field3 = m_rxCounter & 0xFE;
  143. apci.field4 = (m_rxCounter >> 8) & 0xFF;
  144. QByteArray data(reinterpret_cast<const char *>(&apci), sizeof(apci));
  145. if (m_socket->write(data) == sizeof(apci))
  146. {
  147. emit dataSent(data);
  148. emit logMessage(QString("Sent S message: %1").arg(toHexString(data).constData()));
  149. return true;
  150. }
  151. return false;
  152. }
  153. bool IEC104MasterQt::sendIMessage(const QByteArray &asduData)
  154. {
  155. if (!m_connected)
  156. {
  157. return false;
  158. }
  159. QByteArray buffer;
  160. buffer.resize(sizeof(APCI) + asduData.size());
  161. APCI *apci = reinterpret_cast<APCI *>(buffer.data());
  162. apci->start = 0x68;
  163. apci->len = 0x04 + asduData.size();
  164. apci->field1 = m_txCounter & 0xFE;
  165. apci->field2 = (m_txCounter >> 8) & 0xFF;
  166. apci->field3 = m_rxCounter & 0xFE;
  167. apci->field4 = (m_rxCounter >> 8) & 0xFF;
  168. memcpy(buffer.data() + sizeof(APCI), asduData.constData(), asduData.size());
  169. if (m_socket->write(buffer) == buffer.size())
  170. {
  171. emit dataSent(buffer);
  172. emit logMessage(QString("Sent I message: %1").arg(toHexString(buffer).constData()));
  173. m_txCounter += 2;
  174. return true;
  175. }
  176. return false;
  177. }
  178. void IEC104MasterQt::onSocketConnected()
  179. {
  180. m_connected = true;
  181. emit connectionStatusChanged(true);
  182. emit logMessage("Socket connected successfully");
  183. // Start data transfer
  184. sendStartDT();
  185. // 使用单次定时器确保在事件循环中执行
  186. //QTimer::singleShot(10, this, &IEC104MasterQt::delayedStartT3Timer);
  187. // Start supervision timer
  188. m_timerT3->start(m_t3 * 1000);
  189. // if (!m_timerT3->isActive())
  190. // {
  191. // m_timerT3->start(3000);
  192. // }
  193. }
  194. void IEC104MasterQt::onSocketDisconnected()
  195. {
  196. m_connected = false;
  197. emit connectionStatusChanged(false);
  198. emit logMessage("Socket disconnected");
  199. m_timerT1->stop();
  200. m_timerT2->stop();
  201. m_timerT3->stop();
  202. }
  203. void IEC104MasterQt::onSocketReadyRead()
  204. {
  205. QByteArray data = m_socket->readAll();
  206. emit dataReceived(data);
  207. emit logMessage(QString("Received data: %1").arg(toHexString(data).constData()));
  208. processReceivedData(data);
  209. }
  210. void IEC104MasterQt::onSocketError(QAbstractSocket::SocketError error)
  211. {
  212. Q_UNUSED(error)
  213. emit logMessage(QString("Socket error: %1").arg(m_socket->errorString()));
  214. if (m_connected)
  215. {
  216. m_connected = false;
  217. emit connectionStatusChanged(false);
  218. }
  219. }
  220. void IEC104MasterQt::onTimerT1Timeout()
  221. {
  222. emit logMessage("T1 timeout - No confirmation received");
  223. closeLink();
  224. }
  225. void IEC104MasterQt::onTimerT2Timeout()
  226. {
  227. sendSMessage();
  228. //m_timerT2->start(m_t2 * 1000);
  229. }
  230. void IEC104MasterQt::onTimerT3Timeout()
  231. {
  232. if (m_connected)
  233. {
  234. sendTestFR();
  235. //m_timerT3->start(m_t3 * 1000);
  236. }
  237. }
  238. void IEC104MasterQt::delayedStartT3Timer()
  239. {
  240. if (m_connected && m_timerT3) {
  241. if (m_timerT3->isActive()) {
  242. m_timerT3->stop();
  243. }
  244. m_timerT3->start(m_t3 * 1000);
  245. emit logMessage(QString("T3 timer started via delayed call, active: %1, interval: %2ms")
  246. .arg(m_timerT3->isActive()).arg(m_t3 * 1000));
  247. }
  248. }
  249. void IEC104MasterQt::processReceivedData(const QByteArray &data)
  250. {
  251. int index = 0;
  252. while (index < data.size())
  253. {
  254. if (static_cast<unsigned char>(data[index]) == 0x68)
  255. {
  256. if (index + 1 < data.size())
  257. {
  258. int length = static_cast<unsigned char>(data[index + 1]);
  259. if (index + length + 2 <= data.size())
  260. {
  261. QByteArray frame = data.mid(index, length + 2);
  262. const APCI *apci = reinterpret_cast<const APCI *>(frame.constData());
  263. int formatType = apci->field1 & 0x03;
  264. if (formatType == 0 || formatType == 2)
  265. { // I format
  266. //m_timerT3->stop(); // Reset test timer
  267. int revSendNum = (apci->field2 << 8) | apci->field1;
  268. m_rxCounter = revSendNum;
  269. m_lastAckTx = (apci->field4 << 8) | apci->field3;
  270. sendSMessage();
  271. QByteArray asduData = frame.mid(sizeof(APCI));
  272. decodeIMessage(asduData);
  273. }
  274. else if (formatType == 3)
  275. { // U format
  276. //m_timerT3->stop(); // Reset test timer
  277. processFormatU(*apci);
  278. m_lastAckTx = (apci->field4 << 8) | apci->field3;
  279. }
  280. index += length + 2;
  281. continue;
  282. }
  283. }
  284. }
  285. index++;
  286. }
  287. }
  288. void IEC104MasterQt::decodeIMessage(const QByteArray &data)
  289. {
  290. if (data.size() < 6)
  291. return;
  292. const ASDU_header *header = reinterpret_cast<const ASDU_header *>(data.constData());
  293. int num = header->qual & 0x7F;
  294. bool seq = (header->qual & 0x80) != 0;
  295. int cause = header->tx_cause_1;
  296. int asdu = (header->commom_asdu_2 << 8) | header->commom_asdu_1;
  297. emit logMessage(QString("ASDU Address=%1, TypeID=%2, Count=%3, Cause=%4")
  298. .arg(asdu)
  299. .arg(header->type)
  300. .arg(num)
  301. .arg(cause));
  302. int pos = sizeof(ASDU_header);
  303. switch (header->type)
  304. {
  305. case 1:
  306. { // Single-point information (遥信)
  307. if (seq)
  308. {
  309. if (pos + sizeof(SP104) <= data.size())
  310. {
  311. const SP104 *sp = reinterpret_cast<const SP104 *>(data.constData() + pos);
  312. int address = (sp->addr2 << 16) | (sp->addr1 << 8) | sp->addr0;
  313. updateEquipmentStatus(address, sp->siq.spi, 0);
  314. emit logMessage(QString("SP %1: %2").arg(address).arg(sp->siq.spi));
  315. pos += sizeof(SP104);
  316. // Process subsequent SIQ values in sequence
  317. while (pos < data.size())
  318. {
  319. const SIQ104 *siq = reinterpret_cast<const SIQ104 *>(data.constData() + pos);
  320. address++;
  321. updateEquipmentStatus(address, siq->spi, 0);
  322. emit logMessage(QString("SP %1: %2").arg(address).arg(siq->spi));
  323. pos++;
  324. }
  325. }
  326. }
  327. else
  328. {
  329. while (pos + sizeof(SP104) <= data.size())
  330. {
  331. const SP104 *sp = reinterpret_cast<const SP104 *>(data.constData() + pos);
  332. int address = (sp->addr2 << 16) | (sp->addr1 << 8) | sp->addr0;
  333. updateEquipmentStatus(address, sp->siq.spi, 0);
  334. emit logMessage(QString("SP %1: %2").arg(address).arg(sp->siq.spi));
  335. pos += sizeof(SP104);
  336. }
  337. }
  338. if (cause == 3)
  339. {
  340. sendSMessage();
  341. }
  342. }
  343. break;
  344. case 3:
  345. { // Double-point information without time tag (双点信息,不带时标)
  346. if (seq)
  347. {
  348. // 序列方式(地址连续)
  349. if (pos + sizeof(DP104) <= data.size())
  350. {
  351. const DP104* dp = reinterpret_cast<const DP104*>(data.constData() + pos);
  352. int address = (dp->addr2 << 16) | (dp->addr1 << 8) | dp->addr0;
  353. // 解析双点状态
  354. int status = 0;
  355. switch (dp->diq.dpi) {
  356. case 1: status = 1; break; // 分
  357. case 2: status = 2; break; // 合
  358. case 0: status = 0; break; // 中间状态
  359. case 3: status = 4; break; // 不确定状态
  360. }
  361. updateEquipmentStatus(address, status, 1);
  362. emit logMessage(QString("DP %1: %2 (DPI=%3)").arg(address).arg(status).arg(dp->diq.dpi));
  363. pos += sizeof(DP104);
  364. // 处理后续的双点信息(序列方式)
  365. while (pos < data.size())
  366. {
  367. const DIQ104* diq = reinterpret_cast<const DIQ104*>(data.constData() + pos);
  368. address++;
  369. int subsequentStatus = 0;
  370. switch (diq->dpi) {
  371. case 1: subsequentStatus = 1; break;
  372. case 2: subsequentStatus = 2; break;
  373. case 0: subsequentStatus = 0; break;
  374. case 3: subsequentStatus = 4; break;
  375. }
  376. updateEquipmentStatus(address, subsequentStatus, 1);
  377. emit logMessage(QString("DP %1: %2 (DPI=%3)").arg(address).arg(subsequentStatus).arg(diq->dpi));
  378. pos++;
  379. }
  380. }
  381. }
  382. else
  383. {
  384. // 非序列方式(独立地址)
  385. while (pos + sizeof(DP104) <= data.size())
  386. {
  387. const DP104* dp = reinterpret_cast<const DP104*>(data.constData() + pos);
  388. int address = (dp->addr2 << 16) | (dp->addr1 << 8) | dp->addr0;
  389. // 解析双点状态
  390. int status = 0;
  391. switch (dp->diq.dpi) {
  392. case 1: status = 1; break; // 分
  393. case 2: status = 2; break; // 合
  394. case 0: status = 0; break; // 中间状态
  395. case 3: status = 4; break; // 不确定状态
  396. }
  397. updateEquipmentStatus(address, status, 1);
  398. emit logMessage(QString("DP %1: %2 (DPI=%3)").arg(address).arg(status).arg(dp->diq.dpi));
  399. pos += sizeof(DP104);
  400. }
  401. }
  402. if (cause == 3) {
  403. sendSMessage(); // 发送确认帧
  404. }
  405. break;
  406. }
  407. break;
  408. case 9:
  409. { // Measured value, normalized value (规一化测量值)
  410. while (pos + 5 <= data.size())
  411. { // 地址3字节 + 值2字节 + QDS1字节
  412. const unsigned char *ptr = reinterpret_cast<const unsigned char *>(data.constData() + pos);
  413. int address = (ptr[2] << 16) | (ptr[1] << 8) | ptr[0];
  414. // 规一化值 (2字节,有符号)
  415. short normalizedValue = (ptr[4] << 8) | ptr[3];
  416. float value = normalizedValue; // 可根据需要转换为实际工程值
  417. QDS104 qds;
  418. memcpy(&qds, ptr + 5, sizeof(QDS104));
  419. updateMeasuredValue(address, value, !qds.iv, qds.ov, qds.bl, qds.sb);
  420. emit logMessage(QString("Normalized Value %1: %2, Valid: %3")
  421. .arg(address)
  422. .arg(value)
  423. .arg(!qds.iv));
  424. pos += 6; // 3字节地址 + 2字节值 + 1字节QDS
  425. }
  426. }
  427. break;
  428. case 13:
  429. { // Measured value, short floating point value (短浮点数测量值)
  430. while (pos + sizeof(SFP104) <= data.size())
  431. {
  432. const SFP104 *sfp = reinterpret_cast<const SFP104 *>(data.constData() + pos);
  433. int address = (sfp->addr2 << 16) | (sfp->addr1 << 8) | sfp->addr0;
  434. float value = bytesToFloat(sfp->val);
  435. updateMeasuredValue(address, value, !sfp->qds.iv, sfp->qds.ov, sfp->qds.bl, sfp->qds.sb);
  436. emit logMessage(QString("Short Float %1: %2, Valid: %3, Overflow: %4")
  437. .arg(address)
  438. .arg(value)
  439. .arg(!sfp->qds.iv)
  440. .arg(sfp->qds.ov));
  441. pos += sizeof(SFP104);
  442. }
  443. }
  444. break;
  445. case 30:
  446. { // Single-point information with time tag (带时标遥信)
  447. while (pos + sizeof(SP104_T) <= data.size())
  448. {
  449. const SP104_T *spt = reinterpret_cast<const SP104_T *>(data.constData() + pos);
  450. int address = (spt->addr2 << 16) | (spt->addr1 << 8) | spt->addr0;
  451. updateEquipmentStatus(address, spt->siq.spi, 0);
  452. emit logMessage(QString("SP with time %1: %2").arg(address).arg(spt->siq.spi));
  453. pos += sizeof(SP104_T);
  454. }
  455. if (cause == 3)
  456. {
  457. sendSMessage();
  458. }
  459. }
  460. break;
  461. case 31:
  462. { // Double-point information with time tag (带时标双点遥信)
  463. while (pos + sizeof(SP104_T) <= data.size())
  464. {
  465. const SP104_T *spt = reinterpret_cast<const SP104_T *>(data.constData() + pos);
  466. int address = (spt->addr2 << 16) | (spt->addr1 << 8) | spt->addr0;
  467. int nStatus = 0;
  468. if (spt->siq.spi && !spt->siq.rs1)
  469. {
  470. nStatus = 1;
  471. } // 1 0分
  472. else if (!spt->siq.spi && spt->siq.rs1)
  473. {
  474. nStatus = 2;
  475. } // 0 1合
  476. else if (spt->siq.spi && spt->siq.rs1)
  477. {
  478. nStatus = 4;
  479. } // 11不确定状态
  480. else
  481. {
  482. nStatus = 0;
  483. } //
  484. updateEquipmentStatus(address, nStatus, 1);
  485. emit logMessage(QString("DP with time %1: %2 %3").arg(address).arg(spt->siq.spi).arg(spt->siq.rs1));
  486. pos += sizeof(SP104_T);
  487. }
  488. if (cause == 3)
  489. {
  490. sendSMessage();
  491. }
  492. }
  493. break;
  494. case 35:
  495. { // Measured value, scaled value with time tag CP56Time2a (带时标的标度化值)
  496. while (pos + sizeof(ScaledValueWithTime) <= data.size())
  497. {
  498. const ScaledValueWithTime* svt = reinterpret_cast<const ScaledValueWithTime*>(data.constData() + pos);
  499. int address = (svt->addr2 << 16) | (svt->addr1 << 8) | svt->addr0;
  500. // 标度化值(2字节有符号整数)
  501. short scaledValue = (svt->value_high << 8) | svt->value_low;
  502. float value = static_cast<float>(scaledValue); // 可根据实际标度转换为工程值
  503. // 解析时标
  504. QDateTime timestamp = cp56TimeToDateTime(svt->time);
  505. // 更新测量值(带时标)
  506. QMutexLocker locker(&m_statusMutex);
  507. MeasuredValue mv;
  508. mv.value = value;
  509. mv.valid = !svt->qds.iv;
  510. mv.overflow = svt->qds.ov;
  511. mv.blocked = svt->qds.bl;
  512. mv.substituted = svt->qds.sb;
  513. mv.timestamp = timestamp; // 使用报文中的时标
  514. m_measuredValues[address] = mv;
  515. emit measuredValueUpdated(address, value, !svt->qds.iv);
  516. emit logMessage(QString("Scaled Value with time %1: %2, Valid: %3, Time: %4")
  517. .arg(address)
  518. .arg(value)
  519. .arg(!svt->qds.iv)
  520. .arg(timestamp.toString("yyyy-MM-dd hh:mm:ss.zzz")));
  521. pos += sizeof(ScaledValueWithTime);
  522. }
  523. break;
  524. }
  525. case 36:
  526. { // Measured value, short float point value w/CP56Time2a (带时标短浮点数)
  527. while (pos + sizeof(SFP104_T) <= data.size())
  528. {
  529. const SFP104_T *sfpt = reinterpret_cast<const SFP104_T *>(data.constData() + pos);
  530. int address = (sfpt->addr2 << 16) | (sfpt->addr1 << 8) | sfpt->addr0;
  531. float value = bytesToFloat(sfpt->val);
  532. QDateTime timestamp = cp56TimeToDateTime(sfpt->time);
  533. updateMeasuredValue(address, value, !sfpt->qds.iv, sfpt->qds.ov, sfpt->qds.bl, sfpt->qds.sb);
  534. emit logMessage(QString("Short Float with time %1: %2, Valid: %3, Time: %4")
  535. .arg(address)
  536. .arg(value)
  537. .arg(!sfpt->qds.iv)
  538. .arg(timestamp.toString("yyyy-MM-dd hh:mm:ss.zzz")));
  539. pos += sizeof(SFP104_T);
  540. }
  541. }
  542. break;
  543. // 新增:遥控命令确认
  544. case 45: // Single command (单命令)
  545. case 46: // Double command (双命令)
  546. case 59:
  547. processControlCommand(header, data.mid(pos), cause);
  548. break;
  549. // 新增:设值命令确认
  550. case 50: // Setpoint command, normalized value (设点命令规范化值)
  551. case 51: // Setpoint command, normalized value with time tag
  552. //case 100: // Setpoint command, short floating point value (设点命令短浮点数)
  553. processSetpointCommand(header, data.mid(pos), cause);
  554. break;
  555. case 140: // Setpoint command, normalized value with time tag
  556. {
  557. if (sizeof(ER140_Head) <= data.size())
  558. {
  559. const ER140_Head *spt = reinterpret_cast<const ER140_Head *>(data.constData());
  560. // 信息对象地址
  561. int msgadder = (spt->msgadder3 << 16) | (spt->msgadder2 << 8) | spt->msgadder1;
  562. // 装置地址 -- 四遥表中装置地址
  563. int eqpmtadder = (spt->eqpmtadder2 << 8) | spt->eqpmtadder1;
  564. // 信息序号
  565. int msgid = (spt->addr2 << 8) | spt->addr1;
  566. // 双点信息 合2 分1
  567. int dpvalue = spt->dpvalue;
  568. // 相对时间
  569. int dsTime = (spt->dsTime2 << 8) | spt->dsTime1;
  570. // 故障序号
  571. int erid = (spt->erid2 << 8) | spt->erid1;
  572. // 时标
  573. QDateTime timestamp = cp56TimeToDateTime(spt->time);
  574. // 故障电量个数
  575. int erNum = spt->num;
  576. QString strTemp = QString("ER with timer Type: %1; msgadder: %2; eqpmtadder: %3; msgid: %4; dpvalue: %5; erid: %6; erNum: 57")
  577. .arg(spt->head.type)
  578. .arg(msgadder)
  579. .arg(eqpmtadder)
  580. .arg(msgid)
  581. .arg(dpvalue)
  582. .arg(erid)
  583. .arg(erNum);
  584. emit logMessage(strTemp);
  585. pos = sizeof(ER140_Head);
  586. int nIndex = 0;
  587. while (pos + sizeof(ER140_Data) + sizeof(unsigned char) <= data.size())
  588. {
  589. const ER140_Data *spt1 = reinterpret_cast<const ER140_Data *>(data.constData() + pos);
  590. QString strReset = "ER_Data" + QString::number(nIndex) + "/" + QString::number(spt->num) + " subType=" + QString::number(spt1->ERType);
  591. QString strData = "";
  592. pos += sizeof(ER142_Data);
  593. int nErIndex = (spt1->Num2 << 8) | spt1->Num1;
  594. strData = " nErIndex = " + QString::number(nErIndex);
  595. strReset += strData;
  596. switch (spt1->ERType)
  597. {
  598. case 1:
  599. case 2:
  600. {
  601. unsigned char pCharData = 0;
  602. memcpy(&pCharData, data.constData() + pos, sizeof(unsigned char));
  603. strData = QString::number(pCharData);
  604. pos += sizeof(unsigned char);
  605. }
  606. break;
  607. case 3:
  608. case 4:
  609. {
  610. unsigned char Data1[2] = {0};
  611. memcpy(Data1, data.constData() + pos, sizeof(unsigned char) * 2);
  612. int value = (Data1[1] << 8) | Data1[0];
  613. strData = QString::number(value);
  614. pos += sizeof(unsigned char) * 2;
  615. }
  616. break;
  617. case 5:
  618. case 6:
  619. case 7:
  620. {
  621. unsigned char Data1[4] = {0};
  622. memcpy(Data1, data.constData() + pos, sizeof(unsigned char) * 4);
  623. if (spt1->ERType == 7)
  624. {
  625. float value = bytesToFloat(Data1);
  626. strData = QString::number(value);
  627. }
  628. else
  629. {
  630. int value = (Data1[3] << 24) | (Data1[2] << 16) | (Data1[1] << 8) | Data1[0];
  631. strData = QString::number(value);
  632. }
  633. pos += sizeof(unsigned char) * 4;
  634. }
  635. break;
  636. case 8:
  637. {
  638. char Data1[8] = {0};
  639. memcpy(Data1, data.constData() + pos, sizeof(unsigned char) * 8);
  640. strData = QString::fromLocal8Bit(Data1, 8);
  641. pos += sizeof(unsigned char) * 8;
  642. }
  643. break;
  644. }
  645. // pos += sizeof(unsigned char)*8;
  646. strReset.append(" Data=" + strData);
  647. emit logMessage(strReset);
  648. nIndex++;
  649. }
  650. }
  651. }
  652. break;
  653. case 142: // Setpoint command, normalized value with time tag
  654. {
  655. if (sizeof(ER142_Head) <= data.size())
  656. {
  657. const ER142_Head *spt = reinterpret_cast<const ER142_Head *>(data.constData());
  658. int eqpmtadder = (spt->eqpmtadder2 << 8) | spt->eqpmtadder1;
  659. int ERID = (spt->addr2 << 8) | spt->addr1;
  660. int eventnum = spt->evetnum;
  661. QString strTemp = QString("ER with timer Type: %1; eqpmtadder: %2; msgid: %3; eventnum: %4").arg(spt->head.type).arg(eqpmtadder).arg(ERID).arg(eventnum);
  662. emit logMessage(strTemp);
  663. pos = sizeof(ER142_Head);
  664. int nIndex = 0;
  665. while (pos + sizeof(ER142_Data) + sizeof(unsigned char) <= data.size())
  666. {
  667. const ER142_Data *spt1 = reinterpret_cast<const ER142_Data *>(data.constData() + pos);
  668. QString strReset = "ER_Data" + QString::number(nIndex) + "/" + QString::number(spt->ernum) + " subType=" + QString::number(spt1->ERType);
  669. QString strData = "";
  670. pos += sizeof(ER142_Data);
  671. int nErIndex = (spt1->Num2 << 8) | spt1->Num1;
  672. strData = " nErIndex = " + QString::number(nErIndex);
  673. strReset += strData;
  674. switch (spt1->ERType)
  675. {
  676. case 1:
  677. case 2:
  678. {
  679. unsigned char pCharData = 0;
  680. memcpy(&pCharData, data.constData() + pos, sizeof(unsigned char));
  681. strData = QString::number(pCharData);
  682. pos += sizeof(unsigned char);
  683. }
  684. break;
  685. case 3:
  686. case 4:
  687. {
  688. unsigned char Data1[2] = {0};
  689. memcpy(Data1, data.constData() + pos, sizeof(unsigned char) * 2);
  690. int value = (Data1[1] << 8) | Data1[0];
  691. strData = QString::number(value);
  692. pos += sizeof(unsigned char) * 2;
  693. }
  694. break;
  695. case 5:
  696. case 6:
  697. case 7:
  698. {
  699. unsigned char Data1[4] = {0};
  700. memcpy(Data1, data.constData() + pos, sizeof(unsigned char) * 4);
  701. if (spt1->ERType == 7)
  702. {
  703. float value = bytesToFloat(Data1);
  704. strData = QString::number(value);
  705. }
  706. else
  707. {
  708. int value = (Data1[3] << 24) | (Data1[2] << 16) | (Data1[1] << 8) | Data1[0];
  709. strData = QString::number(value);
  710. }
  711. pos += sizeof(unsigned char) * 4;
  712. }
  713. break;
  714. case 8:
  715. {
  716. char Data1[8] = {0};
  717. memcpy(Data1, data.constData() + pos, sizeof(unsigned char) * 8);
  718. strData = QString::fromLocal8Bit(Data1, 8);
  719. pos += sizeof(unsigned char) * 8;
  720. }
  721. break;
  722. }
  723. // pos += sizeof(unsigned char)*8;
  724. strReset.append(" Data=" + strData);
  725. emit logMessage(strReset);
  726. nIndex++;
  727. }
  728. }
  729. }
  730. break;
  731. case 143: // Setpoint command, normalized value with time tag
  732. {
  733. if (sizeof(ER143_Head) <= data.size())
  734. {
  735. const ER143_Head *spt = reinterpret_cast<const ER143_Head *>(data.constData());
  736. // 信息对象地址
  737. int msgadder = (spt->msgadder3 << 16) | (spt->msgadder2 << 8) | spt->msgadder1;
  738. // 装置地址
  739. int eqpmtadder = (spt->eqpmtadder2 << 8) | spt->eqpmtadder1;
  740. // 故障序号
  741. int ERID = (spt->addr2 << 8) | spt->addr1;
  742. int NGD = spt->ngd; // 大循环的范围
  743. // 时间
  744. QString strTimeData = "";
  745. pos = sizeof(ER143_Head);
  746. for (int nIndex = 0; nIndex < NGD; nIndex++)
  747. {
  748. int nEventNum = 0;
  749. if (msgadder == 0)
  750. {
  751. const ER143_SData *SData = reinterpret_cast<const ER143_SData *>(data.constData() + pos);
  752. pos += sizeof(ER143_LData);
  753. int nTimeData = (SData->nTime[1] << 8) | SData->nTime[0];
  754. nEventNum = SData->nNumber;
  755. strTimeData = "DTime=" + QString::number(nTimeData) + "ms";
  756. }
  757. else
  758. {
  759. const ER143_LData *SData = reinterpret_cast<const ER143_LData *>(data.constData() + pos);
  760. pos += sizeof(ER143_LData);
  761. int nTimeData = (SData->nTime[3] << 24) | (SData->nTime[2] << 16) | (SData->nTime[1] << 8) | SData->nTime[0];
  762. nEventNum = SData->nNumber;
  763. strTimeData = "DTime=" + QString::number(nTimeData) + "ms";
  764. }
  765. emit logMessage("Event" + QString::number(nIndex) + " " + strTimeData);
  766. for (int subIndex = 0; subIndex < nEventNum; subIndex++)
  767. {
  768. const ER143_Data *spt1 = reinterpret_cast<const ER143_Data *>(data.constData() + pos);
  769. QString strReset = "ER_Data" + QString::number(subIndex) + "/" + nEventNum + " subType=" + QString::number(spt1->ERType);
  770. QString strData = "";
  771. pos += sizeof(ER143_Data);
  772. int nErIndex = (spt1->Num2 << 8) | spt1->Num1;
  773. strData = " nErIndex = " + QString::number(nErIndex);
  774. strReset += strData;
  775. switch (spt1->ERType)
  776. {
  777. case 1:
  778. case 2:
  779. {
  780. unsigned char pCharData = 0;
  781. memcpy(&pCharData, data.constData() + pos, sizeof(unsigned char));
  782. strData = QString::number(pCharData);
  783. pos += sizeof(unsigned char);
  784. }
  785. break;
  786. case 3:
  787. case 4:
  788. {
  789. unsigned char Data1[2] = {0};
  790. memcpy(Data1, data.constData() + pos, sizeof(unsigned char) * 2);
  791. int value = (Data1[1] << 8) | Data1[0];
  792. strData = QString::number(value);
  793. pos += sizeof(unsigned char) * 2;
  794. }
  795. break;
  796. case 5:
  797. case 6:
  798. case 7:
  799. {
  800. unsigned char Data1[4] = {0};
  801. memcpy(Data1, data.constData() + pos, sizeof(unsigned char) * 4);
  802. if (spt1->ERType == 7)
  803. {
  804. float value = bytesToFloat(Data1);
  805. strData = QString::number(value);
  806. }
  807. else
  808. {
  809. int value = (Data1[3] << 24) | (Data1[2] << 16) | (Data1[1] << 8) | Data1[0];
  810. strData = QString::number(value);
  811. }
  812. pos += sizeof(unsigned char) * 4;
  813. }
  814. break;
  815. case 8:
  816. {
  817. char Data1[8] = {0};
  818. memcpy(Data1, data.constData() + pos, sizeof(unsigned char) * 8);
  819. strData = QString::fromLocal8Bit(Data1, 8);
  820. pos += sizeof(unsigned char) * 8;
  821. }
  822. break;
  823. }
  824. strReset.append(" Data=" + strData);
  825. emit logMessage(strReset);
  826. }
  827. }
  828. QString strTemp = QString("ER with timer Type: %1; msgadder: %2;eqpmtadder: %3; ERID: %4; NGD: %5").arg(spt->head.type).arg(msgadder).arg(eqpmtadder).arg(ERID).arg(NGD);
  829. emit logMessage(strTemp);
  830. }
  831. }
  832. break;
  833. case 100: { // Interrogation command (总召)
  834. if (cause == 7) { // 激活确认
  835. emit logMessage("Interrogation activation confirmation received");
  836. // 可以在这里触发数据处理或状态更新
  837. }
  838. else if (cause == 10) { // 激活终止
  839. sendSMessage();
  840. emit logMessage("Interrogation activation termination received");
  841. // 总召唤完成,可以在这里发送完成信号
  842. //emit interrogationCompleted(true);
  843. }
  844. else if (cause == 47) { // 命令出错
  845. emit logMessage("Interrogation command error");
  846. //emit interrogationCompleted(false);
  847. }
  848. break;
  849. }
  850. case 145://定值返回数据处理
  851. {
  852. FixValueCommand(header, data.mid(pos), cause);
  853. }
  854. break;
  855. case F_FR_NA_1: // 文件准备就绪
  856. processFileReady(header, data.mid(pos), cause);
  857. break;
  858. case F_SR_NA_1: // 节准备就绪
  859. processSectionReady(header, data.mid(pos), cause);
  860. break;
  861. case F_SC_NA_1: // 文件召唤/选择
  862. processFileCall(header, data.mid(pos), cause);
  863. break;
  864. case F_LS_NA_1: // 最后的节/段
  865. processLastSection(header, data.mid(pos), cause);
  866. break;
  867. case F_AF_NA_1: // 认可文件/段
  868. processFileAck(header, data.mid(pos), cause);
  869. break;
  870. case F_SG_NA_1: // 段
  871. processFileSegment(header, data.mid(pos), cause);
  872. break;
  873. case F_DR_NA_1: // 目录
  874. processDirectory(header, data.mid(pos), cause);
  875. break;
  876. default:
  877. emit logMessage(QString("Unhandled ASDU type: %1").arg(header->type));
  878. break;
  879. }
  880. }
  881. // 字节序列转换为浮点数
  882. float IEC104MasterQt::bytesToFloat(const unsigned char *bytes)
  883. {
  884. // IEC 104使用小端字节序
  885. uint32_t temp = (bytes[3] << 24) | (bytes[2] << 16) | (bytes[1] << 8) | bytes[0];
  886. return *reinterpret_cast<float *>(&temp);
  887. }
  888. // CP56Time2a时间格式转换为QDateTime
  889. QDateTime IEC104MasterQt::cp56TimeToDateTime(const unsigned char *timeData)
  890. {
  891. // CP56Time2a格式:
  892. // 字节1-2: 毫秒(0-59999)
  893. // 字节3: 分钟(0-59)
  894. // 字节4: 小时(0-23)
  895. // 字节5: 日期(bit0-4:日, bit5-7:星期)
  896. // 字节6: 月(0-12)
  897. // 字节7: 年(0-99, 相对于2000年)
  898. int milliseconds = (timeData[1] << 8) | timeData[0];
  899. int seconds = milliseconds / 1000;
  900. milliseconds = milliseconds % 1000;
  901. int minutes = timeData[2] & 0x3F; // 去除RES1和RES2位
  902. int hours = timeData[3] & 0x1F; // 去除RES位
  903. int day = timeData[4] & 0x1F;
  904. int month = timeData[5] & 0x0F;
  905. int year = timeData[6] & 0x7F + 2000;
  906. QDate date(year, month, day);
  907. QTime time(hours, minutes, seconds, milliseconds);
  908. return QDateTime(date, time, Qt::UTC); // IEC 104使用UTC时间
  909. }
  910. QByteArray IEC104MasterQt::dateTimeToCp56Time2a(const QDateTime &dateTime)
  911. {
  912. QByteArray cp56Time(7, 0);
  913. // 转换为本地时区(104规约通常使用本地时间)
  914. QDateTime localDateTime = dateTime.toLocalTime();
  915. // 获取时间组件
  916. QDate date = localDateTime.date();
  917. QTime time = localDateTime.time();
  918. // 字节1-2:毫秒(低字节在前)
  919. int milliseconds = time.msec() + time.second() * 1000;
  920. cp56Time[0] = static_cast<char>(milliseconds & 0xFF); // 低字节
  921. cp56Time[1] = static_cast<char>((milliseconds >> 8) & 0xFF); // 高字节
  922. // 字节3:分钟
  923. cp56Time[2] = static_cast<char>(time.minute() & 0x3F); // 只取低6位
  924. // 字节4:小时
  925. cp56Time[3] = static_cast<char>(time.hour() & 0x1F); // 只取低5位
  926. // 字节5:月的日期(1-31)
  927. cp56Time[4] = static_cast<char>(date.day() & 0x1F); // 只取低5位
  928. // 字节6:星期(位0-2) + 月的索引(位4-7)
  929. int dayOfWeek = date.dayOfWeek(); // 1=周一, 7=周日
  930. if (dayOfWeek == 7)
  931. dayOfWeek = 0; // 104规约中0表示周日
  932. else
  933. dayOfWeek--; // 调整为0-6
  934. int month = date.month() - 1; // 月份索引(0-11)
  935. cp56Time[5] = static_cast<char>((month << 4) | (dayOfWeek & 0x07));
  936. // 字节7:年(0-99,相对于2000年)
  937. int year = date.year() - 2000;
  938. cp56Time[6] = static_cast<char>(year & 0x7F);
  939. return cp56Time;
  940. }
  941. void IEC104MasterQt::updateEquipmentStatus(int address, int status, int nSingle)
  942. {
  943. QMutexLocker locker(&m_statusMutex);
  944. m_equipmentStatus[address] = status;
  945. emit equipmentStatusUpdated(address, status, nSingle);
  946. }
  947. void IEC104MasterQt::updateMeasuredValue(int address, float value, bool valid,
  948. bool overflow, bool blocked, bool substituted)
  949. {
  950. QMutexLocker locker(&m_statusMutex);
  951. MeasuredValue mv;
  952. mv.value = value;
  953. mv.valid = valid;
  954. mv.overflow = overflow;
  955. mv.blocked = blocked;
  956. mv.substituted = substituted;
  957. mv.timestamp = QDateTime::currentDateTime();
  958. m_measuredValues[address] = mv;
  959. emit measuredValueUpdated(address, value, valid);
  960. }
  961. QMap<int, MeasuredValue> IEC104MasterQt::getMeasuredValues() const
  962. {
  963. QMutexLocker locker(&m_statusMutex);
  964. return m_measuredValues;
  965. }
  966. QMap<int, QMap<int, QMap<int, FixDataValue> > > IEC104MasterQt::getFixDataValues() const
  967. {
  968. QMutexLocker locker(&m_statusMutex);
  969. return m_fixValues;
  970. }
  971. QByteArray IEC104MasterQt::toHexString(const QByteArray &data)
  972. {
  973. QByteArray result;
  974. for (int i = 0; i < data.size(); ++i)
  975. {
  976. if (i > 0)
  977. result.append(' ');
  978. result.append(QByteArray::number(static_cast<unsigned char>(data[i]), 16).right(2).rightJustified(2, '0'));
  979. }
  980. return result;
  981. }
  982. void IEC104MasterQt::processFormatU(const APCI &apci)
  983. {
  984. int command = apci.field1;
  985. switch (command)
  986. {
  987. case 0x07: // STARTDT CON
  988. emit logMessage("Received STARTDT confirmation");
  989. break;
  990. case 0x0B: // STOPDT CON
  991. emit logMessage("Received STOPDT confirmation");
  992. break;
  993. case 0x13: // STOPDT ACT
  994. emit logMessage("Received STOPDT ACT");
  995. // 应该回复 STOPDT CON
  996. sendUMessage(0x23);
  997. break;
  998. case 0x23: // STOPDT CON
  999. emit logMessage("Received STOPDT confirmation");
  1000. break;
  1001. case 0x43: // TESTFR CON
  1002. emit logMessage("Received TESTFR confirmation");
  1003. // 应该回复 TESTFR CON
  1004. sendUMessage(0x83);
  1005. break;
  1006. case 0x83: // TESTFR CON
  1007. emit logMessage("Received TESTFR confirmation");
  1008. break;
  1009. default:
  1010. emit logMessage(QString("Unknown U format command: 0x%1").arg(command, 2, 16, QLatin1Char('0')));
  1011. break;
  1012. }
  1013. }
  1014. QString IEC104MasterQt::getDataValue(int &nPos, const QByteArray &data, int nType)
  1015. {
  1016. QString strData="";
  1017. switch (nType)
  1018. {
  1019. case 1:
  1020. case 2:
  1021. {
  1022. unsigned char pCharData = 0;
  1023. memcpy(&pCharData, data.constData() + nPos, sizeof(unsigned char));
  1024. strData = QString::number(pCharData);
  1025. nPos += sizeof(unsigned char);
  1026. }
  1027. break;
  1028. case 3:
  1029. case 4:
  1030. {
  1031. unsigned char Data1[2] = {0};
  1032. memcpy(Data1, data.constData() + nPos, sizeof(unsigned char) * 2);
  1033. int value = (Data1[1] << 8) | Data1[0];
  1034. strData = QString::number(value);
  1035. nPos += sizeof(unsigned char) * 2;
  1036. }
  1037. break;
  1038. case 5:
  1039. case 6:
  1040. case 7:
  1041. {
  1042. unsigned char Data1[4] = {0};
  1043. memcpy(Data1, data.constData() + nPos, sizeof(unsigned char) * 4);
  1044. if (nType == 7)
  1045. {
  1046. float value = bytesToFloat(Data1);
  1047. strData = QString::number(value);
  1048. }
  1049. else
  1050. {
  1051. int value = (Data1[3] << 24) | (Data1[2] << 16) | (Data1[1] << 8) | Data1[0];
  1052. strData = QString::number(value);
  1053. }
  1054. nPos += sizeof(unsigned char) * 4;
  1055. }
  1056. break;
  1057. case 8:
  1058. {
  1059. char Data1[8] = {0};
  1060. memcpy(Data1, data.constData() + nPos, sizeof(unsigned char) * 8);
  1061. strData = QString::fromLocal8Bit(Data1, 8);
  1062. nPos += sizeof(unsigned char) * 8;
  1063. }
  1064. break;
  1065. }
  1066. return strData;
  1067. }
  1068. QString IEC104MasterQt::Data2Value(int &nPos, const QByteArray &data, int nType)
  1069. {
  1070. if (nPos < 0 || nPos >= data.size()) {
  1071. return QString();
  1072. }
  1073. switch (nType) {
  1074. case TYPE_UI8: {
  1075. if (nPos + 1 > data.size()) return QString();
  1076. quint8 value = static_cast<quint8>(data.at(nPos));
  1077. nPos += 1;
  1078. return QString::number(value);
  1079. }
  1080. case TYPE_I8: {
  1081. if (nPos + 1 > data.size()) return QString();
  1082. qint8 value = static_cast<qint8>(data.at(nPos));
  1083. nPos += 1;
  1084. return QString::number(value);
  1085. }
  1086. case TYPE_UI16: {
  1087. if (nPos + 2 > data.size()) return QString();
  1088. quint16 value = *reinterpret_cast<const quint16*>(data.constData() + nPos);
  1089. value = qFromLittleEndian(value);
  1090. nPos += 2;
  1091. return QString::number(value);
  1092. }
  1093. case TYPE_I16: {
  1094. if (nPos + 2 > data.size()) return QString();
  1095. qint16 value = *reinterpret_cast<const qint16*>(data.constData() + nPos);
  1096. value = qFromLittleEndian(value);
  1097. nPos += 2;
  1098. return QString::number(value);
  1099. }
  1100. case TYPE_UI32: {
  1101. if (nPos + 4 > data.size()) return QString();
  1102. quint32 value = *reinterpret_cast<const quint32*>(data.constData() + nPos);
  1103. value = qFromLittleEndian(value);
  1104. nPos += 4;
  1105. return QString::number(value);
  1106. }
  1107. case TYPE_I32: {
  1108. if (nPos + 4 > data.size()) return QString();
  1109. qint32 value = *reinterpret_cast<const qint32*>(data.constData() + nPos);
  1110. value = qFromLittleEndian(value);
  1111. nPos += 4;
  1112. return QString::number(value);
  1113. }
  1114. case TYPE_F32: {
  1115. if (nPos + 4 > data.size()) return QString();
  1116. // 使用手动字节交换方法处理浮点数
  1117. unsigned char Data1[4] = {0};
  1118. memcpy(Data1, data.constData() + nPos, sizeof(unsigned char) * 4);
  1119. float value = bytesToFloat(Data1);
  1120. nPos += 4;
  1121. return QString::number(value);
  1122. }
  1123. case TYPE_S64: {
  1124. if (nPos + 8 > data.size()) return QString();
  1125. char Data1[8] = {0};
  1126. memcpy(Data1, data.constData() + nPos, sizeof(unsigned char) * 8);
  1127. nPos += sizeof(unsigned char) * 8;
  1128. // 注意:浮点数的字节序处理需要特别小心
  1129. // 这里假设系统字节序与数据字节序一致,实际使用时可能需要手动转换
  1130. nPos += 8;
  1131. return QString::fromLocal8Bit(Data1, 8);
  1132. }
  1133. default:
  1134. return QString();
  1135. }
  1136. }
  1137. bool IEC104MasterQt::Value2Data(QByteArray &data, int nType, const QString &strData)
  1138. {
  1139. bool ok = false;
  1140. switch (nType) {
  1141. case TYPE_UI8: {
  1142. quint8 value = static_cast<quint8>(strData.toUShort(&ok));
  1143. if (ok) {
  1144. data.append(static_cast<char>(value));
  1145. }
  1146. break;
  1147. }
  1148. case TYPE_I8: {
  1149. qint8 value = static_cast<qint8>(strData.toShort(&ok));
  1150. if (ok) {
  1151. data.append(static_cast<char>(value));
  1152. }
  1153. break;
  1154. }
  1155. case TYPE_UI16: {
  1156. quint16 value = static_cast<quint16>(strData.toUShort(&ok));
  1157. if (ok) {
  1158. quint16 leValue = qToLittleEndian(value);
  1159. data.append(reinterpret_cast<const char*>(&leValue), 2);
  1160. }
  1161. break;
  1162. }
  1163. case TYPE_I16: {
  1164. qint16 value = static_cast<qint16>(strData.toShort(&ok));
  1165. if (ok) {
  1166. qint16 leValue = qToLittleEndian(value);
  1167. data.append(reinterpret_cast<const char*>(&leValue), 2);
  1168. }
  1169. break;
  1170. }
  1171. case TYPE_UI32: {
  1172. quint32 value = strData.toUInt(&ok);
  1173. if (ok) {
  1174. quint32 leValue = qToLittleEndian(value);
  1175. data.append(reinterpret_cast<const char*>(&leValue), 4);
  1176. }
  1177. break;
  1178. }
  1179. case TYPE_I32: {
  1180. qint32 value = strData.toInt(&ok);
  1181. if (ok) {
  1182. qint32 leValue = qToLittleEndian(value);
  1183. data.append(reinterpret_cast<const char*>(&leValue), 4);
  1184. }
  1185. break;
  1186. }
  1187. case TYPE_F32: {
  1188. float value = strData.toFloat(&ok);
  1189. if (ok) {
  1190. // 将浮点数转换为小端序字节数组
  1191. union {
  1192. float f;
  1193. quint8 b[4];
  1194. } converter;
  1195. converter.f = value;
  1196. // 小端序:先存低位字节
  1197. data.append(static_cast<char>(converter.b[0]));
  1198. data.append(static_cast<char>(converter.b[1]));
  1199. data.append(static_cast<char>(converter.b[2]));
  1200. data.append(static_cast<char>(converter.b[3]));
  1201. }
  1202. break;
  1203. }
  1204. case TYPE_S64: {
  1205. char value[8] ="";
  1206. if (strData.size()!=8) {
  1207. memcpy(value,strData.toLocal8Bit(),sizeof(value));
  1208. data.append(reinterpret_cast<const char*>(&value), 8);
  1209. }
  1210. }
  1211. default:
  1212. ok = false;
  1213. break;
  1214. }
  1215. return ok;
  1216. }
  1217. bool IEC104MasterQt::sendSingleCommand(int commonAddress, int objectAddress, bool command, bool selectExecute)
  1218. {
  1219. if (!m_connected)
  1220. {
  1221. emit logMessage("Not connected, cannot send single command");
  1222. return false;
  1223. }
  1224. QByteArray data;
  1225. // ASDU头
  1226. data.append(static_cast<char>(0x2D)); // 类型: 45-单命令, 46-双命令
  1227. data.append(static_cast<char>(0x01)); // 可变结构限定词: 1个信息对象
  1228. data.append(static_cast<char>(0x06)); // 传送原因: 6-激活, 8-停止激活
  1229. data.append(static_cast<char>(0x00));
  1230. data.append(static_cast<char>(commonAddress & 0xFF)); // 公共地址低字节
  1231. data.append(static_cast<char>((commonAddress >> 8) & 0xFF)); // 公共地址高字节
  1232. // 信息对象地址
  1233. data.append(static_cast<char>(objectAddress & 0xFF));
  1234. data.append(static_cast<char>((objectAddress >> 8) & 0xFF));
  1235. data.append(static_cast<char>((objectAddress >> 16) & 0xFF));
  1236. // 命令值
  1237. unsigned char commandValue = command ? 0x01 : 0x00;
  1238. if (selectExecute)
  1239. {
  1240. // QOC (命令质量)
  1241. data.append(static_cast<char>(commandValue+0x80)); // 通常命令
  1242. }
  1243. else
  1244. {
  1245. data.append(static_cast<char>(commandValue));
  1246. }
  1247. // if (selectExecute)
  1248. // {
  1249. // // QOC (命令质量)
  1250. // data.append(static_cast<char>(0x80)); // 通常命令
  1251. // }
  1252. // else
  1253. {
  1254. data.append(static_cast<char>(0x00));
  1255. }
  1256. emit logMessage(QString("Sending single command: Address=%1, Command=%2, SelectExecute=%3")
  1257. .arg(objectAddress)
  1258. .arg(command ? "ON" : "OFF")
  1259. .arg(selectExecute));
  1260. // 保存待确认的命令
  1261. if (selectExecute)
  1262. {
  1263. QMutexLocker locker(&m_commandMutex);
  1264. ControlCommand cmd;
  1265. cmd.address = objectAddress;
  1266. cmd.type = ControlCommandType::SinglePoint;
  1267. cmd.value = command;
  1268. cmd.commonAddress = commonAddress;
  1269. cmd.timestamp = QDateTime::currentDateTime();
  1270. m_pendingCommands[objectAddress] = cmd;
  1271. }
  1272. return sendIMessage(data);
  1273. }
  1274. // 新增:发送双点命令
  1275. bool IEC104MasterQt::sendDoubleCommand(int commonAddress, int objectAddress, int command, bool selectExecute)
  1276. {
  1277. if (!m_connected)
  1278. {
  1279. emit logMessage("Not connected, cannot send double command");
  1280. return false;
  1281. }
  1282. QByteArray data;
  1283. // ASDU头
  1284. data.append(static_cast<char>(selectExecute ? 0x2E : 0x2F)); // 类型: 46-双命令, 47-双命令停止激活
  1285. data.append(static_cast<char>(0x01)); // 可变结构限定词: 1个信息对象
  1286. data.append(static_cast<char>(selectExecute ? 0x06 : 0x08)); // 传送原因: 6-激活, 8-停止激活
  1287. data.append(static_cast<char>(0x00));
  1288. data.append(static_cast<char>(commonAddress & 0xFF)); // 公共地址低字节
  1289. data.append(static_cast<char>((commonAddress >> 8) & 0xFF)); // 公共地址高字节
  1290. // 信息对象地址
  1291. data.append(static_cast<char>(objectAddress & 0xFF));
  1292. data.append(static_cast<char>((objectAddress >> 8) & 0xFF));
  1293. data.append(static_cast<char>((objectAddress >> 16) & 0xFF));
  1294. // 双点命令值 (1-分, 2-合)
  1295. unsigned char commandValue = static_cast<unsigned char>(command & 0x03);
  1296. data.append(static_cast<char>(commandValue));
  1297. // QOC (命令质量)
  1298. data.append(static_cast<char>(0x00)); // 通常命令
  1299. emit logMessage(QString("Sending double command: Address=%1, Command=%2, SelectExecute=%3")
  1300. .arg(objectAddress)
  1301. .arg(command)
  1302. .arg(selectExecute));
  1303. // 保存待确认的命令
  1304. if (selectExecute)
  1305. {
  1306. QMutexLocker locker(&m_commandMutex);
  1307. ControlCommand cmd;
  1308. cmd.address = objectAddress;
  1309. cmd.type = ControlCommandType::DoublePoint;
  1310. cmd.value = (command == 2); // 转换为bool: 1=false, 2=true
  1311. cmd.commonAddress = commonAddress;
  1312. cmd.timestamp = QDateTime::currentDateTime();
  1313. m_pendingCommands[objectAddress] = cmd;
  1314. }
  1315. return sendIMessage(data);
  1316. }
  1317. // 新增:发送双点命令
  1318. bool IEC104MasterQt::sendDoubleCommand2(int commonAddress, int objectAddress, int command, bool selectExecute,bool isRun)
  1319. {
  1320. if (!m_connected)
  1321. {
  1322. emit logMessage("Not connected, cannot send double command");
  1323. return false;
  1324. }
  1325. QByteArray data;
  1326. // ASDU头
  1327. data.append(static_cast<char>(0x3B)); //
  1328. data.append(static_cast<char>(0x01)); // 可变结构限定词: 1个信息对象
  1329. if (selectExecute)
  1330. {
  1331. data.append(static_cast<char>(isRun ? 0x06 : 0x08)); // 传送原因: 6-激活,7-确认 8-停止激活
  1332. }
  1333. else
  1334. {
  1335. data.append(static_cast<char>(isRun ? 0x06 : 0x08)); // 传送原因: 6-激活,7-确认 8-停止激活
  1336. }
  1337. data.append(static_cast<char>(0x00));
  1338. data.append(static_cast<char>(commonAddress & 0xFF)); // 公共地址低字节
  1339. data.append(static_cast<char>((commonAddress >> 8) & 0xFF)); // 公共地址高字节
  1340. // 信息对象地址
  1341. data.append(static_cast<char>(objectAddress & 0xFF));
  1342. data.append(static_cast<char>((objectAddress >> 8) & 0xFF));
  1343. data.append(static_cast<char>((objectAddress >> 16) & 0xFF));
  1344. // 双点命令值 (1-分, 2-合)
  1345. if (selectExecute)
  1346. {
  1347. unsigned char commandValue = static_cast<unsigned char>(command & 0x03);
  1348. data.append(static_cast<char>(commandValue)+0x80);
  1349. }else
  1350. {
  1351. unsigned char commandValue = static_cast<unsigned char>(command & 0x03);
  1352. data.append(static_cast<char>(commandValue));
  1353. }
  1354. // QOC (命令质量)
  1355. // data.append(static_cast<char>(0x00)); // 通常命令
  1356. // 时标
  1357. QDateTime timestamp = QDateTime::currentDateTime();
  1358. QByteArray date = dateTimeToCp56Time2a(timestamp);
  1359. data.append(date);
  1360. emit logMessage(QString("Sending double command: Address=%1, Command=%2, SelectExecute=%3")
  1361. .arg(objectAddress)
  1362. .arg(command)
  1363. .arg(selectExecute));
  1364. // 保存待确认的命令
  1365. if (selectExecute)
  1366. {
  1367. QMutexLocker locker(&m_commandMutex);
  1368. ControlCommand cmd;
  1369. cmd.address = objectAddress;
  1370. cmd.type = ControlCommandType::DoublePoint;
  1371. cmd.value = (command == 2); // 转换为bool: 1=false, 2=true
  1372. cmd.commonAddress = commonAddress;
  1373. cmd.timestamp = QDateTime::currentDateTime();
  1374. m_pendingCommands[objectAddress] = cmd;
  1375. }
  1376. return sendIMessage(data);
  1377. }
  1378. bool IEC104MasterQt::sendDoubleCommand3(int commonAddress, int nEqpmtID, int objectAddress, int command, bool selectExecute /*= true*/, bool isRun /*= true*/)
  1379. {
  1380. if (!m_connected)
  1381. {
  1382. emit logMessage("Not connected, cannot send double command");
  1383. return false;
  1384. }
  1385. QByteArray data;
  1386. // ASDU头
  1387. data.append(static_cast<char>(0x3B)); // 类型: 46-双命令, 47-双命令停止激活
  1388. data.append(static_cast<char>(0x01)); // 可变结构限定词: 1个信息对象
  1389. data.append(static_cast<char>(isRun ? 0x06 : 0x08)); // 传送原因: 6-激活, 8-停止激活
  1390. data.append(static_cast<char>(0x00));
  1391. data.append(static_cast<char>(commonAddress & 0xFF)); // 公共地址低字节
  1392. data.append(static_cast<char>(( nEqpmtID ) & 0xFF)); // 公共地址高字节
  1393. // 信息对象地址
  1394. data.append(static_cast<char>(objectAddress & 0xFF));
  1395. data.append(static_cast<char>((objectAddress >> 8) & 0xFF));
  1396. data.append(static_cast<char>((objectAddress >> 16) & 0xFF));
  1397. // 双点命令值 (1-分, 2-合)
  1398. if (selectExecute)
  1399. {
  1400. unsigned char commandValue = static_cast<unsigned char>(command & 0x03);
  1401. //data.append(static_cast<char>(commandValue) + 0x80);
  1402. data.append(static_cast<char>(commandValue));
  1403. }
  1404. else
  1405. {
  1406. unsigned char commandValue = static_cast<unsigned char>(command & 0x03);
  1407. data.append(static_cast<char>(commandValue));
  1408. }
  1409. // QOC (命令质量)
  1410. // data.append(static_cast<char>(0x00)); // 通常命令
  1411. // 时标
  1412. QDateTime timestamp = QDateTime::currentDateTime();
  1413. QByteArray date = dateTimeToCp56Time2a(timestamp);
  1414. data.append(date);
  1415. emit logMessage(QString("Sending double command: Address=%1, Command=%2, SelectExecute=%3")
  1416. .arg(objectAddress)
  1417. .arg(command)
  1418. .arg(selectExecute));
  1419. // 保存待确认的命令
  1420. if (selectExecute)
  1421. {
  1422. QMutexLocker locker(&m_commandMutex);
  1423. ControlCommand cmd;
  1424. cmd.address = objectAddress;
  1425. cmd.type = ControlCommandType::DoublePoint;
  1426. cmd.value = (command == 2); // 转换为bool: 1=false, 2=true
  1427. cmd.commonAddress = commonAddress;
  1428. cmd.EqpmtAddress = nEqpmtID;
  1429. cmd.timestamp = QDateTime::currentDateTime();
  1430. m_pendingCommands[objectAddress] = cmd;
  1431. }
  1432. return sendIMessage(data);
  1433. }
  1434. bool IEC104MasterQt::getFixAreaValue(int commonAddress,int objectAddress,int nEqpmtID)
  1435. {
  1436. if (!m_connected)
  1437. {
  1438. emit logMessage("Not connected, cannot send float setpoint");
  1439. return false;
  1440. }
  1441. QByteArray data;
  1442. // ASDU头
  1443. data.append(static_cast<char>(0x91)); // 类型: 145
  1444. data.append(static_cast<char>(0x01)); // 可变结构限定词: 1个信息对象
  1445. data.append(static_cast<char>(0x05)); // 传送原因: 3突发
  1446. data.append(static_cast<char>(0x00));
  1447. data.append(static_cast<char>(commonAddress & 0xFF)); // 公共地址低字节
  1448. data.append(static_cast<char>((commonAddress >> 8) & 0xFF)); // 公共地址高字节
  1449. // 信息对象地址
  1450. data.append(static_cast<char>(objectAddress & 0xFF));
  1451. data.append(static_cast<char>((objectAddress >> 8) & 0xFF));
  1452. data.append(static_cast<char>((objectAddress >> 16) & 0xFF));
  1453. //装置地址
  1454. data.append(static_cast<char>(nEqpmtID & 0xFF)); // 公共地址低字节
  1455. data.append(static_cast<char>((nEqpmtID >> 8) & 0xFF)); // 公共地址高字节
  1456. data.append(static_cast<char>(0xF1)); // 信息序号(INF)
  1457. data.append(static_cast<char>(0x00));//返回信息标识
  1458. data.append(static_cast<char>(0x01));//通用分类数据集数目
  1459. data.append(static_cast<char>(0x00));//通用标识序号
  1460. data.append(static_cast<char>(0x01));//通用标识序号
  1461. data.append(static_cast<char>(0x00));//描述类别
  1462. emit logMessage(QString("Sending normalized setpoint: Address=%1, Value=%2, SelectExecute=%3")
  1463. .arg(commonAddress)
  1464. .arg(objectAddress)
  1465. .arg(nEqpmtID));
  1466. return sendIMessage(data);
  1467. }
  1468. bool IEC104MasterQt::getFixValue(int commonAddress,int objectAddress,int nEqpmtID)
  1469. {
  1470. if (!m_connected)
  1471. {
  1472. emit logMessage("Not connected, cannot send float setpoint");
  1473. return false;
  1474. }
  1475. m_FixData.clear(); //清除前一次记录
  1476. QByteArray data;
  1477. // ASDU头
  1478. data.append(static_cast<char>(0x91)); // 类型: 145
  1479. data.append(static_cast<char>(0x01)); // 可变结构限定词: 1个信息对象
  1480. data.append(static_cast<char>(0x05)); // 传送原因: 3突发
  1481. data.append(static_cast<char>(0x00));
  1482. data.append(static_cast<char>(commonAddress & 0xFF)); // 公共地址低字节
  1483. data.append(static_cast<char>((commonAddress >> 8) & 0xFF)); // 公共地址高字节
  1484. // 信息对象地址
  1485. data.append(static_cast<char>(objectAddress & 0xFF));
  1486. data.append(static_cast<char>((objectAddress >> 8) & 0xFF));
  1487. data.append(static_cast<char>((objectAddress >> 16) & 0xFF));
  1488. //装置地址
  1489. data.append(static_cast<char>(nEqpmtID & 0xFF)); // 公共地址低字节
  1490. data.append(static_cast<char>((nEqpmtID >> 8) & 0xFF)); // 公共地址高字节
  1491. //data.append(static_cast<char>(0x00));
  1492. data.append(static_cast<char>(0xF1)); // 信息序号(INF)
  1493. data.append(static_cast<char>(0x00));//返回信息标识
  1494. data.append(static_cast<char>(0x01));//通用分类数据集数目 NGD
  1495. data.append(static_cast<char>(0x01));//通用标识序号 GIN组
  1496. data.append(static_cast<char>(0x00));//通用标识序号 GIN条目
  1497. data.append(static_cast<char>(0x00));//通用标识序号 KOD
  1498. data.append(static_cast<char>(0x03));//数据类型 3
  1499. data.append(static_cast<char>(0x01));//数据大小 1
  1500. data.append(static_cast<char>(0x01));//数据序号 1
  1501. data.append(static_cast<char>(0x00));//描述类别 GID
  1502. emit logMessage(QString("Sending normalized setpoint: Address=%1, Value=%2, SelectExecute=%3")
  1503. .arg(commonAddress)
  1504. .arg(objectAddress)
  1505. .arg(nEqpmtID));
  1506. return sendIMessage(data);
  1507. }
  1508. bool IEC104MasterQt::sendFixAreaValueCommand(FixAreaCommandType nType, int commonAddress,int objectAddress,int nEqpmtID,int nFixArea)
  1509. {
  1510. if (!m_connected)
  1511. {
  1512. emit logMessage("Not connected, cannot send float setpoint");
  1513. return false;
  1514. }
  1515. QByteArray data;
  1516. // ASDU头
  1517. data.append(static_cast<char>(0x91)); // 类型: 145
  1518. data.append(static_cast<char>(0x01)); // 可变结构限定词: 1个信息对象
  1519. data.append(static_cast<char>(0x05)); // 传送原因: 3突发
  1520. data.append(static_cast<char>(0x00));
  1521. data.append(static_cast<char>(commonAddress & 0xFF)); // 公共地址低字节
  1522. data.append(static_cast<char>((commonAddress >> 8) & 0xFF)); // 公共地址高字节
  1523. // 信息对象地址
  1524. data.append(static_cast<char>(objectAddress & 0xFF));
  1525. data.append(static_cast<char>((objectAddress >> 8) & 0xFF));
  1526. data.append(static_cast<char>((objectAddress >> 16) & 0xFF));
  1527. //装置地址
  1528. data.append(static_cast<char>(nEqpmtID & 0xFF)); // 公共地址低字节
  1529. data.append(static_cast<char>((nEqpmtID >> 8) & 0xFF)); // 公共地址高字节
  1530. if(nType == FixAreaCommandType::FixAreaCommandType_Preset)
  1531. data.append(static_cast<char>(0xF9)); // 信息序号(INF)
  1532. else if(nType == FixAreaCommandType::FixAreaCommandType_Execut)
  1533. data.append(static_cast<char>(0xFA)); // 信息序号(INF)
  1534. else if(nType == FixAreaCommandType::FixAreaCommandType_Revoke)
  1535. data.append(static_cast<char>(0xFB)); // 信息序号(INF)
  1536. else
  1537. data.append(static_cast<char>(0xF9)); // 信息序号(INF)
  1538. data.append(static_cast<char>(0x00));//返回信息标识
  1539. data.append(static_cast<char>(0x01));//通用分类数据集数目 NGD
  1540. data.append(static_cast<char>(0x00));//通用标识序号 GIN组
  1541. data.append(static_cast<char>(0x01));//通用标识序号 GIN条目
  1542. data.append(static_cast<char>(0x00));//通用标识序号 KOD
  1543. data.append(static_cast<char>(0x03));//数据类型 3
  1544. data.append(static_cast<char>(0x01));//数据大小 1
  1545. data.append(static_cast<char>(0x01));//数据序号 1
  1546. data.append(static_cast<char>((nFixArea + 1) & 0xFF));//描述类别 GID 区号+1
  1547. emit logMessage(QString("Sending normalized setpoint: Address=%1, Value=%2, SelectExecute=%3")
  1548. .arg(commonAddress)
  1549. .arg(objectAddress)
  1550. .arg(nEqpmtID));
  1551. return sendIMessage(data);
  1552. }
  1553. bool IEC104MasterQt::sendPresetFixValueCommand(int commonAddress,int objectAddress,int nEqpmtID, int nFixArea,int nIndex,int nType, QString &msg)
  1554. {
  1555. if (!m_connected)
  1556. {
  1557. emit logMessage("Not connected, cannot send float setpoint");
  1558. return false;
  1559. }
  1560. QByteArray heasData;
  1561. // ASDU头
  1562. heasData.append(static_cast<char>(0x91)); // 类型: 145
  1563. heasData.append(static_cast<char>(0x01)); // 可变结构限定词: 1个信息对象
  1564. heasData.append(static_cast<char>(0x05)); // 传送原因: 3突发
  1565. heasData.append(static_cast<char>(0x00));
  1566. heasData.append(static_cast<char>(commonAddress & 0xFF)); // 公共地址低字节
  1567. heasData.append(static_cast<char>((commonAddress >> 8) & 0xFF)); // 公共地址高字节
  1568. // 信息对象地址
  1569. heasData.append(static_cast<char>(objectAddress & 0xFF));
  1570. heasData.append(static_cast<char>((objectAddress >> 8) & 0xFF));
  1571. heasData.append(static_cast<char>((objectAddress >> 16) & 0xFF));
  1572. //装置地址
  1573. heasData.append(static_cast<char>(nEqpmtID & 0xFF)); // 公共地址低字节
  1574. heasData.append(static_cast<char>((nEqpmtID >> 8) & 0xFF)); // 公共地址高字节
  1575. heasData.append(static_cast<char>(0xF9)); // 信息序号(INF)
  1576. heasData.append(static_cast<char>(0x00));//返回信息标识
  1577. // 先改值
  1578. QByteArray ginData;
  1579. bool ret = Value2Data(ginData,nType,msg);
  1580. int ginDatalenth = 4; //需要根据类型计算
  1581. #if 1
  1582. //data.append(static_cast<char>(0x9D));//通用分类数据集数目 NGD
  1583. int nPos = 0;
  1584. while (nPos < m_FixData.size())
  1585. {
  1586. // 取出数据类型定义
  1587. const P_145_Area *ptrArea = reinterpret_cast<const P_145_Area *>(m_FixData.constData() + nPos);
  1588. // 定值序号
  1589. nPos+=sizeof (P_145_Area);
  1590. if(ptrArea->GINnum == nIndex)
  1591. {
  1592. m_FixData.replace(nPos, ginDatalenth, ginData); // 替换前4个字节
  1593. break;
  1594. }
  1595. // 添加偏移
  1596. QString strData=Data2Value(nPos,m_FixData,ptrArea->FixValueType);
  1597. }
  1598. //截取发送
  1599. nPos = 0;
  1600. int startPos = 0;
  1601. int length = 0;
  1602. int cunot = 0;
  1603. int index = 0;
  1604. bool isLast;
  1605. QByteArray fixData;
  1606. while (nPos < m_FixData.size())
  1607. {
  1608. index++;
  1609. // 取出数据类型定义
  1610. const P_145_Area *ptrArea = reinterpret_cast<const P_145_Area *>(m_FixData.constData() + nPos);
  1611. startPos = nPos;
  1612. // 计算偏移
  1613. nPos+=sizeof (P_145_Area);
  1614. QString strData=Data2Value(nPos,m_FixData,ptrArea->FixValueType);
  1615. length = nPos - startPos; //单个定值包长度
  1616. // 拷贝
  1617. if (startPos + length <= m_FixData.size()) {
  1618. for (int i = 0; i < length; ++i) {
  1619. fixData.append(m_FixData[startPos + i]);
  1620. }
  1621. isLast = true;
  1622. }
  1623. //满20个打包发送
  1624. if ( index % 20 == 0)
  1625. {
  1626. QByteArray sendData;
  1627. sendData.append(heasData);
  1628. if(cunot == 0)
  1629. {
  1630. sendData.append(static_cast<char>(0x94));
  1631. cunot = 1;
  1632. }
  1633. else
  1634. {
  1635. sendData.append(static_cast<char>(0xd4));
  1636. cunot = 0;
  1637. }
  1638. sendData.append(fixData);
  1639. sendIMessage(sendData);
  1640. fixData.clear();
  1641. isLast = false;
  1642. }
  1643. }
  1644. if (isLast)
  1645. {
  1646. QByteArray sendData;
  1647. sendData.append(heasData);
  1648. sendData.append(static_cast<char>(0x01));
  1649. sendData.append(fixData);
  1650. sendIMessage(sendData);
  1651. fixData.clear();
  1652. isLast = false;
  1653. }
  1654. #else
  1655. data.append(static_cast<char>(0x01));//通用分类数据集数目 NGD
  1656. data.append(static_cast<char>((nFixArea + 1) & 0xFF));//通用标识序号 GIN组 区号+1
  1657. data.append(static_cast<char>(nIndex));//通用标识序号 GIN条目
  1658. data.append(static_cast<char>(0x00));//通用标识序号 KOD
  1659. data.append(static_cast<char>(0x03));//数据类型 3
  1660. data.append(static_cast<char>(0x01));//数据大小 1
  1661. data.append(static_cast<char>(0x01));//数据序号 1
  1662. data.append(ginData);//描述类别 GID 定值
  1663. #endif
  1664. emit logMessage(QString("Sending normalized setpoint: Address=%1, Value=%2, SelectExecute=%3")
  1665. .arg(commonAddress)
  1666. .arg(objectAddress)
  1667. .arg(nEqpmtID));
  1668. //return sendIMessage(data);
  1669. return true;
  1670. }
  1671. bool IEC104MasterQt::sendSetFixValueCommand(FixAreaCommandType nType, int commonAddress,int objectAddress,int nEqpmtID,int nFixArea)
  1672. {
  1673. if (!m_connected)
  1674. {
  1675. emit logMessage("Not connected, cannot send float setpoint");
  1676. return false;
  1677. }
  1678. QByteArray data;
  1679. // ASDU头
  1680. data.append(static_cast<char>(0x91)); // 类型: 145
  1681. data.append(static_cast<char>(0x01)); // 可变结构限定词: 1个信息对象
  1682. data.append(static_cast<char>(0x05)); // 传送原因: 3突发
  1683. data.append(static_cast<char>(0x00));
  1684. data.append(static_cast<char>(commonAddress & 0xFF)); // 公共地址低字节
  1685. data.append(static_cast<char>((commonAddress >> 8) & 0xFF)); // 公共地址高字节
  1686. // 信息对象地址
  1687. data.append(static_cast<char>(objectAddress & 0xFF));
  1688. data.append(static_cast<char>((objectAddress >> 8) & 0xFF));
  1689. data.append(static_cast<char>((objectAddress >> 16) & 0xFF));
  1690. //装置地址
  1691. data.append(static_cast<char>(nEqpmtID & 0xFF)); // 公共地址低字节
  1692. data.append(static_cast<char>((nEqpmtID >> 8) & 0xFF)); // 公共地址高字节
  1693. if(nType == FixAreaCommandType::FixAreaCommandType_Execut)
  1694. data.append(static_cast<char>(0xFA)); // 信息序号(INF)
  1695. else if(nType == FixAreaCommandType::FixAreaCommandType_Revoke)
  1696. data.append(static_cast<char>(0xFB)); // 信息序号(INF)
  1697. else
  1698. data.append(static_cast<char>(0x00)); // 信息序号(INF)
  1699. data.append(static_cast<char>(0x00));//返回信息标识
  1700. data.append(static_cast<char>(0x01));//通用分类数据集数目 NGD
  1701. data.append(static_cast<char>((nFixArea + 1) & 0xFF));//通用标识序号 GIN组 区号+1
  1702. data.append(static_cast<char>(0x00));//通用标识序号 GIN条目
  1703. data.append(static_cast<char>(0x00));//通用标识序号 KOD
  1704. data.append(static_cast<char>(0x03));//数据类型 3
  1705. data.append(static_cast<char>(0x01));//数据大小 1
  1706. data.append(static_cast<char>(0x01));//数据序号 1
  1707. data.append(static_cast<char>((nFixArea + 1) & 0xFF));//描述类别 GID 区号+1
  1708. emit logMessage(QString("Sending normalized setpoint: Address=%1, Value=%2, SelectExecute=%3")
  1709. .arg(commonAddress)
  1710. .arg(objectAddress)
  1711. .arg(nEqpmtID));
  1712. return sendIMessage(data);
  1713. }
  1714. //时钟同步
  1715. bool IEC104MasterQt::sendTimeSynchronizationCommand()
  1716. {
  1717. if (!m_connected)
  1718. {
  1719. emit logMessage("Not connected, cannot send float setpoint");
  1720. return false;
  1721. }
  1722. int commonAddress = 1;
  1723. int objectAddress = 0;
  1724. QByteArray data;
  1725. // ASDU头
  1726. data.append(static_cast<char>(0x67)); // 类型: 103
  1727. data.append(static_cast<char>(0x01)); // 可变结构限定词: 1个信息对象
  1728. data.append(static_cast<char>(0x06)); // 传送原因: 6激活
  1729. data.append(static_cast<char>(0x00));
  1730. data.append(static_cast<char>(commonAddress & 0xFF)); // 公共地址低字节
  1731. data.append(static_cast<char>((commonAddress >> 8) & 0xFF)); // 公共地址高字节
  1732. // 信息对象地址
  1733. data.append(static_cast<char>(objectAddress & 0xFF));
  1734. data.append(static_cast<char>((objectAddress >> 8) & 0xFF));
  1735. data.append(static_cast<char>((objectAddress >> 16) & 0xFF));
  1736. //CP56Time2a
  1737. // 时标
  1738. QDateTime timestamp = QDateTime::currentDateTime();
  1739. QByteArray date = dateTimeToCp56Time2a(timestamp);
  1740. data.append(date);
  1741. return sendIMessage(data);
  1742. }
  1743. // 新增:发送浮点设值命令
  1744. bool IEC104MasterQt::sendFloatSetpoint(int commonAddress, int objectAddress, float value, bool selectExecute)
  1745. {
  1746. if (!m_connected)
  1747. {
  1748. emit logMessage("Not connected, cannot send float setpoint");
  1749. return false;
  1750. }
  1751. QByteArray data;
  1752. // ASDU头
  1753. data.append(static_cast<char>(selectExecute ? 0x32 : 0x33)); // 类型: 50-设点命令规范化值, 51-设点命令规范化值停止激活
  1754. data.append(static_cast<char>(0x01)); // 可变结构限定词: 1个信息对象
  1755. data.append(static_cast<char>(selectExecute ? 0x06 : 0x08)); // 传送原因: 6-激活, 8-停止激活
  1756. data.append(static_cast<char>(0x00));
  1757. data.append(static_cast<char>(commonAddress & 0xFF)); // 公共地址低字节
  1758. data.append(static_cast<char>((commonAddress >> 8) & 0xFF)); // 公共地址高字节
  1759. // 信息对象地址
  1760. data.append(static_cast<char>(objectAddress & 0xFF));
  1761. data.append(static_cast<char>((objectAddress >> 8) & 0xFF));
  1762. data.append(static_cast<char>((objectAddress >> 16) & 0xFF));
  1763. // 规一化值
  1764. short normalizedValue = floatToNormalized(value);
  1765. data.append(static_cast<char>(normalizedValue & 0xFF));
  1766. data.append(static_cast<char>((normalizedValue >> 8) & 0xFF));
  1767. // QOS (设值命令质量)
  1768. data.append(static_cast<char>(0x00)); // 通常命令
  1769. emit logMessage(QString("Sending normalized setpoint: Address=%1, Value=%2, SelectExecute=%3")
  1770. .arg(objectAddress)
  1771. .arg(value)
  1772. .arg(selectExecute));
  1773. // 保存待确认的命令
  1774. if (selectExecute)
  1775. {
  1776. QMutexLocker locker(&m_commandMutex);
  1777. ControlCommand cmd;
  1778. cmd.address = objectAddress;
  1779. cmd.type = ControlCommandType::FloatSetpoint;
  1780. cmd.setpoint = value;
  1781. cmd.commonAddress = commonAddress;
  1782. cmd.timestamp = QDateTime::currentDateTime();
  1783. m_pendingCommands[objectAddress] = cmd;
  1784. }
  1785. return sendIMessage(data);
  1786. }
  1787. // 新增:发送短浮点数设值命令
  1788. bool IEC104MasterQt::sendSetpointCommandNormalized(int commonAddress, int objectAddress, float value, bool selectExecute)
  1789. {
  1790. if (!m_connected)
  1791. {
  1792. emit logMessage("Not connected, cannot send setpoint command");
  1793. return false;
  1794. }
  1795. QByteArray data;
  1796. // ASDU头
  1797. data.append(static_cast<char>(selectExecute ? 0x64 : 0x65)); // 类型: 100-设点命令短浮点数, 101-设点命令短浮点数停止激活
  1798. data.append(static_cast<char>(0x01)); // 可变结构限定词: 1个信息对象
  1799. data.append(static_cast<char>(selectExecute ? 0x06 : 0x08)); // 传送原因: 6-激活, 8-停止激活
  1800. data.append(static_cast<char>(0x00));
  1801. data.append(static_cast<char>(commonAddress & 0xFF)); // 公共地址低字节
  1802. data.append(static_cast<char>((commonAddress >> 8) & 0xFF)); // 公共地址高字节
  1803. // 信息对象地址
  1804. data.append(static_cast<char>(objectAddress & 0xFF));
  1805. data.append(static_cast<char>((objectAddress >> 8) & 0xFF));
  1806. data.append(static_cast<char>((objectAddress >> 16) & 0xFF));
  1807. // 短浮点数值
  1808. QByteArray floatBytes = floatToBytes(value);
  1809. data.append(floatBytes);
  1810. // QOS (设值命令质量)
  1811. data.append(static_cast<char>(0x00)); // 通常命令
  1812. emit logMessage(QString("Sending float setpoint: Address=%1, Value=%2, SelectExecute=%3")
  1813. .arg(objectAddress)
  1814. .arg(value)
  1815. .arg(selectExecute));
  1816. // 保存待确认的命令
  1817. if (selectExecute)
  1818. {
  1819. QMutexLocker locker(&m_commandMutex);
  1820. ControlCommand cmd;
  1821. cmd.address = objectAddress;
  1822. cmd.type = ControlCommandType::FloatSetpoint;
  1823. cmd.setpoint = value;
  1824. cmd.commonAddress = commonAddress;
  1825. cmd.timestamp = QDateTime::currentDateTime();
  1826. m_pendingCommands[objectAddress] = cmd;
  1827. }
  1828. return sendIMessage(data);
  1829. }
  1830. bool IEC104MasterQt::sendSingleCommand2(int commonAddress, int objectAddress, bool command, bool selectExecute /*= true*/, bool isRun /*= true*/)
  1831. {
  1832. }
  1833. // 新增:处理遥控命令确认
  1834. void IEC104MasterQt::processControlCommand(const ASDU_header *header, const QByteArray &data, int cause)
  1835. {
  1836. if (data.size() < 4)
  1837. return; // 至少需要地址3字节 + 命令值1字节 + QOC1字节
  1838. const unsigned char *ptr = reinterpret_cast<const unsigned char *>(data.constData());
  1839. int objectAddress = (ptr[2] << 16) | (ptr[1] << 8) | ptr[0];
  1840. unsigned char commandValue = ptr[3];
  1841. QMutexLocker locker(&m_commandMutex);
  1842. if (m_pendingCommands.contains(objectAddress))
  1843. {
  1844. ControlCommand cmd = m_pendingCommands[objectAddress];
  1845. switch (cause)
  1846. {
  1847. case 7: // 激活确认
  1848. emit logMessage(QString("Control command activation confirmed: Address=%1")
  1849. .arg(objectAddress));
  1850. emit controlCommandConfirmed(objectAddress, true, "Activation confirmed");
  1851. if (cmd.type== ControlCommandType::SinglePoint)
  1852. {
  1853. sendSingleCommand(cmd.commonAddress, cmd.address, cmd.value ? 1 : 0, false);
  1854. }
  1855. else if (cmd.type == ControlCommandType::DoublePoint)
  1856. {
  1857. sendDoubleCommand2(cmd.commonAddress, cmd.address, cmd.value ? 2 : 1, false);
  1858. }
  1859. m_pendingCommands.remove(objectAddress);
  1860. break;
  1861. case 9: // 激活终止
  1862. m_pendingCommands.remove(objectAddress);
  1863. emit logMessage(QString("Control command activation terminated: Address=%1")
  1864. .arg(objectAddress));
  1865. emit controlCommandConfirmed(objectAddress, true, "Activation terminated");
  1866. break;
  1867. case 47: // 命令出错
  1868. m_pendingCommands.remove(objectAddress);
  1869. emit logMessage(QString("Control command error: Address=%1")
  1870. .arg(objectAddress));
  1871. emit controlCommandConfirmed(objectAddress, false, "Command error");
  1872. break;
  1873. default:
  1874. emit logMessage(QString("Unknown control command cause: %1").arg(cause));
  1875. break;
  1876. }
  1877. }
  1878. }
  1879. // 新增:处理设值命令确认
  1880. void IEC104MasterQt::processSetpointCommand(const ASDU_header *header, const QByteArray &data, int cause)
  1881. {
  1882. if (data.size() < 6)
  1883. return; // 至少需要地址3字节 + 值2字节 + QOS1字节
  1884. const unsigned char *ptr = reinterpret_cast<const unsigned char *>(data.constData());
  1885. int objectAddress = (ptr[2] << 16) | (ptr[1] << 8) | ptr[0];
  1886. QMutexLocker locker(&m_commandMutex);
  1887. if (m_pendingCommands.contains(objectAddress))
  1888. {
  1889. ControlCommand cmd = m_pendingCommands[objectAddress];
  1890. switch (cause)
  1891. {
  1892. case 7: // 激活确认
  1893. emit logMessage(QString("Setpoint command activation confirmed: Address=%1")
  1894. .arg(objectAddress));
  1895. emit setpointCommandConfirmed(objectAddress, true, "Activation confirmed");
  1896. break;
  1897. case 9: // 激活终止
  1898. m_pendingCommands.remove(objectAddress);
  1899. emit logMessage(QString("Setpoint command activation terminated: Address=%1")
  1900. .arg(objectAddress));
  1901. emit setpointCommandConfirmed(objectAddress, true, "Activation terminated");
  1902. break;
  1903. case 47: // 命令出错
  1904. m_pendingCommands.remove(objectAddress);
  1905. emit logMessage(QString("Setpoint command error: Address=%1")
  1906. .arg(objectAddress));
  1907. emit setpointCommandConfirmed(objectAddress, false, "Command error");
  1908. break;
  1909. default:
  1910. emit logMessage(QString("Unknown setpoint command cause: %1").arg(cause));
  1911. break;
  1912. }
  1913. }
  1914. }
  1915. // 新增:发送命令激活终止
  1916. void IEC104MasterQt::sendCommandActivationTermination(int type, int commonAddress, int objectAddress)
  1917. {
  1918. // 根据命令类型发送停止激活命令
  1919. switch (type)
  1920. {
  1921. case 45: // 单命令
  1922. sendSingleCommand(commonAddress, objectAddress, false, false);
  1923. break;
  1924. case 46: // 双命令
  1925. sendDoubleCommand(commonAddress, objectAddress, 0, false);
  1926. break;
  1927. case 50: // 设点命令规范化值
  1928. sendFloatSetpoint(commonAddress, objectAddress, 0.0f, false);
  1929. break;
  1930. case 100: // 设点命令短浮点数
  1931. sendSetpointCommandNormalized(commonAddress, objectAddress, 0.0f, false);
  1932. break;
  1933. default:
  1934. emit logMessage(QString("Unknown command type for termination: %1").arg(type));
  1935. break;
  1936. }
  1937. }
  1938. void IEC104MasterQt::FixValueCommand(const ASDU_header *header, const QByteArray &data, int cause)
  1939. {
  1940. //信息序号
  1941. if (data.size() < 6)
  1942. return; // 至少需要地址3字节 + 值2字节 + QOS1字节
  1943. int nPos=sizeof(P_EP_145);
  1944. const P_EP_145 *ptr = reinterpret_cast<const P_EP_145 *>(data.constData());
  1945. int nEqpmtID=(ptr->eqpmtadder2 << 8) | ptr->eqpmtadder1;
  1946. switch(ptr->msgID)
  1947. {
  1948. case FixValueType::GetFixAll:
  1949. {
  1950. bool isFixArea = false;
  1951. while (nPos < data.size())
  1952. {
  1953. // 取出数据类型定义
  1954. const P_145_Area *ptrArea = reinterpret_cast<const P_145_Area *>(data.constData() + nPos);
  1955. // 取出数据
  1956. nPos+=sizeof (P_145_Area);
  1957. QString strData=Data2Value(nPos,data,ptrArea->FixValueType);
  1958. updateFixValue(nEqpmtID, ptrArea->GIN, ptrArea->GINnum, ptrArea->FixValueType, strData);
  1959. if(strData.isEmpty())
  1960. isFixArea = true;
  1961. }
  1962. //将定值数据记录下来
  1963. if(!isFixArea)
  1964. {
  1965. int startPos = sizeof(P_EP_145);
  1966. int length = data.size() - startPos;
  1967. if (startPos + length <= data.size()) {
  1968. for (int i = 0; i < length; ++i) {
  1969. m_FixData.append(data[startPos + i]);
  1970. }
  1971. }
  1972. }
  1973. }
  1974. break;
  1975. case FixValueType::SetFixValue:
  1976. {
  1977. }
  1978. break;
  1979. case FixValueType::RunFixValue:
  1980. {
  1981. }
  1982. break;
  1983. case FixValueType::CancelFixValue:
  1984. {
  1985. }
  1986. break;
  1987. }
  1988. }
  1989. void IEC104MasterQt::updateFixValue(int nEqpmtID,int nAreaID, int nAddress, int nType, QString strData)
  1990. {
  1991. QMutexLocker locker(&m_statusMutex);
  1992. FixDataValue mv;
  1993. mv.nEqpmtId = nEqpmtID;
  1994. mv.nAddr = nAddress;
  1995. mv.nType = nType;
  1996. mv.strData = strData;;
  1997. mv.timestamp = QDateTime::currentDateTime();
  1998. auto iters=m_fixValues.find(nEqpmtID);
  1999. if (iters==m_fixValues.end())
  2000. {
  2001. QMap<int,QMap<int,FixDataValue>> mlistData;
  2002. iters=m_fixValues.insert(nEqpmtID,mlistData);
  2003. }
  2004. auto iter=iters->find(nAreaID);
  2005. if(iter==iters->end())
  2006. {
  2007. QMap<int,FixDataValue> mlistData;
  2008. iter=iters->insert(nAreaID,mlistData);
  2009. }
  2010. iter->insert(nAddress,mv);
  2011. }
  2012. // 新增:浮点数转换为字节序列
  2013. QByteArray IEC104MasterQt::floatToBytes(float value)
  2014. {
  2015. QByteArray bytes(4, 0);
  2016. uint32_t temp = *reinterpret_cast<uint32_t *>(&value);
  2017. // IEC 104使用小端字节序
  2018. bytes[0] = static_cast<char>(temp & 0xFF);
  2019. bytes[1] = static_cast<char>((temp >> 8) & 0xFF);
  2020. bytes[2] = static_cast<char>((temp >> 16) & 0xFF);
  2021. bytes[3] = static_cast<char>((temp >> 24) & 0xFF);
  2022. return bytes;
  2023. }
  2024. // 新增:浮点数转换为规一化值
  2025. short IEC104MasterQt::floatToNormalized(float value, float scale)
  2026. {
  2027. // 将浮点数转换为规一化值 (-1.0 到 1.0 映射到 -32768 到 32767)
  2028. float normalized = value / scale;
  2029. if (normalized > 1.0f)
  2030. normalized = 1.0f;
  2031. if (normalized < -1.0f)
  2032. normalized = -1.0f;
  2033. return static_cast<short>(normalized * 32767.0f);
  2034. }
  2035. // 添加文件传输处理函数实现
  2036. void IEC104MasterQt::processFileReady(const ASDU_header *header, const QByteArray &data, int cause)
  2037. {
  2038. if (data.size() < sizeof(FileReady)) return;
  2039. const FileReady *fr = reinterpret_cast<const FileReady*>(data.constData());
  2040. //QString fileName = bytesToFileName(&fr->name1);
  2041. int num = fr->name1;
  2042. QString fileName = QString::number(num);
  2043. int fileSize = bytesToFileSize(&fr->length1);
  2044. bool ready = (fr->ready_status == 0);
  2045. emit logMessage(QString("File ready: %1, Size: %2, Ready: %3")
  2046. .arg(fileName).arg(fileSize).arg(ready));
  2047. emit fileReadyReceived(fileName, fileSize, ready);
  2048. // 更新文件传输状态
  2049. QMutexLocker locker(&m_fileMutex);
  2050. if (m_fileTransfers.contains(fileName)) {
  2051. FileTransferInfo &info = m_fileTransfers[fileName];
  2052. info.fileSize = fileSize;
  2053. info.status = ready ? FTS_Success : FTS_NotReady;
  2054. info.lastUpdate = QDateTime::currentDateTime();
  2055. }
  2056. }
  2057. void IEC104MasterQt::processSectionReady(const ASDU_header *header, const QByteArray &data, int cause)
  2058. {
  2059. if (data.size() < sizeof(SectionReady)) return;
  2060. const SectionReady *sr = reinterpret_cast<const SectionReady*>(data.constData());
  2061. //QString fileName = bytesToFileName(&sr->name1);
  2062. int num = sr->name1;
  2063. QString fileName = QString::number(num);
  2064. int section = sr->section_name;
  2065. int sectionSize = bytesToFileSize(&sr->length1);
  2066. bool ready = (sr->ready_status == 0);
  2067. emit logMessage(QString("Section ready: %1, Section: %2, Size: %3, Ready: %4")
  2068. .arg(fileName).arg(section).arg(sectionSize).arg(ready));
  2069. emit sectionReadyReceived(fileName, section, sectionSize, ready);
  2070. }
  2071. void IEC104MasterQt::processFileSegment(const ASDU_header *header, const QByteArray &data, int cause)
  2072. {
  2073. if (data.size() < 6) return; // 至少需要文件名和节名
  2074. const unsigned char *ptr = reinterpret_cast<const unsigned char*>(data.constData());
  2075. //QString fileName = bytesToFileName(ptr+3); //有3个字节
  2076. int num = ptr[3];
  2077. QString fileName = QString::number(num);
  2078. int section = ptr[5];
  2079. int length = ptr[6];
  2080. int datalen = data.size()-7;
  2081. // 段数据从第6字节开始
  2082. QByteArray segmentData = data.mid(7,length);
  2083. emit logMessage(QString("File segment: %1, Section: %2, Data size: %3")
  2084. .arg(fileName).arg(section).arg(segmentData.size()));
  2085. // 保存段数据
  2086. QMutexLocker locker(&m_fileMutex);
  2087. if (m_fileTransfers.contains(fileName)) {
  2088. FileTransferInfo &info = m_fileTransfers[fileName];
  2089. //info.fileData.append(segmentData);
  2090. //如果该节已经存在,追加数据
  2091. if(info.fileDataMap.contains(section))
  2092. {
  2093. info.fileDataMap[section].append(segmentData);
  2094. }
  2095. else
  2096. {
  2097. info.fileDataMap.insert(section, segmentData);
  2098. }
  2099. info.currentSection = section;
  2100. info.lastUpdate = QDateTime::currentDateTime();
  2101. emit fileSegmentReceived(fileName, section, segmentData);
  2102. }
  2103. }
  2104. //召唤目录
  2105. void IEC104MasterQt::processDirectory(const ASDU_header *header, const QByteArray &data, int cause)
  2106. {
  2107. int pos = 3;
  2108. QList<QString> fileList;
  2109. while (pos + 16 <= data.size()) { // 每个目录项9字节
  2110. const unsigned char *ptr = reinterpret_cast<const unsigned char*>(data.constData() + pos);
  2111. int fileNameID = (ptr[1]<< 8) | ptr[0];
  2112. int fileSize = bytesToFileSize(ptr + 2);
  2113. QDateTime timestamp = cp56TimeToDateTime(ptr+6);
  2114. fileList.append(QString("%1 (%2 bytes) Time=%3").arg(QString::number(fileNameID)).arg(fileSize).arg(timestamp.toString("yyyy-MM-dd hh:mm:ss.zzz")));
  2115. pos += 16;
  2116. emit logMessage(QString("Directory entry: %1, Size: %2 Time=%3").arg(QString::number(fileNameID)).arg(fileSize).arg(timestamp.toString("yyyy-MM-dd hh:mm:ss.zzz")));
  2117. }
  2118. emit directoryReceived(fileList);
  2119. }
  2120. // 添加其他文件传输处理函数(简化实现)
  2121. void IEC104MasterQt::processFileCall(const ASDU_header *header, const QByteArray &data, int cause)
  2122. {
  2123. if (data.size() < sizeof(FileCall)) return;
  2124. const FileCall *fc = reinterpret_cast<const FileCall*>(data.constData());
  2125. QString fileName = bytesToFileName(&fc->name1);
  2126. int section = fc->section_name;
  2127. int qualifier = fc->qualifier;
  2128. QString qualifierStr;
  2129. switch (qualifier) {
  2130. case FTRQ_SelectFile: qualifierStr = "Select File"; break;
  2131. case FTRQ_RequestFile: qualifierStr = "Request File"; break;
  2132. case FTRQ_SelectSection: qualifierStr = "Select Section"; break;
  2133. case FTRQ_RequestSection: qualifierStr = "Request Section"; break;
  2134. case FTRQ_Directory: qualifierStr = "Directory"; break;
  2135. default: qualifierStr = "Unknown"; break;
  2136. }
  2137. emit logMessage(QString("File call: %1, Section: %2, Qualifier: %3")
  2138. .arg(fileName).arg(section).arg(qualifierStr));
  2139. }
  2140. //节最后段数据接收后校验数据
  2141. void IEC104MasterQt::processLastSection(const ASDU_header *header, const QByteArray &data, int cause)
  2142. {
  2143. if (data.size() < sizeof(LastSection)) return;
  2144. const LastSection *ls = reinterpret_cast<const LastSection*>(data.constData());
  2145. //QString fileName = bytesToFileName(&ls->name1);
  2146. int num = ls->name1;
  2147. QString fileName = QString::number(num);
  2148. int section = ls->section_name;
  2149. int lsq = ls->last_section;
  2150. int checksum = ls->checksum;
  2151. emit logMessage(QString("fileName: %1, Current Section: %2, lsq: %3, Checksum: 0x%4")
  2152. .arg(fileName).arg(section).arg(lsq).arg(checksum, 4, 16, QLatin1Char('0')));
  2153. // 文件传输完成
  2154. QMutexLocker locker(&m_fileMutex);
  2155. if (m_fileTransfers.contains(fileName)) {
  2156. FileTransferInfo &info = m_fileTransfers[fileName];
  2157. info.status = FTS_Success;
  2158. info.lastUpdate = QDateTime::currentDateTime();
  2159. // 计算校验和验证
  2160. quint16 calculatedChecksum = 0;
  2161. int nsectionData = 0;
  2162. if(lsq == SCQ_StopSelect) //所选节传输完毕
  2163. {
  2164. if (info.fileDataMap.contains(section)) {
  2165. const QByteArray sectionData = info.fileDataMap[section];
  2166. for (int i = 0; i < sectionData.size(); i++) {
  2167. calculatedChecksum += static_cast<quint8>(sectionData[i]);
  2168. }
  2169. nsectionData = sectionData.size();
  2170. emit logMessage(QString("calculatedCheck fileData SIZE: %1, fileData: %2").arg(sectionData.size()).arg(toHexString(sectionData).constData()));
  2171. }
  2172. }
  2173. else if(lsq == SCQ_SelectFile) //所选文件传输完毕
  2174. {
  2175. for (auto it = info.fileDataMap.constBegin(); it != info.fileDataMap.constEnd(); ++it) {
  2176. const QByteArray& sectionData = it.value();
  2177. for (int i = 0; i < sectionData.size(); i++) {
  2178. calculatedChecksum += static_cast<quint8>(sectionData[i]);
  2179. }
  2180. nsectionData += sectionData.size();
  2181. }
  2182. }
  2183. calculatedChecksum &= 0x00FF;
  2184. bool checksumValid = (calculatedChecksum == checksum);
  2185. if (checksumValid) {
  2186. emit fileTransferCompleted(fileName, section, lsq, true, "File transfer completed successfully");
  2187. emit logMessage(QString("File transfer completed: %1, Size: %2 bytes, Checksum valid")
  2188. .arg(fileName).arg(nsectionData));
  2189. } else {
  2190. emit fileTransferCompleted(fileName, section, lsq, false,
  2191. QString("Checksum error: expected 0x%1, got 0x%2")
  2192. .arg(checksum, 4, 16, QLatin1Char('0'))
  2193. .arg(calculatedChecksum, 4, 16, QLatin1Char('0')));
  2194. emit logMessage(QString("File transfer checksum error: %1").arg(fileName));
  2195. }
  2196. }
  2197. }
  2198. void IEC104MasterQt::processFileAck(const ASDU_header *header, const QByteArray &data, int cause)
  2199. {
  2200. if (data.size() < sizeof(FileAck)) return;
  2201. const FileAck *fa = reinterpret_cast<const FileAck*>(data.constData());
  2202. QString fileName = bytesToFileName(&fa->name1);
  2203. int section = fa->section_name;
  2204. int ackStatus = fa->ack_status;//限定词?
  2205. QString statusStr = (ackStatus == 0) ? "Accepted" : "Rejected";
  2206. emit logMessage(QString("File acknowledgement: %1, Section: %2, Status: %3")
  2207. .arg(fileName).arg(section).arg(statusStr));
  2208. // 如果被拒绝,更新文件传输状态
  2209. if (ackStatus != 0) {
  2210. QMutexLocker locker(&m_fileMutex);
  2211. if (m_fileTransfers.contains(fileName)) {
  2212. FileTransferInfo &info = m_fileTransfers[fileName];
  2213. info.status = FTS_AccessDenied;
  2214. info.lastUpdate = QDateTime::currentDateTime();
  2215. emit fileTransferCompleted(fileName, section, ackStatus, false, "File transfer rejected by remote");
  2216. }
  2217. }
  2218. }
  2219. // 文件传输公共接口实现
  2220. bool IEC104MasterQt::requestDirectory(int commonAddress,int msgAddress)
  2221. {
  2222. QByteArray data;
  2223. data.append(static_cast<char>(F_SC_NA_1)); // 类型
  2224. data.append(static_cast<char>(0x01)); // 可变结构限定词
  2225. data.append(static_cast<char>(0x05)); // 传送原因
  2226. data.append(static_cast<char>(0x00));
  2227. data.append(static_cast<char>(commonAddress & 0xFF));
  2228. data.append(static_cast<char>((commonAddress >> 8) & 0xFF));
  2229. // 信息对象地址
  2230. data.append(static_cast<char>(msgAddress & 0xFF));
  2231. data.append(static_cast<char>((msgAddress >> 8) & 0xFF));
  2232. data.append(static_cast<char>((msgAddress >> 16) & 0xFF));
  2233. // 文件名全0表示目录请求
  2234. data.append(static_cast<char>(0x00));
  2235. data.append(static_cast<char>(0x00));
  2236. data.append(static_cast<char>(0x00));
  2237. data.append(static_cast<char>(0x00));
  2238. data.append(static_cast<char>(0x00)); // 节名
  2239. data.append(static_cast<char>(0x02)); // 限定词
  2240. //请求目录
  2241. emit logMessage("Requesting directory...");
  2242. return sendIMessage(data);
  2243. }
  2244. //选择文件
  2245. bool IEC104MasterQt::selectFile(int commonAddress,int msgAddress, const QString &fileName)
  2246. {
  2247. QByteArray data;
  2248. data.append(static_cast<char>(F_SC_NA_1));
  2249. data.append(static_cast<char>(0x01));
  2250. data.append(static_cast<char>(0x0D));
  2251. data.append(static_cast<char>(0x00));
  2252. data.append(static_cast<char>(commonAddress & 0xFF));
  2253. data.append(static_cast<char>((commonAddress >> 8) & 0xFF));
  2254. // 信息对象地址
  2255. data.append(static_cast<char>(msgAddress & 0xFF));
  2256. data.append(static_cast<char>((msgAddress >> 8) & 0xFF));
  2257. data.append(static_cast<char>((msgAddress >> 16) & 0xFF));
  2258. QByteArray nameBytes = fileNameToBytes(fileName);
  2259. //data.append(nameBytes);
  2260. data.append(static_cast<char>(fileName.toInt() & 0xFF));
  2261. data.append(static_cast<char>((fileName.toInt() >> 8) & 0xFF));
  2262. data.append(static_cast<char>(0x00)); // 节名
  2263. data.append(static_cast<char>(SCQ_SelectFile));
  2264. emit logMessage(QString("Selecting file: %1").arg(fileName));
  2265. // 初始化文件传输状态
  2266. QMutexLocker locker(&m_fileMutex);
  2267. FileTransferInfo info;
  2268. info.fileName = fileName;
  2269. info.startTime = QDateTime::currentDateTime();
  2270. info.status = FTS_Success;
  2271. m_fileTransfers[fileName] = info;
  2272. return sendIMessage(data);
  2273. }
  2274. //请求文件
  2275. bool IEC104MasterQt::requestFile(int commonAddress,int msgAddress, const QString &fileName)
  2276. {
  2277. QByteArray data;
  2278. data.append(static_cast<char>(F_SC_NA_1));
  2279. data.append(static_cast<char>(0x01));
  2280. data.append(static_cast<char>(0x0D));
  2281. data.append(static_cast<char>(0x00));
  2282. data.append(static_cast<char>(commonAddress & 0xFF));
  2283. data.append(static_cast<char>((commonAddress >> 8) & 0xFF));
  2284. // 信息对象地址
  2285. data.append(static_cast<char>(msgAddress & 0xFF));
  2286. data.append(static_cast<char>((msgAddress >> 8) & 0xFF));
  2287. data.append(static_cast<char>((msgAddress >> 16) & 0xFF));
  2288. QByteArray nameBytes = fileNameToBytes(fileName);
  2289. //data.append(nameBytes);
  2290. data.append(static_cast<char>(fileName.toInt() & 0xFF));
  2291. data.append(static_cast<char>((fileName.toInt() >> 8) & 0xFF));
  2292. data.append(static_cast<char>(0x00));
  2293. data.append(static_cast<char>(FTRQ_RequestFile));
  2294. emit logMessage(QString("Requesting file: %1").arg(fileName));
  2295. return sendIMessage(data);
  2296. }
  2297. //请求段数据
  2298. bool IEC104MasterQt::requestSection(int commonAddress,int msgAddress, const QString &fileName, int section)
  2299. {
  2300. if (!m_connected) {
  2301. emit logMessage("Not connected, cannot request section");
  2302. return false;
  2303. }
  2304. QByteArray data;
  2305. data.append(static_cast<char>(F_SC_NA_1)); // 类型
  2306. data.append(static_cast<char>(0x01)); // 可变结构限定词
  2307. data.append(static_cast<char>(0x0D)); // 传送原因
  2308. data.append(static_cast<char>(0x00));
  2309. data.append(static_cast<char>(commonAddress & 0xFF));
  2310. data.append(static_cast<char>((commonAddress >> 8) & 0xFF));
  2311. // 信息对象地址
  2312. data.append(static_cast<char>(msgAddress & 0xFF));
  2313. data.append(static_cast<char>((msgAddress >> 8) & 0xFF));
  2314. data.append(static_cast<char>((msgAddress >> 16) & 0xFF));
  2315. QByteArray nameBytes = fileNameToBytes(fileName);
  2316. //data.append(nameBytes);
  2317. data.append(static_cast<char>(fileName.toInt() & 0xFF));
  2318. data.append(static_cast<char>((fileName.toInt() >> 8) & 0xFF));
  2319. data.append(static_cast<char>(section)); // 节名
  2320. data.append(static_cast<char>(SCQ_RequesSection)); // 限定词
  2321. emit logMessage(QString("Requesting section: %1, Section: %2").arg(fileName).arg(section));
  2322. return sendIMessage(data);
  2323. }
  2324. //认可段数据
  2325. bool IEC104MasterQt::acceptSection(int commonAddress,int msgAddress, const QString &fileName, int section, int lsq)
  2326. {
  2327. if (!m_connected) {
  2328. emit logMessage("Not connected, cannot request section");
  2329. return false;
  2330. }
  2331. QByteArray data;
  2332. data.append(static_cast<char>(F_AF_NA_1)); // 类型
  2333. data.append(static_cast<char>(0x01)); // 可变结构限定词
  2334. data.append(static_cast<char>(0x0D)); // 传送原因
  2335. data.append(static_cast<char>(0x00));
  2336. data.append(static_cast<char>(commonAddress & 0xFF));
  2337. data.append(static_cast<char>((commonAddress >> 8) & 0xFF));
  2338. // 信息对象地址
  2339. data.append(static_cast<char>(msgAddress & 0xFF));
  2340. data.append(static_cast<char>((msgAddress >> 8) & 0xFF));
  2341. data.append(static_cast<char>((msgAddress >> 16) & 0xFF));
  2342. QByteArray nameBytes = fileNameToBytes(fileName);
  2343. //data.append(nameBytes);
  2344. data.append(static_cast<char>(fileName.toInt() & 0xFF));
  2345. data.append(static_cast<char>((fileName.toInt() >> 8) & 0xFF));
  2346. data.append(static_cast<char>(section)); // 节名
  2347. data.append(static_cast<char>(lsq)); // 限定词 确认段
  2348. emit logMessage(QString("Requesting section: %1, Section: %2").arg(fileName).arg(section));
  2349. return sendIMessage(data);
  2350. }
  2351. // 工具函数实现
  2352. QString IEC104MasterQt::bytesToFileName(const unsigned char *bytes)
  2353. {
  2354. char name[5] = {0};
  2355. for (int i = 0; i < 2; i++) {
  2356. name[i] = bytes[i];
  2357. }
  2358. return QString::fromLocal8Bit(name).trimmed();
  2359. }
  2360. QByteArray IEC104MasterQt::fileNameToBytes(const QString &fileName)
  2361. {
  2362. QByteArray bytes(2, 0);
  2363. QByteArray nameBytes = fileName.toLocal8Bit();
  2364. for (int i = 0; i < qMin(2, nameBytes.size()); i++) {
  2365. bytes[i] = nameBytes[i];
  2366. }
  2367. return bytes;
  2368. }
  2369. int IEC104MasterQt::bytesToFileSize(const unsigned char *bytes)
  2370. {
  2371. return (bytes[0]) | (bytes[1] << 8);
  2372. }
  2373. QByteArray IEC104MasterQt::fileSizeToBytes(int fileSize)
  2374. {
  2375. QByteArray bytes(2, 0);
  2376. bytes[0] = static_cast<char>(fileSize & 0xFF);
  2377. bytes[1] = static_cast<char>((fileSize >> 8) & 0xFF);
  2378. return bytes;
  2379. }
  2380. QMap<QString, FileTransferInfo> IEC104MasterQt::getFileTransfers() const
  2381. {
  2382. QMutexLocker locker(&m_fileMutex);
  2383. return m_fileTransfers;
  2384. }
  2385. bool IEC104MasterQt::acknowledgeFile(int commonAddress,int msgAddress, const QString &fileName, bool accept)
  2386. {
  2387. if (!m_connected)
  2388. {
  2389. emit logMessage("Not connected, cannot acknowledge file");
  2390. return false;
  2391. }
  2392. QByteArray data;
  2393. data.append(static_cast<char>(F_AF_NA_1)); // 类型
  2394. data.append(static_cast<char>(0x01)); // 可变结构限定词
  2395. data.append(static_cast<char>(0x07)); // 传送原因
  2396. data.append(static_cast<char>(0x00));
  2397. data.append(static_cast<char>(commonAddress & 0xFF));
  2398. data.append(static_cast<char>((commonAddress >> 8) & 0xFF));
  2399. // 信息对象地址
  2400. data.append(static_cast<char>(msgAddress & 0xFF));
  2401. data.append(static_cast<char>((msgAddress >> 8) & 0xFF));
  2402. data.append(static_cast<char>((msgAddress >> 16) & 0xFF));
  2403. QByteArray nameBytes = fileNameToBytes(fileName);
  2404. data.append(nameBytes);
  2405. data.append(static_cast<char>(0x00)); // 节名
  2406. data.append(static_cast<char>(accept ? 0 : 1)); // 认可状态: 0=接受, 1=拒绝
  2407. emit logMessage(QString("Acknowledge file: %1, Accept: %2").arg(fileName).arg(accept));
  2408. return sendIMessage(data);
  2409. }
  2410. bool IEC104MasterQt::acknowledgeSection(int commonAddress,int msgAddress, const QString &fileName, int section, bool accept)
  2411. {
  2412. if (!m_connected)
  2413. {
  2414. emit logMessage("Not connected, cannot acknowledge section");
  2415. return false;
  2416. }
  2417. QByteArray data;
  2418. data.append(static_cast<char>(F_AF_NA_1)); // 类型
  2419. data.append(static_cast<char>(0x01)); // 可变结构限定词
  2420. data.append(static_cast<char>(0x07)); // 传送原因
  2421. data.append(static_cast<char>(0x00));
  2422. data.append(static_cast<char>(commonAddress & 0xFF));
  2423. data.append(static_cast<char>((commonAddress >> 8) & 0xFF));
  2424. // 信息对象地址
  2425. data.append(static_cast<char>(msgAddress & 0xFF));
  2426. data.append(static_cast<char>((msgAddress >> 8) & 0xFF));
  2427. data.append(static_cast<char>((msgAddress >> 16) & 0xFF));
  2428. QByteArray nameBytes = fileNameToBytes(fileName);
  2429. data.append(nameBytes);
  2430. data.append(static_cast<char>(section)); // 节名
  2431. data.append(static_cast<char>(accept ? 0 : 1)); // 认可状态: 0=接受, 1=拒绝
  2432. emit logMessage(QString("Acknowledge section: %1, Section: %2, Accept: %3")
  2433. .arg(fileName).arg(section).arg(accept));
  2434. return sendIMessage(data);
  2435. }