-
Notifications
You must be signed in to change notification settings - Fork 0
/
ModbusRTU.h
408 lines (340 loc) · 13.2 KB
/
ModbusRTU.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
//---------------------------------------------------------------------------
#ifndef ModbusRTUH
#define ModbusRTUH
//---------------------------------------------------------------------------
#include <vector>
#include <algorithm>
#include <System.DateUtils.hpp>
#include <cstdint>
#include <boost/crc.hpp>
#include "CommPort.h"
#include "Modbus.h"
#define FT_MICROSECOND ( 10UI64 )
#define FT_MILLISECOND ( 1000UI64 * FT_MICROSECOND )
#define FT_SECOND ( 1000UI64 * FT_MILLISECOND )
#define FT_MINUTE ( 60UI64 * FT_SECOND )
#define FT_HOUR ( 60UI64 * FT_MINUTE )
#define FT_DAY ( 24UI64 * FT_HOUR )
#if !defined( MODBUS_RTU_DEFAULT_RETRY_COUNT )
#define MODBUS_RTU_DEFAULT_RETRY_COUNT 3
#endif
//---------------------------------------------------------------------------
namespace Modbus {
//---------------------------------------------------------------------------
class ERTUParametersError : public EBaseException
{
public:
ERTUParametersError( String Msg ) : EBaseException( Msg ) {}
};
//---------------------------------------------------------------------------
namespace Master {
//---------------------------------------------------------------------------
class RTUProtocol : public Protocol {
public:
using FrameCont = std::vector<uint8_t>;
enum class FlowDirection { RX, TX };
using TFlowEvent =
void __fastcall ( __closure * )(
RTUProtocol& Sender, FlowDirection Dir, const FrameCont& Frame
);
explicit RTUProtocol( int RetryCount = MODBUS_RTU_DEFAULT_RETRY_COUNT );
~RTUProtocol();
String GetCommPort() const;
void SetCommPort( String Val );
int GetCommSpeed() const;
void SetCommSpeed( int Val );
int GetCommParity() const;
void SetCommParity( int Val );
int GetCommBits() const;
void SetCommBits( int Val );
int GetCommStopBits() const;
void SetCommStopBits( int Val );
TFlowEvent SetFlowEventHandler( TFlowEvent EventHandler );
protected:
virtual String DoGetProtocolName() const override { return _T( "Modbus RTU" ); }
virtual String DoGetProtocolParamsStr() const override;
virtual void DoOpen() override;
virtual void DoClose() override;
virtual bool DoIsConnected() const override;
// DoReadCoilStatus
// DoReadInputStatus
virtual void DoReadHoldingRegisters( Context const & Context,
RegAddrType StartAddr,
RegCountType PointCount,
RegDataType* Data ) override;
virtual void DoReadInputRegisters( Context const & Context,
RegAddrType StartAddr,
RegCountType PointCount,
RegDataType* Data ) override;
// DoForceSingleCoil
virtual void DoPresetSingleRegister( Context const & Context,
RegAddrType Addr, RegDataType Data ) override;
// DoReadExceptionStatus
// DoDiagnostics
// DoProgram484
// DoPoll484
// DoFetchCommEventCtr
// DoFetchCommEventLog
// DoProgramController
// DoPollController
// DoForceMultipleCoils
// DoPresetMultipleRegisters
void DoPresetMultipleRegisters( Context const & Context,
RegAddrType StartAddr,
RegCountType PointCount,
const RegDataType* Data ) override;
// DoReportSlave
// DoProgram884_M84
// DoResetCommLink
// DoReadGeneralReference
// DoWriteGeneralReference
// DoMaskWrite4XRegister
virtual void DoMaskWrite4XRegister( Context const & Context,
RegAddrType Addr,
RegDataType AndMask,
RegDataType OrMask ) override;
// DoReadWrite4XRegisters
// DoReadFIFOQueue
private:
static Context const DefaultRTUContext;
TCommPort commPort_;
bool cancelTXEcho_;
int retryCount_;
unsigned timeoutValue_;
TFlowEvent onFlowEvent_;
#if defined ( _DEBUG ) && defined( VIEW_RTU_PROTOCOL_DIAG )
template<typename P, typename It>
void ShowBuffer( const P& Prefix, It Begin, It End );
#endif
template<typename OutputIterator>
void SendAndReceiveFrames( Context const & Context,
const FrameCont& TxFrame, OutputIterator Out,
FrameCont::size_type RxFramelength,
int RetryCount );
template<typename OutputIterator>
bool SendAndReceiveFramesInt( Context const & Context,
const FrameCont& TxFrame, OutputIterator Out,
FrameCont::size_type RxFramelength,
bool NoThrow );
template<typename OutputIterator>
static OutputIterator Write( OutputIterator Out, uint8_t Data );
template<typename OutputIterator>
static OutputIterator Write( OutputIterator Out, uint16_t Data );
template<typename InputIterator>
static InputIterator Read( InputIterator In, uint16_t& Data );
template<typename InputIterator>
static uint16_t ComputeCRC( InputIterator Begin, InputIterator End );
template<typename OutputIterator>
static OutputIterator WriteAddressPointCountPair( OutputIterator Out,
RegAddrType StartAddr,
RegCountType PointCount );
template<typename T>
__int64 GetMinimumFrameTime( T FrameLen ) const;
unsigned int GetParityBitCount() const;
unsigned int GetStopBitCount() const;
// static unsigned __int64 GetSystemTimeAsUint64();
// unsigned __int64 GetTimeoutIntervalAsUint64() const;
static String ParityToStr( int Val );
static String StopBitsToStr( int Val );
void ReadRegisters( FunctionCode FnCode, Context const & Context,
RegAddrType StartAddr, RegCountType PointCount,
RegDataType* Data );
public:
template<typename OutputIterator, typename InputIterator>
static OutputIterator WriteCRC( OutputIterator Out,
InputIterator Begin, InputIterator End );
__property bool CancelTXEcho = { read = cancelTXEcho_, write = cancelTXEcho_ };
__property int RetryCount = { read = retryCount_, write = retryCount_ };
__property unsigned TimeoutValue = { read = timeoutValue_, write = timeoutValue_ };
};
//---------------------------------------------------------------------------
template<typename OutputIterator>
OutputIterator RTUProtocol::Write( OutputIterator Out, uint8_t Data )
{
*Out++ = Data;
return Out;
}
//---------------------------------------------------------------------------
template<typename OutputIterator>
OutputIterator RTUProtocol::Write( OutputIterator Out, uint16_t Data )
{
*Out++ = ( Data >> 8 ) & 0xFF; // Data Hi
*Out++ = Data & 0xFF; // Data Lo
return Out;
}
//---------------------------------------------------------------------------
template<typename InputIterator>
InputIterator RTUProtocol::Read( InputIterator In, uint16_t& Data )
{
uint16_t const HiData = static_cast<uint16_t>( *In++ & 0xFF ) << 8;
Data = HiData + *In++;
return In;
}
//---------------------------------------------------------------------------
template<typename OutputIterator>
OutputIterator RTUProtocol::WriteAddressPointCountPair( OutputIterator Out,
RegAddrType StartAddr,
RegCountType PointCount )
{
Out = Write( Out, StartAddr );
return Write( Out, PointCount );
}
//---------------------------------------------------------------------------
template<typename OutputIterator, typename InputIterator>
OutputIterator RTUProtocol::WriteCRC( OutputIterator Out,
InputIterator Begin,
InputIterator End )
{
uint16_t const CRC = ComputeCRC( Begin, End );
*Out++ = CRC & 0xFF;
*Out++ = ( CRC >> 8 ) & 0xFF;
return Out;
}
//---------------------------------------------------------------------------
template<typename InputIterator>
uint16_t RTUProtocol::ComputeCRC( InputIterator Begin, InputIterator End )
{
return std::for_each( Begin, End, boost::crc_16_type( 0xFFFF ) ).checksum();
}
//---------------------------------------------------------------------------
#if defined ( _DEBUG ) && defined( VIEW_RTU_PROTOCOL_DIAG )
template<typename P, typename It>
void RTUProtocol::ShowBuffer( const P& Prefix, It Begin, It End )
{
String Msg = String( Prefix );
while ( Begin != End ) {
Msg += IntToHex( int( *Begin++ ) & 0xFF, 2 ) + String( _T( ' ' ) );
}
::OutputDebugString( Msg.c_str() );
}
#endif
template<typename OutputIterator>
void RTUProtocol::SendAndReceiveFrames( Context const & Context,
const FrameCont& TxFrame, OutputIterator Out,
FrameCont::size_type RxFramelength,
int RetryCount )
{
for ( int Idx = 0 ; ; ++Idx ) {
if ( SendAndReceiveFramesInt( Context, TxFrame, Out, RxFramelength, Idx < RetryCount ) ) {
break;
}
}
}
template<typename OutputIterator>
bool RTUProtocol::SendAndReceiveFramesInt( Context const & Context,
const FrameCont& TxFrame, OutputIterator Out,
FrameCont::size_type RxFramelength,
bool NoThrow )
{
static const FrameCont::size_type EatEchoExtraCharCount = 0;
FrameCont RxFrame;
RxFrame.reserve( 200 );
#if defined ( _DEBUG ) && defined( VIEW_RTU_PROTOCOL_DIAG )
ShowBuffer(
Format(
_T( "TX(%s): " ), ARRAYOFCONST( ( commPort_.GetCommPort().c_str() ) )
),
TxFrame.begin(), TxFrame.end()
);
#endif
if ( onFlowEvent_ )
onFlowEvent_( *this, FlowDirection::TX, TxFrame );
commPort_.PurgeCommPort();
commPort_.WriteBuffer(
const_cast<FrameCont::value_type*>( &TxFrame[0] ), TxFrame.size()
);
if ( CancelTXEcho ) {
for ( FrameCont::size_type Cnt = TxFrame.size() + EatEchoExtraCharCount ; Cnt ; --Cnt ) {
uint8_t Char;
if ( !commPort_.ReadBytes( &Char, 1 ) ) {
if ( NoThrow ) {
return false;
}
else {
throw EContextException(
Context,
SysErrorMessage( GetLastError() )
);
}
}
}
}
boost::crc_16_type Crc( 0xFFFF );
for ( FrameCont::size_type Idx = 0 ; Idx < RxFramelength ; ++Idx ) {
uint8_t Char;
if ( unsigned int const BytesRead = commPort_.ReadBytes( &Char, 1 ) ) {
Crc.process_byte( Char );
RxFrame.push_back( Char );
if ( ( Char & 0x80 ) && Idx == 1 ) {
//#if defined( _DEBUG )
//::OutputDebugString( Format( _T( "%.2X" ), ARRAYOFCONST( ( int( Char ) ) ) ).c_str() );
//endif
RxFramelength = 5;
}
}
else if ( NoThrow ) {
return false;
}
else if ( !BytesRead ) {
throw EContextException(
Context,
_T( "Timeout error" )
);
}
else {
throw EContextException(
Context,
SysErrorMessage( GetLastError() )
);
}
}
#if defined ( _DEBUG ) && defined( VIEW_RTU_PROTOCOL_DIAG )
ShowBuffer(
Format(
_T( "RX(%s): " ), ARRAYOFCONST( ( commPort_.GetCommPort().c_str() ) )
),
RxFrame.begin(), RxFrame.end()
);
#endif
if ( onFlowEvent_ ) {
onFlowEvent_( *this, FlowDirection::RX, RxFrame );
}
if ( Crc.checksum() ) {
if ( NoThrow ) {
return false;
}
else {
throw EContextException( Context, _T( "Bad CRC (RX)" ) );
}
}
if ( RxFrame[0] != TxFrame[0] ) {
if ( NoThrow ) {
return false;
}
else {
throw EContextException( Context, _T( "Slave address mismatch" ) );
}
}
if ( RxFrame[1] != TxFrame[1] ) {
if ( NoThrow ) {
return false;
}
else {
if ( RxFrame[1] & 0x80 ) {
RaiseStandardException( Context, ExceptionCode( RxFrame[2] ) );
}
else {
throw EContextException( Context, _T( "Function code mismatch" ) );
}
}
}
std::copy( RxFrame.begin() + 2, RxFrame.end(), Out );
return true;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
}; // End of namespace Master
//---------------------------------------------------------------------------
}; // End of namespace Modbus
//---------------------------------------------------------------------------
#endif