CuteHMI - Modbus (CuteHMI.Modbus.4)
AbstractDevice.hpp
1#ifndef H_EXTENSIONS_CUTEHMI_MODBUS_4_INCLUDE_CUTEHMI_MODBUS_ABSTRACTDEVICE_HPP
2#define H_EXTENSIONS_CUTEHMI_MODBUS_4_INCLUDE_CUTEHMI_MODBUS_ABSTRACTDEVICE_HPP
3
4#include "internal/common.hpp"
5#include "internal/RegisterTraits.hpp"
6#include "InputRegister.hpp"
7#include "internal/HoldingRegister.hpp"
8#include "internal/InputRegister.hpp"
9#include "DiscreteInput.hpp"
10#include "Coil.hpp"
11
12#include <cutehmi/InplaceError.hpp>
13#include <cutehmi/services/Serviceable.hpp>
14
15#include <QObject>
16#include <QUuid>
17#include <QJsonObject>
18#include <QJsonArray>
19#include <QQmlListProperty>
20#include <QModbusPdu>
21#include <QQmlEngine>
22
23#include <list>
24
25namespace cutehmi {
26namespace modbus {
27
28namespace internal {
29class IterableTasks;
30}
31
35class CUTEHMI_MODBUS_API AbstractDevice:
36 public QObject,
38{
39 Q_OBJECT
40 QML_NAMED_ELEMENT(AbstractDevice)
41 QML_UNCREATABLE("AbstractDevice is an abstract class")
42
43 friend class test_AbstractDevice;
44 friend class test_AbstractServer;
45 friend class internal::IterableTasks;
46
47 public:
48 enum State {
52 CLOSED
53 };
54 Q_ENUM(State)
55
56 enum Function {
57 FUNCTION_INVALID = QModbusPdu::Invalid,
58 FUNCTION_READ_COILS = QModbusPdu::ReadCoils,
59 FUNCTION_READ_DISCRETE_INPUTS = QModbusPdu::ReadDiscreteInputs,
60 FUNCTION_READ_HOLDING_REGISTERS = QModbusPdu::ReadHoldingRegisters,
61 FUNCTION_READ_INPUT_REGISTERS = QModbusPdu::ReadInputRegisters,
62 FUNCTION_WRITE_COIL = QModbusPdu::WriteSingleCoil,
63 FUNCTION_WRITE_HOLDING_REGISTER = QModbusPdu::WriteSingleRegister,
64 FUNCTION_READ_EXCEPTION_STATUS = QModbusPdu::ReadExceptionStatus,
65 FUNCTION_DIAGNOSTICS = QModbusPdu::Diagnostics,
66 FUNCTION_FETCH_COMM_EVENT_COUNTER = QModbusPdu::GetCommEventCounter,
67 FUNCTION_FETCH_COMM_EVENT_LOG = QModbusPdu::GetCommEventLog,
68 FUNCTION_WRITE_MULTIPLE_COILS = QModbusPdu::WriteMultipleCoils,
69 FUNCTION_WRITE_MULTIPLE_HOLDING_REGISTERS = QModbusPdu::WriteMultipleRegisters,
70 FUNCTION_REPORT_SLAVE_ID = QModbusPdu::ReportServerId,
71 FUNCTION_READ_FILE_RECORD = QModbusPdu::ReadFileRecord,
72 FUNCTION_WRITE_FILE_RECORD = QModbusPdu::WriteFileRecord,
73 FUNCTION_MASK_WRITE_HOLDING_REGISTER = QModbusPdu::MaskWriteRegister,
74 FUNCTION_READ_WRITE_MULTIPLE_HOLDING_REGISTERS = QModbusPdu::ReadWriteMultipleRegisters,
75 FUNCTION_READ_FIFO_QUEUE = QModbusPdu::ReadFifoQueue,
76 // FUNCTION_ENCAPSULATED_INTERFACE_TRANSPORT = QModbusPdu::EncapsulatedInterfaceTransport, // Currently not implemented.
82 FUNCTION_RAW
83 };
84 Q_ENUM(Function)
85
86 enum DiagnosticsSubfunction : quint16 {
87 DIAGNOSTICS_RETURN_QUERY_DATA = 0x00,
88 DIAGNOSTICS_RESTART_COMM_OPTION = 0x01,
89 DIAGNOSTICS_RETURN_DIAGNOSTICS_REGISTER = 0x02,
90 DIAGNOSTICS_CHANGE_ASCII_INPUT_DELIMITER = 0x03,
91 DIAGNOSTICS_FORCE_LISTEN_ONLY_MODE = 0x04,
92 DIAGNOSTICS_CLEAR_COUNTERS_AND_DIAGNOSTICS_REGISTER = 0x0A,
93 DIAGNOSTICS_RETURN_BUS_MESSAGE_COUNT = 0x0B,
94 DIAGNOSTICS_RETURN_BUS_COMM_ERROR_COUNT = 0x0C,
95 DIAGNOSTICS_RETURN_BUS_EXCEPTION_ERROR_COUNT = 0x0D,
96 DIAGNOSTICS_RETURN_SLAVE_MESSAGE_COUNT = 0x0E,
97 DIAGNOSTICS_RETURN_SLAVE_NO_RESPONSE_COUNT = 0x0F,
98 DIAGNOSTICS_RETURN_SLAVE_NAK_COUNT = 0x10,
99 DIAGNOSTICS_RETURN_SLAVE_BUSY_COUNT = 0x11,
100 DIAGNOSTICS_RETURN_BUS_CHARACTER_OVERRUN_COUNT = 0x12,
101 DIAGNOSTICS_RETURN_IOP_OVERRUN_COUNT = 0x13,
102 DIAGNOSTICS_CLEAR_OVERRUN_COUNTER_AND_FLAG = 0x14,
103 DIAGNOSTICS_GET_CLEAR_MODBUS_PLUS_STATISTICS = 0x15
104 };
105 Q_ENUM(DiagnosticsSubfunction)
106
107
123
126 static constexpr int MAX_READ_TCP_COILS = 1976;
127
131 static constexpr int MAX_READ_RTU_COILS = 2008;
132
136 static constexpr int MAX_WRITE_TCP_COILS = 1944;
137
141 static constexpr int MAX_WRITE_RTU_COILS = 1976;
142
146 static constexpr int MAX_READ_TCP_DISCRETE_INPUTS = 1976;
147
151 static constexpr int MAX_READ_RTU_DISCRETE_INPUTS = 2008;
152
156 static constexpr int MAX_READ_TCP_HOLDING_REGISTERS = 123;
157
161 static constexpr int MAX_READ_RTU_HOLDING_REGISTERS = 125;
162
166 static constexpr int MAX_WRITE_TCP_HOLDING_REGISTERS = 123;
167
171 static constexpr int MAX_WRITE_RTU_HOLDING_REGISTERS = 125;
172
176 static constexpr int MAX_READ_TCP_INPUT_REGISTERS = 123;
177
181 static constexpr int MAX_READ_RTU_INPUT_REGISTERS = 125;
183
184 static constexpr quint16 MIN_ADDRESS = 0;
185 static constexpr quint16 MAX_ADDRESS = 65535;
186
187 static constexpr int INITIAL_MAX_READ_COILS = 16; // Max RTU: 2008, Max TCP: 1976
188 static constexpr int INITIAL_MAX_WRITE_COILS = 16; // Max RTU: 1976, Max TCP: 1944
189 static constexpr int INITIAL_MAX_READ_DISCRETE_INPUTS = 16; // Max RTU: 2008, Max TCP: 1976
190 static constexpr int INITIAL_MAX_WRITE_DISCRETE_INPUTS = 16; // Max RTU: N/A, Max TCP: N/A
191 static constexpr int INITIAL_MAX_READ_HOLDING_REGISTERS = 16; // Max RTU: 125, Max TCP: 123
192 static constexpr int INITIAL_MAX_WRITE_HOLDING_REGISTERS = 16; // Max RTU: 123, Max TCP: 121
193 static constexpr int INITIAL_MAX_READ_INPUT_REGISTERS = 16; // Max RTU: 125, Max TCP: 123
194 static constexpr int INITIAL_MAX_WRITE_INPUT_REGISTERS = 16; // Max RTU: N/A, Max TCP: N/A
195 static constexpr int INITIAL_MAX_REQUESTS = 1000;
196 static constexpr State INITIAL_STATE = CLOSED;
197 static constexpr bool INITIAL_READY = false;
198
199 Q_PROPERTY(State state READ state NOTIFY stateChanged)
200
201 Q_PROPERTY(bool ready READ ready NOTIFY readyChanged)
202
203 Q_PROPERTY(int maxReadCoils READ maxReadCoils WRITE setMaxReadCoils NOTIFY maxReadCoilsChanged)
204
205 Q_PROPERTY(int maxWriteCoils READ maxWriteCoils WRITE setMaxWriteCoils NOTIFY maxWriteCoilsChanged)
206
207 Q_PROPERTY(int maxReadDiscreteInputs READ maxReadDiscreteInputs WRITE setMaxReadDiscreteInputs NOTIFY maxReadDiscreteInputsChanged)
208
209 Q_PROPERTY(int maxWriteDiscreteInputs READ maxWriteDiscreteInputs WRITE setMaxWriteDiscreteInputs NOTIFY maxWriteDiscreteInputsChanged)
210
211 Q_PROPERTY(int maxReadHoldingRegisters READ maxReadHoldingRegisters WRITE setMaxReadHoldingRegisters NOTIFY maxReadHoldingRegistersChanged)
212
213 Q_PROPERTY(int maxWriteHoldingRegisters READ maxWriteHoldingRegisters WRITE setMaxWriteHoldingRegisters NOTIFY maxWriteHoldingRegistersChanged)
214
215 Q_PROPERTY(int maxReadInputRegisters READ maxReadInputRegisters WRITE setMaxReadInputRegisters NOTIFY maxReadInputRegistersChanged)
216
217 Q_PROPERTY(int maxWriteInputRegisters READ maxWriteInputRegisters WRITE setMaxWriteInputRegisters NOTIFY maxWriteInputRegistersChanged)
218
219 Q_PROPERTY(int maxRequests READ maxRequests WRITE setMaxRequests NOTIFY maxRequestsChanged)
220
221 State state() const;
222
227 bool ready() const;
228
229 int maxReadCoils() const;
230
231 void setMaxReadCoils(int maxReadCoils);
232
233 int maxWriteCoils() const;
234
235 void setMaxWriteCoils(int maxWriteCoils);
236
237 int maxReadDiscreteInputs() const;
238
239 void setMaxReadDiscreteInputs(int maxReadDiscreteInputs);
240
241 int maxWriteDiscreteInputs() const;
242
243 void setMaxWriteDiscreteInputs(int maxWriteDiscreteInputs);
244
245 int maxReadHoldingRegisters() const;
246
247 void setMaxReadHoldingRegisters(int maxReadHoldingRegisters);
248
249 int maxWriteHoldingRegisters() const;
250
251 void setMaxWriteHoldingRegisters(int maxWriteHoldingRegisters);
252
253 int maxReadInputRegisters() const;
254
255 void setMaxReadInputRegisters(int maxReadInputRegisters);
256
257 int maxWriteInputRegisters() const;
258
259 void setMaxWriteInputRegisters(int maxWriteInputRegisters);
260
261 int maxRequests() const;
262
263 void setMaxRequests(int maxRequests);
264
265 Coil * coilAt(quint16 address);
266
267 DiscreteInput * discreteInputAt(quint16 address);
268
269 HoldingRegister * holdingRegisterAt(quint16 address);
270
271 InputRegister * inputRegisterAt(quint16 address);
272
283 Q_INVOKABLE void requestReadCoils(quint16 address, quint16 amount = 1, QUuid * requestId = nullptr);
284
292 Q_INVOKABLE void requestWriteCoil(quint16 address, bool value, QUuid * requestId = nullptr);
293
302 Q_INVOKABLE void requestWriteMultipleCoils(quint16 address, QJsonArray values, QUuid * requestId = nullptr);
303
314 Q_INVOKABLE void requestReadDiscreteInputs(quint16 address, quint16 amount = 1, QUuid * requestId = nullptr);
315
324 Q_INVOKABLE void requestWriteDiscreteInput(quint16 address, bool value, QUuid * requestId = nullptr);
325
334 Q_INVOKABLE void requestWriteMultipleDiscreteInputs(quint16 address, QJsonArray values, QUuid * requestId = nullptr);
335
347 Q_INVOKABLE void requestReadHoldingRegisters(quint16 address, quint16 amount = 1, QUuid * requestId = nullptr);
348
357 Q_INVOKABLE void requestWriteHoldingRegister(quint16 address, quint16 value, QUuid * requestId = nullptr);
358
367 Q_INVOKABLE void requestWriteMultipleHoldingRegisters(quint16 address, QJsonArray values, QUuid * requestId = nullptr);
368
380 Q_INVOKABLE void requestReadInputRegisters(quint16 address, quint16 amount = 1, QUuid * requestId = nullptr);
381
390 Q_INVOKABLE void requestWriteInputRegister(quint16 address, quint16 value, QUuid * requestId = nullptr);
391
400 Q_INVOKABLE void requestWriteMultipleInputRegisters(quint16 address, QJsonArray values, QUuid * requestId = nullptr);
401
410 Q_INVOKABLE void requestDiagnostics(cutehmi::modbus::AbstractDevice::DiagnosticsSubfunction subfunction, quint16 data, QUuid * requestId = nullptr);
411
417 Q_INVOKABLE void requestReadExceptionStatus(QUuid * requestId = nullptr);
418
424 Q_INVOKABLE void requestFetchCommEventCounter(QUuid * requestId = nullptr);
425
431 Q_INVOKABLE void requestFetchCommEventLog(QUuid * requestId = nullptr);
432
438 Q_INVOKABLE void requestReportSlaveId(QUuid * requestId = nullptr);
439
450 Q_INVOKABLE void requestMaskWriteHoldingRegister(quint16 address, quint16 andMask, quint16 orMask, QUuid * requestId = nullptr);
451
462 Q_INVOKABLE void requestReadWriteMultipleHoldingRegisters(quint16 readAddress, quint16 amount, quint16 writeAddress, QJsonArray values, QUuid * requestId = nullptr);
463
471 Q_INVOKABLE void requestReadFIFOQueue(quint16 address, QUuid * requestId = nullptr);
472
482 Q_INVOKABLE QJsonObject readFileRecordSubrequest(quint16 file, quint16 address, quint16 amount);
483
494 Q_INVOKABLE void requestReadFileRecord(QJsonArray subrequests, QUuid * requestId = nullptr);
495
505 Q_INVOKABLE QJsonObject writeFileRecordSubrequest(quint16 file, quint16 address, QJsonArray values);
506
517 Q_INVOKABLE void requestWriteFileRecord(QJsonArray subrequests, QUuid * requestId = nullptr);
518
533 Q_INVOKABLE void request(cutehmi::modbus::AbstractDevice::Function function, QJsonObject payload, QUuid * requestId = nullptr);
534
535 public slots:
536 virtual void open() = 0;
537
538 virtual void close() = 0;
539
540 signals:
541 void errored(cutehmi::InplaceError error);
542
543 void stateChanged();
544
545 void readyChanged();
546
547 void maxReadCoilsChanged();
548
549 void maxWriteCoilsChanged();
550
551 void maxReadDiscreteInputsChanged();
552
553 void maxWriteDiscreteInputsChanged();
554
555 void maxReadHoldingRegistersChanged();
556
557 void maxWriteHoldingRegistersChanged();
558
559 void maxReadInputRegistersChanged();
560
561 void maxWriteInputRegistersChanged();
562
563 void maxRequestsChanged();
564
565 void requestCompleted(QJsonObject request, QJsonObject reply);
566
567 protected:
568 typedef typename internal::RegisterTraits<internal::Coil>::Container CoilDataContainer;
569 typedef typename internal::RegisterTraits<internal::DiscreteInput>::Container DiscreteInputDataContainer;
570 typedef typename internal::RegisterTraits<internal::HoldingRegister>::Container HoldingRegisterDataContainer;
571 typedef typename internal::RegisterTraits<internal::InputRegister>::Container InputRegisterDataContainer;
572
573 AbstractDevice(QObject * parent = nullptr);
574
581 ~AbstractDevice() override;
582
593 virtual void handleRequest(const QJsonObject & request) = 0;
594
595 const CoilDataContainer & coilData() const;
596
597 CoilDataContainer & coilData();
598
599 const DiscreteInputDataContainer & discreteInputData() const;
600
601 DiscreteInputDataContainer & discreteInputData();
602
603 const HoldingRegisterDataContainer & holdingRegisterData() const;
604
605 HoldingRegisterDataContainer & holdingRegisterData();
606
607 const InputRegisterDataContainer & inputRegisterData() const;
608
609 InputRegisterDataContainer & inputRegisterData();
610
611 QJsonObject pendingRequest(QUuid requestId) const;
612
613 QJsonObject takePendingRequest(QUuid requestId);
614
615 protected slots:
621 virtual void handleReply(QUuid requestId, QJsonObject reply);
622
623 void setState(cutehmi::modbus::AbstractDevice::State state);
624
625 void setReady(bool ready);
626
627 void handleError(cutehmi::InplaceError error);
628
629 protected:
630 Q_SIGNAL void broke();
631
632 Q_SIGNAL void stopped();
633
634 Q_SIGNAL void started();
635
636 private:
638
639 static void ValidatePayloadAddressKey(const QJsonObject & json, const QString & key = "address");
640
641 static void ValidatePayloadAmountKey(const QJsonObject & json, int max);
642
643 static void ValidatePayloadValueKeyInt(const QJsonObject & json);
644
645 static void ValidatetPayloadValueKeyBool(const QJsonObject & json);
646
647 static void ValidatePayloadReadFileRecordSubrequestsKey(const QJsonObject & json);
648
649 static void ValidatePayloadWriteFileRecordSubrequestsKey(const QJsonObject & json);
650
651 static void ValidateBoolKey(const QJsonObject & json, const QString & key, const QString & path = "");
652
653 static void ValidateNumberKey(const QJsonObject & json, const QString & key, const QString & path = "");
654
655 static void ValidateArrayKey(const QJsonObject & json, const QString & key, const QString & path = "");
656
657 static void ValidateNumberArrayKey(const QJsonObject & json, const QString & key, const QString & path = "");
658
659 static void ValidateBoolArrayKey(const QJsonObject & json, const QString & key, const QString & path = "");
660
661 static void ValidateObjectArrayKey(const QJsonObject & json, const QString & key, const QString & path = "", std::function<void(const QJsonObject & json, const QString & path)> filter = nullptr);
662
663 static void ValidateReadFileRecordSubresponsesKey(const QJsonObject & json, const QString & path = "");
664
665 static void ValidateWriteFileRecordSubresponsesKey(const QJsonObject & json, const QString & path = "");
666
667 bool validateRequest(const QJsonObject & request);
668
669 bool validateReply(const QJsonObject & request, const QJsonObject & reply);
670
671 struct Members
672 {
673 State state;
674 bool ready;
675 int maxReadCoils;
676 int maxWriteCoils;
677 int maxReadDiscreteInputs;
678 int maxWriteDiscreteInputs;
679 int maxReadHoldingRegisters;
680 int maxWriteHoldingRegisters;
681 int maxReadInputRegisters;
682 int maxWriteInputRegisters;
683 int maxRequests;
684 InputRegisterDataContainer inputRegisters;
685 HoldingRegisterDataContainer holdingRegisters;
686 DiscreteInputDataContainer discreteInputs;
687 CoilDataContainer coils;
688 PendingRequestsContainer pendingRequests;
689
690 Members():
691 state(INITIAL_STATE),
692 ready(INITIAL_READY),
693 maxReadCoils(INITIAL_MAX_READ_COILS),
694 maxWriteCoils(INITIAL_MAX_WRITE_COILS),
695 maxReadDiscreteInputs(INITIAL_MAX_READ_DISCRETE_INPUTS),
696 maxWriteDiscreteInputs(INITIAL_MAX_WRITE_DISCRETE_INPUTS),
697 maxReadHoldingRegisters(INITIAL_MAX_READ_HOLDING_REGISTERS),
698 maxWriteHoldingRegisters(INITIAL_MAX_WRITE_HOLDING_REGISTERS),
699 maxReadInputRegisters(INITIAL_MAX_READ_INPUT_REGISTERS),
700 maxWriteInputRegisters(INITIAL_MAX_WRITE_INPUT_REGISTERS),
701 maxRequests(INITIAL_MAX_REQUESTS)
702 {
703 }
704 };
705
707};
708
709}
710}
711
712Q_DECLARE_METATYPE(cutehmi::modbus::AbstractDevice::State) // Must use this macro despite using Q_ENUM to register metatype inside Init.
713
714#endif
715
716//(c)C: Copyright © 2022, Michał Policht <michal@policht.pl>. All rights reserved.
717//(c)C: SPDX-License-Identifier: LGPL-3.0-or-later OR MIT
718//(c)C: This file is a part of CuteHMI.
719//(c)C: CuteHMI is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
720//(c)C: CuteHMI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
721//(c)C: You should have received a copy of the GNU Lesser General Public License along with CuteHMI. If not, see <https://www.gnu.org/licenses/>.
722//(c)C: Additionally, this file is licensed under terms of MIT license as expressed below.
723//(c)C: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
724//(c)C: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
725//(c)C: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Abstract Modbus device.
Definition: AbstractDevice.hpp:38
internal::RegisterTraits< internal::InputRegister >::Container InputRegisterDataContainer
Definition: AbstractDevice.hpp:571
internal::RegisterTraits< internal::HoldingRegister >::Container HoldingRegisterDataContainer
Definition: AbstractDevice.hpp:570
DiagnosticsSubfunction
Definition: AbstractDevice.hpp:86
internal::RegisterTraits< internal::Coil >::Container CoilDataContainer
Definition: AbstractDevice.hpp:568
internal::RegisterTraits< internal::DiscreteInput >::Container DiscreteInputDataContainer
Definition: AbstractDevice.hpp:569
Function
Definition: AbstractDevice.hpp:56
@ FUNCTION_WRITE_INPUT_REGISTER
Definition: AbstractDevice.hpp:80
@ FUNCTION_WRITE_MULTIPLE_DISCRETE_INPUTS
Definition: AbstractDevice.hpp:79
@ FUNCTION_WRITE_MULTIPLE_INPUT_REGISTERS
Definition: AbstractDevice.hpp:81
@ FUNCTION_WRITE_DISCRETE_INPUT
Definition: AbstractDevice.hpp:78
State
Definition: AbstractDevice.hpp:48
@ OPENED
Definition: AbstractDevice.hpp:50
@ CLOSING
Definition: AbstractDevice.hpp:51
@ OPENING
Definition: AbstractDevice.hpp:49
Cached properties of 16 bit register.
Definition: Register16.hpp:15
Cached properties of 1 bit register.
Definition: Register1.hpp:15
Definition: IterableTasks.hpp:18