CuteHMI - Modbus (CuteHMI.Modbus.2)
RegisterControllerMixin.hpp
1 #ifndef H_EXTENSIONS_CUTEHMI_MODBUS_2_INCLUDE_CUTEHMI_MODBUS_INTERNAL_REGISTERCONTROLLERMIXIN_HPP
2 #define H_EXTENSIONS_CUTEHMI_MODBUS_2_INCLUDE_CUTEHMI_MODBUS_INTERNAL_REGISTERCONTROLLERMIXIN_HPP
3 
4 #include "common.hpp"
5 #include "functions.hpp"
6 #include "RegisterControllerTraits.hpp"
7 #include "../AbstractDevice.hpp"
8 
9 #include <QBasicTimer>
10 #include <QJsonObject>
11 
12 namespace cutehmi {
13 namespace modbus {
14 namespace internal {
15 
16 template <typename DERIVED>
18 {
19  protected:
21 
23 
24  void setValue(ValueType value);
25 
26  void writeValue();
27 
28  void timerEvent(QTimerEvent * event);
29 
30  void onRequestCompleted(QJsonObject request, QJsonObject reply);
31 
32  void clearPostponedWrite();
33 
34  private:
35  const DERIVED & derived() const;
36 
37  DERIVED & derived();
38 };
39 
40 template <typename DERIVED>
42 {
43 }
44 
45 template <typename DERIVED>
47 {
48  derived().m->requestedValue = value;
49 
50  if (derived().device() == nullptr)
51  CUTEHMI_WARNING("Attempting to set value, but no device has been assigned to the controller.");
52  else {
53  if (derived().m->value != value) {
54  derived().m->adjustingValue = true;
55 
56  if (derived().writeMode() == DERIVED::WRITE_DELAYED)
57  derived().m->writeTimer.start(derived().writeDelay(), & derived());
58  else if (derived().writeMode() == DERIVED::WRITE_POSTPONED) {
59  // If m->requestId is not null, then controller has not finished with previous request yet.
60  if (derived().m->requestId != nullptr)
61  derived().m->postponedWritePending = true;
62  else
63  derived().writeValue();
64  } else if (derived().writeMode() == DERIVED::WRITE_IMMEDIATE)
65  derived().writeValue();
66  // If write mode is WRITE_EXPLICIT, then do nothing.
67  } else {
68  if (derived().writeMode() == DERIVED::WRITE_DELAYED) {
69  if (derived().m->requestId == nullptr) {
70  derived().m->adjustingValue = false;
71  derived().m->writeTimer.stop();
72  } else {
73  derived().m->adjustingValue = true;
74  derived().m->writeTimer.start(derived().writeDelay(), & derived());
75  }
76  } else if (derived().writeMode() == DERIVED::WRITE_POSTPONED) {
77  if (derived().m->requestId != nullptr) {
78  derived().m->adjustingValue = true;
79  derived().m->postponedWritePending = true;
80  }
81  } else if ((derived().writeMode() == DERIVED::WRITE_IMMEDIATE) && (derived().m->requestId != nullptr))
82  derived().writeValue();
83  }
84  }
85 }
86 
87 template<typename DERIVED>
89 {
90  derived().requestWrite(derived().m->requestedValue);
91  derived().m->adjustingValue = false;
92 }
93 
94 template<typename DERIVED>
96 {
97  Q_UNUSED(event)
98 
99  derived().m->writeTimer.stop();
100  derived().writeValue();
101 }
102 
103 template<typename DERIVED>
105 {
106  AbstractDevice::Function function = static_cast<AbstractDevice::Function>(request.value("function").toInt());
107  QUuid requestId = QUuid::fromString(request.value("id").toString());
108  bool success = reply.value("success").toBool();
109  quint16 address = static_cast<quint16>(request.value("payload").toObject().value("address").toDouble());
110  if (function == derived().writeRegisterFunction()) {
111  if (requestId == derived().m->requestId) {
112  if (success) {
113  if (derived().readOnWrite())
114  // Non-null requestId implies that device is not null (see setDevice() and setupRegister()).
115  derived().requestReadRegisters(static_cast<quint16>(derived().address()), derived().bytes(), & derived().m->requestId);
116  else {
117  derived().setBusy(derived().m->postponedWritePending);
118 
119  emit derived().valueWritten();
120 
121  // Without readOnWrite verification, written value acts as one, which is currently set in register.
123  derived().updateValue(request.value("payload").toObject().value("value"));
124 
125  derived().m->requestId = nullptr;
126  }
127  } else {
128  if (!derived().readOnWrite())
129  derived().setBusy(derived().m->postponedWritePending);
130 
131  emit derived().valueFailed();
132 
133  derived().m->requestId = nullptr;
134  }
135  }
136  } else if (function == derived().readRegistersFunction()) {
137  quint16 endAddress = address + static_cast<quint16>(request.value("payload").toObject().value("amount").toDouble()) - 1;
138  if (static_cast<quint16>(derived().address()) >= address && static_cast<quint16>(derived().address()) <= endAddress) {
139  if (requestId == derived().m->requestId) {
140  // If requestId == m->requestId, then request must have been made by controller due to readOnWrite.
141 
142  derived().setBusy(!success || derived().m->postponedWritePending);
143 
144  // Non-null requestId implies that register is not null (see setDevice() and setupRegister()).
145  if (success && (derived().verifyRegisterValue()))
146  emit derived().valueWritten();
147  else
148  emit derived().valueMismatch(); // In case of read failure we can't verify value. Even though write request must have succeeded assume valueMismatch() in such case.
149 
150  derived().updateValue();
151 
152  derived().m->requestId = nullptr;
153  } else if (derived().m->requestId.isNull()) {
154  // Standard update, if controller is not waiting for its own request made due to readOnWrite.
155 
156  derived().setBusy(!success || derived().m->postponedWritePending);
157 
158  derived().updateValue();
159  }
160  }
161  }
162  clearPostponedWrite();
163 }
164 
165 template<typename DERIVED>
167 {
168  if (derived().m->postponedWritePending)
169  derived().writeValue();
170  derived().m->postponedWritePending = false;
171 }
172 
173 template <typename DERIVED>
174 const DERIVED & RegisterControllerMixin<DERIVED>::derived() const
175 {
176  return static_cast<const DERIVED &>(*this);
177 }
178 
179 template <typename DERIVED>
180 DERIVED & RegisterControllerMixin<DERIVED>::derived()
181 {
182  return static_cast<DERIVED &>(*this);
183 }
184 
185 }
186 }
187 }
188 
189 #endif
190 
191 //(c)C: Copyright © 2019, Michał Policht <michal@policht.pl>. All rights reserved.
192 //(c)C: This file is a part of CuteHMI.
193 //(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.
194 //(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.
195 //(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/>.
QJsonValue::toBool
bool toBool(bool defaultValue) const const
QJsonValue::toObject
QJsonObject toObject() const const
QTimerEvent
cutehmi::modbus::internal::RegisterControllerTraits
Definition: RegisterControllerTraits.hpp:15
QJsonValue::toInt
int toInt(int defaultValue) const const
QUuid::fromString
QUuid fromString(QStringView text)
cutehmi::modbus::internal::RegisterControllerMixin::onRequestCompleted
void onRequestCompleted(QJsonObject request, QJsonObject reply)
Definition: RegisterControllerMixin.hpp:104
cutehmi::modbus::internal::RegisterControllerMixin
Definition: RegisterControllerMixin.hpp:17
QUuid
QJsonValue::toString
QString toString() const const
cutehmi::modbus::internal::RegisterControllerMixin::setValue
void setValue(ValueType value)
Definition: RegisterControllerMixin.hpp:46
cutehmi::modbus::internal::RegisterControllerMixin::ValueType
RegisterControllerTraits< DERIVED >::ValueType ValueType
Definition: RegisterControllerMixin.hpp:20
cutehmi::modbus::internal::RegisterControllerMixin::RegisterControllerMixin
RegisterControllerMixin()
Definition: RegisterControllerMixin.hpp:41
cutehmi
QJsonObject::value
QJsonValue value(const QString &key) const const
QJsonObject
cutehmi::modbus::internal::RegisterControllerMixin::writeValue
void writeValue()
Definition: RegisterControllerMixin.hpp:88
std::internal
T internal(T... args)
CUTEHMI_WARNING
#define CUTEHMI_WARNING(EXPR)
cutehmi::modbus::internal::RegisterControllerMixin::clearPostponedWrite
void clearPostponedWrite()
Definition: RegisterControllerMixin.hpp:166
cutehmi::modbus::AbstractDevice::Function
Function
Definition: AbstractDevice.hpp:51
cutehmi::modbus::internal::RegisterControllerMixin::timerEvent
void timerEvent(QTimerEvent *event)
Definition: RegisterControllerMixin.hpp:95
QJsonValue::toDouble
double toDouble(double defaultValue) const const