1 | // Copyright (C) 2021 basysKom GmbH, opensource@basyskom.com |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qopcuahistoryreadresponseimpl_p.h" |
5 | |
6 | QT_BEGIN_NAMESPACE |
7 | |
8 | quint64 QOpcUaHistoryReadResponseImpl::m_currentHandle = 0; |
9 | |
10 | QOpcUaHistoryReadResponseImpl::QOpcUaHistoryReadResponseImpl(const QOpcUaHistoryReadRawRequest &request) |
11 | : m_requestType(RequestType::ReadRaw) |
12 | , m_readRawRequest(request) |
13 | , m_handle(++m_currentHandle) |
14 | { |
15 | } |
16 | |
17 | QOpcUaHistoryReadResponseImpl::QOpcUaHistoryReadResponseImpl(const QOpcUaHistoryReadEventRequest &request) |
18 | : m_requestType(RequestType::ReadEvent) |
19 | , m_readEventRequest(request) |
20 | , m_handle(++m_currentHandle) |
21 | { |
22 | } |
23 | |
24 | QOpcUaHistoryReadResponseImpl::~QOpcUaHistoryReadResponseImpl() |
25 | { |
26 | releaseContinuationPoints(); |
27 | } |
28 | |
29 | bool QOpcUaHistoryReadResponseImpl::hasMoreData() const |
30 | { |
31 | return m_state == QOpcUaHistoryReadResponse::State::MoreDataAvailable; |
32 | } |
33 | |
34 | bool QOpcUaHistoryReadResponseImpl::readMoreData() |
35 | { |
36 | if (!hasMoreData()) |
37 | return false; |
38 | |
39 | if (m_requestType == RequestType::ReadRaw) { |
40 | const auto request = createReadRawRequestWithContinuationPoints(); |
41 | emit historyReadRawRequested(request, continuationPoints: m_continuationPoints, releaseContinuationPoints: false, handle: handle()); |
42 | return true; |
43 | } else if (m_requestType == RequestType::ReadEvent) { |
44 | const auto request = createEventRequestWithContinuationPoints(); |
45 | emit historyReadEventsRequested(request, continuationPoints: m_continuationPoints, releaseContinuationPoints: false, handle: handle()); |
46 | return true; |
47 | } |
48 | |
49 | return false; |
50 | } |
51 | |
52 | QOpcUaHistoryReadResponse::State QOpcUaHistoryReadResponseImpl::state() const |
53 | { |
54 | return m_state; |
55 | } |
56 | |
57 | bool QOpcUaHistoryReadResponseImpl::releaseContinuationPoints() |
58 | { |
59 | if (m_requestType == RequestType::ReadRaw) { |
60 | const auto request = createReadRawRequestWithContinuationPoints(); |
61 | |
62 | if (!request.nodesToRead().isEmpty()) |
63 | emit historyReadRawRequested(request, continuationPoints: m_continuationPoints, releaseContinuationPoints: true, handle: handle()); |
64 | |
65 | m_continuationPoints.clear(); |
66 | |
67 | setState(QOpcUaHistoryReadResponse::State::Finished); |
68 | } else if (m_requestType == RequestType::ReadEvent) { |
69 | const auto request = createEventRequestWithContinuationPoints(); |
70 | |
71 | if (!request.nodesToRead().isEmpty()) |
72 | emit historyReadEventsRequested(request, continuationPoints: m_continuationPoints, releaseContinuationPoints: true, handle: handle()); |
73 | |
74 | m_continuationPoints.clear(); |
75 | |
76 | setState(QOpcUaHistoryReadResponse::State::Finished); |
77 | }; |
78 | |
79 | return true; |
80 | } |
81 | |
82 | QList<QOpcUaHistoryData> QOpcUaHistoryReadResponseImpl::data() const |
83 | { |
84 | return m_data; |
85 | } |
86 | |
87 | QList<QOpcUaHistoryEvent> QOpcUaHistoryReadResponseImpl::events() const |
88 | { |
89 | return m_events; |
90 | } |
91 | |
92 | QOpcUa::UaStatusCode QOpcUaHistoryReadResponseImpl::serviceResult() const |
93 | { |
94 | return m_serviceResult; |
95 | } |
96 | |
97 | void QOpcUaHistoryReadResponseImpl::handleDataAvailable(const QList<QOpcUaHistoryData> &data, const QList<QByteArray> &continuationPoints, |
98 | QOpcUa::UaStatusCode serviceResult, quint64 responseHandle) |
99 | { |
100 | if (responseHandle != handle()) |
101 | return; |
102 | |
103 | m_serviceResult = serviceResult; |
104 | m_continuationPoints = continuationPoints; |
105 | |
106 | if (m_data.empty()) { |
107 | m_data = data; |
108 | } else { |
109 | int index = 0; |
110 | for (const auto &result : data) { |
111 | auto &target = m_data[m_dataMapping.at(i: index++)]; |
112 | target.setStatusCode(result.statusCode()); |
113 | for (const auto &value : result.result()) { |
114 | target.addValue(value); |
115 | } |
116 | } |
117 | } |
118 | |
119 | bool found = false; |
120 | for (const auto &continuationPoint : m_continuationPoints) { |
121 | if (!continuationPoint.isEmpty()) { |
122 | setState(QOpcUaHistoryReadResponse::State::MoreDataAvailable); |
123 | found = true; |
124 | break; |
125 | } |
126 | } |
127 | |
128 | if (!found) |
129 | setState(QOpcUaHistoryReadResponse::State::Finished); |
130 | |
131 | emit readHistoryDataFinished(results: m_data, serviceResult: m_serviceResult); |
132 | } |
133 | |
134 | void QOpcUaHistoryReadResponseImpl::handleEventsAvailable(const QList<QOpcUaHistoryEvent> &data, const QList<QByteArray> &continuationPoints, |
135 | QOpcUa::UaStatusCode serviceResult, quint64 responseHandle) |
136 | { |
137 | if (responseHandle != handle()) |
138 | return; |
139 | |
140 | m_serviceResult = serviceResult; |
141 | m_continuationPoints = continuationPoints; |
142 | |
143 | if (m_events.empty()) { |
144 | m_events = data; |
145 | } else { |
146 | int index = 0; |
147 | for (const auto &result : data) { |
148 | auto &target = m_events[m_dataMapping.at(i: index++)]; |
149 | target.setStatusCode(result.statusCode()); |
150 | for (const auto &event : result.events()) { |
151 | target.addEvent(value: event); |
152 | } |
153 | } |
154 | } |
155 | |
156 | bool found = false; |
157 | for (const auto &continuationPoint : m_continuationPoints) { |
158 | if (!continuationPoint.isEmpty()) { |
159 | setState(QOpcUaHistoryReadResponse::State::MoreDataAvailable); |
160 | found = true; |
161 | break; |
162 | } |
163 | } |
164 | |
165 | if (!found) |
166 | setState(QOpcUaHistoryReadResponse::State::Finished); |
167 | |
168 | emit readHistoryEventsFinished(results: m_events, serviceResult: m_serviceResult); |
169 | } |
170 | |
171 | void QOpcUaHistoryReadResponseImpl::handleRequestError(quint64 requestHandle) |
172 | { |
173 | if (requestHandle == handle()) |
174 | setState(QOpcUaHistoryReadResponse::State::Error); |
175 | } |
176 | |
177 | quint64 QOpcUaHistoryReadResponseImpl::handle() const |
178 | { |
179 | return m_handle; |
180 | } |
181 | |
182 | void QOpcUaHistoryReadResponseImpl::setState(QOpcUaHistoryReadResponse::State state) |
183 | { |
184 | if (m_state != state) { |
185 | m_state = state; |
186 | emit stateChanged(state); |
187 | } |
188 | } |
189 | |
190 | QOpcUaHistoryReadRawRequest QOpcUaHistoryReadResponseImpl::createReadRawRequestWithContinuationPoints() |
191 | { |
192 | QOpcUaHistoryReadRawRequest request; |
193 | request.setStartTimestamp(m_readRawRequest.startTimestamp()); |
194 | request.setEndTimestamp(m_readRawRequest.endTimestamp()); |
195 | request.setNumValuesPerNode(m_readRawRequest.numValuesPerNode()); |
196 | request.setReturnBounds(m_readRawRequest.returnBounds()); |
197 | request.setTimestampsToReturn(m_readRawRequest.timestampsToReturn()); |
198 | |
199 | int arrayIndex = 0; |
200 | QList<int> newDataMapping; |
201 | QList<QByteArray> newContinuationPoints; |
202 | |
203 | for (const auto &continuationPoint : m_continuationPoints) { |
204 | int mappingIndex = 0; |
205 | if (m_dataMapping.empty()) |
206 | mappingIndex = arrayIndex; |
207 | else |
208 | mappingIndex = m_dataMapping.at(i: arrayIndex); |
209 | |
210 | if (!continuationPoint.isEmpty()) { |
211 | newDataMapping.push_back(t: mappingIndex); |
212 | newContinuationPoints.push_back(t: continuationPoint); |
213 | request.addNodeToRead(nodeToRead: m_readRawRequest.nodesToRead().at(i: mappingIndex)); |
214 | } |
215 | |
216 | ++arrayIndex; |
217 | } |
218 | |
219 | m_dataMapping = newDataMapping; |
220 | m_continuationPoints = newContinuationPoints; |
221 | |
222 | return request; |
223 | } |
224 | |
225 | QOpcUaHistoryReadEventRequest QOpcUaHistoryReadResponseImpl::createEventRequestWithContinuationPoints() |
226 | { |
227 | QOpcUaHistoryReadEventRequest request; |
228 | request.setStartTimestamp(m_readEventRequest.startTimestamp()); |
229 | request.setEndTimestamp(m_readEventRequest.endTimestamp()); |
230 | request.setNumValuesPerNode(m_readEventRequest.numValuesPerNode()); |
231 | request.setFilter(m_readEventRequest.filter()); |
232 | |
233 | int arrayIndex = 0; |
234 | QList<int> newDataMapping; |
235 | QList<QByteArray> newContinuationPoints; |
236 | |
237 | for (const auto &continuationPoint : m_continuationPoints) { |
238 | int mappingIndex = 0; |
239 | if (m_dataMapping.empty()) |
240 | mappingIndex = arrayIndex; |
241 | else |
242 | mappingIndex = m_dataMapping.at(i: arrayIndex); |
243 | |
244 | if (!continuationPoint.isEmpty()) { |
245 | newDataMapping.push_back(t: mappingIndex); |
246 | newContinuationPoints.push_back(t: continuationPoint); |
247 | request.addNodeToRead(nodeToRead: m_readEventRequest.nodesToRead().at(i: mappingIndex)); |
248 | } |
249 | |
250 | ++arrayIndex; |
251 | } |
252 | |
253 | m_dataMapping = newDataMapping; |
254 | m_continuationPoints = newContinuationPoints; |
255 | |
256 | return request; |
257 | } |
258 | |
259 | QT_END_NAMESPACE |
260 | |