forked from Krzysztow/qtLibModbus
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmbconnection.h
445 lines (368 loc) · 14.5 KB
/
mbconnection.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
#ifndef MBCONNECTION_H
#define MBCONNECTION_H
#include <QHostAddress>
#include "errno.h"
#include "modbus.h"
#include "mbthreadedconnmanager.h"
/**
* @brief The MBConnection class - wrapper around libmodbus library that enables its normal as well as asynchronous use.
*/
class MBConnection:
public QObject
{
Q_OBJECT
public:
~MBConnection();
enum Parity {
ParityNone,
ParityEven,
ParityOdd
};
enum ErrorRecovery {
ERNone,
ERLink,
ERProtocol
};
enum RequestType {
Connect,
Close,
ReadBits,
ReadInputBits,
ReadRegisters,
ReadInputRegisters,
WriteBit,
WriteBits,
WriteRegister,
WriteRegisters,
ReportSlaveId,
RawRequest,
RawConfirmation,
InvalidRequest
};
/**
* Following are creation methods for two connection types TCP and Rtu.
* If you want to run connections in a separate thread, to use nonblocking behaviour, pass not NULL threadedConnManager pointer
* and then use a set of *Async(...) methods.
*/
static MBConnection *newTcpConnection(const QHostAddress &servIp, quint16 port, MBThreadedConnManager *threadedConnManager = 0);
static MBConnection *newRtuConnection(const QString &device, int baudRate, Parity parity, int dataBitsNo, int stopBitsNo, MBThreadedConnManager *threadedConnManager = 0);
/**
* Following set of methods is intended for connections that were created with runInThread set true.
* That means their invokations are to be run in a separate thread managed by @MBConnectionsManager.
* The order requests execution is in a FIFO manner.
*
* Every *Async invocation result in queued Modbus command. The command can be identified with invokeId returned by *Async methods.
* When the the given Modbus command is executed, its completion (or error) is broadcasted with emission of apprioriate signal (or @errorOccured()) with the particular invokeId.
*
* @note : Keep in mind, that you are interested in connection of the signals to the slots implemented in your objects. The MBConnection would run in the other thread, thus
* you have to use connection of Qt::Queuedconnection type in order not to fall into any race conditions.
*/
public:
/**
* @brief connectAsync - tries to connect to the host asynchronously.
* In any case, result information is broadcasted with connectionChanged() signal;
* If error occures additional errorOccured() signal is emitted.
* @return
*/
int connectAsync() {
if (0 != _threadMgr) {
incrementReqId();
_threadMgr->mbConnectAsync(this, _lastRequestId);
return _lastRequestId;
}
else
return -1;
}
/**
* @brief closeAsync - asynchronously closes the connection.
* @param prioritize - if set to true, doesn't wait for all the scheduled tasks to be executed - removes all the queued tasks that use this connection and disconnect will be issued just
* after current task is finished (since we can't stop current task).
* if set to false, first all the queued tasks will be executed and then connection closed.
* @return
*/
int closeAsync(bool prioritize = false) {
if (0 != _threadMgr) {
incrementReqId();
_threadMgr->closeAsync(this, prioritize, _lastRequestId);
return _lastRequestId;
}
else
return -1;
}
int readBitsAsync(int slaveId, int addr, QVector<quint8> *result) {
if (0 != _threadMgr) {
incrementReqId();
_threadMgr->readBitsAsync(this, slaveId, addr, result, _lastRequestId);
return _lastRequestId;
}
else
return -1;
}
int readInputBitsAsync(int slaveId, int addr, QVector<quint8> *result) {
if (0 != _threadMgr) {
incrementReqId();
_threadMgr->readInputBitsAsync(this, slaveId, addr, result, _lastRequestId);
return _lastRequestId;
}
else
return -1;
}
int readRegistersAsync(int slaveId, int addr, QVector<quint16> *result) {
if (0 != _threadMgr) {
incrementReqId();
_threadMgr->readRegistersAsync(this, slaveId, addr, result, _lastRequestId);
return _lastRequestId;
}
else
return -1;
}
int readInputRegistersAsync(int slaveId, int addr, QVector<quint16> *result) {
if (0 != _threadMgr) {
incrementReqId();
_threadMgr->readInputRegistersAsync(this, slaveId, addr, result, _lastRequestId);
return _lastRequestId;
}
else
return -1;
}
int writeBitAsync(int slaveId, int coilAddr, int status) {
++_lastRequestId;
if (0 != _threadMgr) {
incrementReqId();
_threadMgr->writeBitAsync(this, slaveId, coilAddr, status, _lastRequestId);
return _lastRequestId;
}
else
return -1;
}
int writeBitsAsync(int slaveId, int addr, QVector<quint8> *data) {
if (0 != _threadMgr) {
incrementReqId();
_threadMgr->writeBitsAsync(this, slaveId, addr, data, _lastRequestId);
return _lastRequestId;
}
else
return -1;
}
int writeRegisterAsync(int slaveId, int regAddr, int value) {
if (0 != _threadMgr) {
incrementReqId();
_threadMgr->writeRegisterAsync(this, slaveId, regAddr, value, _lastRequestId);
return _lastRequestId;
}
else
return -1;
}
int writeRegistersAsync(int slaveId, int addr, QVector<quint16> *data) {
if (0 != _threadMgr) {
incrementReqId();
_threadMgr->writeRegistersAsync(this, slaveId, addr, data, _lastRequestId);
return _lastRequestId;
}
else
return -1;
}
int reportSlaveIdAsync(int slaveId, QVector<quint8> *dest) {
if (0 != _threadMgr) {
incrementReqId();
_threadMgr->reportSlaveIdAsync(this, slaveId, dest, _lastRequestId);
return _lastRequestId;
}
else
return -1;
}
int sendRawRequestAsync(int slaveId, QVector<quint8> *dest) {
if (0 != _threadMgr) {
incrementReqId();
_threadMgr->reportSlaveIdAsync(this, slaveId, dest, _lastRequestId);
return _lastRequestId;
}
else
return -1;
}
int waitForConfirmationAsync(int slaveId, QVector<quint8> *resp) {
if (0 != _threadMgr) {
incrementReqId();
_threadMgr->waitForConfirmationAsync(this, slaveId, resp, _lastRequestId);
return _lastRequestId;
}
else
return -1;
}
/**
* Following set of methods is just a wrapper around libmobdus.
* These methods are to be invoked on objects that were created without threadManager.
* If you want to use connection that was meant to be run in a separate thread (created with threadManager),
* use *Async() methods set.
*/
public:
int setSlave(int slave) {
Q_ASSERT((0 == _threadMgr) ? true : (QThread::currentThread() == _threadMgr->managedThread()));
return modbus_set_slave(_ctxt, slave);
}
int mbConnect() {
Q_ASSERT((0 == _threadMgr) ? true : (QThread::currentThread() == _threadMgr->managedThread()));
return modbus_connect(_ctxt);
}
void close() {
Q_ASSERT((0 == _threadMgr) ? true : (QThread::currentThread() == _threadMgr->managedThread()));
modbus_close(_ctxt);
}
int readBits(int addr, int nb, QVector<quint8> *result) {
Q_ASSERT((0 == _threadMgr) ? true : (QThread::currentThread() == _threadMgr->managedThread()));
return modbus_read_bits(_ctxt, addr, nb, result->data());
}
int readInputBits(int addr, int nb, QVector<quint8> *result) {
Q_ASSERT((0 == _threadMgr) ? true : (QThread::currentThread() == _threadMgr->managedThread()));
return modbus_read_input_bits(_ctxt, addr, nb, result->data());
}
int readRegisters(int addr, int nb, QVector<quint16> *result) {
Q_ASSERT((0 == _threadMgr) ? true : (QThread::currentThread() == _threadMgr->managedThread()));
return modbus_read_registers(_ctxt, addr, nb, result->data());
}
int readInputRegisters(int addr, int nb, QVector<quint16> *result) {
Q_ASSERT((0 == _threadMgr) ? true : (QThread::currentThread() == _threadMgr->managedThread()));
return modbus_read_input_registers(_ctxt, addr, nb, result->data());
}
int writeBit(int coilAddr, int status) {
Q_ASSERT((0 == _threadMgr) ? true : (QThread::currentThread() == _threadMgr->managedThread()));
return modbus_write_bit(_ctxt, coilAddr, status);
}
int writeRegister(int regAddr, int value) {
Q_ASSERT((0 == _threadMgr) ? true : (QThread::currentThread() == _threadMgr->managedThread()));
return modbus_write_register(_ctxt, regAddr, value);
}
int writeBits(int addr, int nb, QVector<quint8> *data) {
Q_ASSERT((0 == _threadMgr) ? true : (QThread::currentThread() == _threadMgr->managedThread()));
return modbus_write_bits(_ctxt, addr, nb, data->data());
}
int writeRegisters(int addr, int nb, QVector<quint16> *data) {
Q_ASSERT((0 == _threadMgr) ? true : (QThread::currentThread() == _threadMgr->managedThread()));
return modbus_write_registers(_ctxt, addr, nb, data->data());
}
int reportSlaveId(QVector<quint8> *dest) {
Q_ASSERT((0 == _threadMgr) ? true : (QThread::currentThread() == _threadMgr->managedThread()));
return modbus_report_slave_id(_ctxt, dest->data());
}
int sendRawRequest(QVector<quint8> *req) {
Q_ASSERT((0 == _threadMgr) ? true : (QThread::currentThread() == _threadMgr->managedThread()));
return modbus_send_raw_request(_ctxt, req->data(), req->size());
}
int waitForConfirmation(QVector<quint8> *resp) {
Q_ASSERT((0 == _threadMgr) ? true : (QThread::currentThread() == _threadMgr->managedThread()));
return modbus_receive_confirmation(_ctxt, resp->data());
}
int writeAndReadRegisters(int writeAddr, int writeNb, QVector<quint16> *wData,
int readAddr, int readNb, QVector<quint16> *rData) {
Q_ASSERT((0 == _threadMgr) ? true : (QThread::currentThread() == _threadMgr->managedThread()));
return modbus_write_and_read_registers(_ctxt, writeAddr, writeNb, wData->data(),
readAddr, readNb, rData->data());
}
public:
int setErrorRecovery(ErrorRecovery erMode) {
//@todo : not thread safe. This should be done only when the connection is not executing any request currelntly.
modbus_error_recovery_mode mode;
switch (erMode) {
case (ERLink):
mode = MODBUS_ERROR_RECOVERY_LINK;
break;
case (ERProtocol):
mode = MODBUS_ERROR_RECOVERY_PROTOCOL;
break;
case (ERNone)://fall through
default:
mode = MODBUS_ERROR_RECOVERY_NONE;
break;
}
return modbus_set_error_recovery(_ctxt, mode);
}
void setResponseTimeout(int timeout_ms) {
//@todo : not thread safe. This should be done only when the connection is not executing any request currelntly.
Q_ASSERT(timeout_ms > 0);
timeval tv;
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
modbus_set_response_timeout(_ctxt, &tv);
}
int responseTimeout() {
timeval tv;
modbus_get_response_timeout(_ctxt, &tv);
return ((tv.tv_sec * 1000) + (tv.tv_sec / 1000));
}
//negative value means don't care about the byte timeout
void setByteTimeout(int timeout_ms) {
//@todo : not thread safe. This should be done only when the connection is not executing any request currelntly.
timeval tv;
if (timeout_ms < 0) {
tv.tv_sec = -1;
tv.tv_usec = -1;
}
else {
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
}
modbus_set_byte_timeout(_ctxt, &tv);
}
int byteTimeout() {
timeval tv;
modbus_get_byte_timeout(_ctxt, &tv);
return ((tv.tv_sec * 1000) + (tv.tv_sec / 1000));
}
int headerLength() {
return modbus_get_header_length(_ctxt);
}
int flush() {
return modbus_flush(_ctxt);
}
void setDebug(bool debug) {
modbus_set_debug(_ctxt, debug);
}
static const char *errorStr(int errnum) {
return modbus_strerror(errnum);
}
public:
bool cancelRequest(int invokeId) {
if (0 != _threadMgr) {
return _threadMgr->cancelRequest(this, invokeId);
}
else
return false;
}
signals:
//! todo: where to use qRegisterMetaType<MBConnection::RequestType>("MBConnection::RequestType") to do it only once and within library?
void requestFinished(int invokeId, int res, /*MBConnection::RequestType*/int type);
void rawRequestSent(int invokeId, int res);
void confirmationReceived(int invokeId, int res);
void connectionChanged(int invokeId, bool isConnected);
void errorOccured(int invokeId, int errNo, /*MBConnection::RequestType*/int type);
void requestCancelled(int invokeId);
public:
inline void emitRequestFinished(int invokeId, int res, MBConnection::RequestType type) {
emit requestFinished(invokeId, res, type);
}
inline void emitRawRequestSent(int invokeId, int res) {
emit rawRequestSent(invokeId, res);
}
inline void emitConfirmationReceived(int invokeId, int res) {
emit confirmationReceived(invokeId, res);
}
inline void emitConnectionChanged(int invokeId, bool isConnected) {
emit connectionChanged(invokeId, isConnected);
}
inline void emitErrorOccured(int invokeId, int errNo, MBConnection::RequestType type) {
emit errorOccured(invokeId, errNo, type);
}
inline void emitRequestCancelled(int invokeId) {
emit requestCancelled(invokeId);
}
private:
MBConnection(modbus_t *ctxt, MBThreadedConnManager *threadMgr);
inline void incrementReqId() {
if (++_lastRequestId < 0)
_lastRequestId = 0;
}
int _lastRequestId;
modbus_t *_ctxt;
MBThreadedConnManager *_threadMgr;
};
#endif // MBCONNECTION_H