Blame view

src/ModbusAdapter.cpp 8.36 KB
cadcf24a   Peter M. Groen   Setting up workin...
1
2
  
  #include "ConnectionConfig.h"
cadcf24a   Peter M. Groen   Setting up workin...
3
  #include "ModbusAdapter.h"
cadcf24a   Peter M. Groen   Setting up workin...
4
5
6
  
  #include <cstring>  /// Added for memset functionality
  
cadcf24a   Peter M. Groen   Setting up workin...
7
  ModbusAdapter::ModbusAdapter()
527f96d4   Peter M. Groen   Setting up workin...
8
      : modbus( nullptr )
cadcf24a   Peter M. Groen   Setting up workin...
9
  {
5735b406   Peter M. Groen   Revert "Setting u...
10
      this->InitBuffers();
cadcf24a   Peter M. Groen   Setting up workin...
11
12
13
14
15
16
17
  }
  
  ModbusAdapter::~ModbusAdapter()
  {
  
  }
  
527f96d4   Peter M. Groen   Setting up workin...
18
  bool ModbusAdapter::ModbusConnect( const ConnectionConfig &config )
cadcf24a   Peter M. Groen   Setting up workin...
19
  {
527f96d4   Peter M. Groen   Setting up workin...
20
      if( connected )
cadcf24a   Peter M. Groen   Setting up workin...
21
      {
b85a3e4a   Peter M. Groen   Setting up workin...
22
          this->ModbusDisconnect();   // Will already set m_connected
cadcf24a   Peter M. Groen   Setting up workin...
23
24
      }
  
527f96d4   Peter M. Groen   Setting up workin...
25
      connectionType = config.getType();
cadcf24a   Peter M. Groen   Setting up workin...
26
  
527f96d4   Peter M. Groen   Setting up workin...
27
      switch( connectionType )
cadcf24a   Peter M. Groen   Setting up workin...
28
29
      {
          case ConnectionType::CT_SERIAL:
527f96d4   Peter M. Groen   Setting up workin...
30
              connected = this->ModbusConnectRTU( config.getPort(), config.getBaudRate(), config.getParity(), config.getDataBits(), config.getStopBits(), config.getTimeOut(), MODBUS_RTU_RTS_NONE );
cadcf24a   Peter M. Groen   Setting up workin...
31
32
33
              break;
  
          case ConnectionType::CT_TCP:
527f96d4   Peter M. Groen   Setting up workin...
34
              connected = this->ModbusConnectTCP( config.getIpAddress(), config.getTcpPort(), 10 );
cadcf24a   Peter M. Groen   Setting up workin...
35
36
37
38
39
40
              break;
  
          default:
              // throw a sensible message or return an errorcode.
              break;
      }
527f96d4   Peter M. Groen   Setting up workin...
41
      return connected;
cadcf24a   Peter M. Groen   Setting up workin...
42
43
  }
  
b85a3e4a   Peter M. Groen   Setting up workin...
44
  bool ModbusAdapter::ModbusDisconnect()
cadcf24a   Peter M. Groen   Setting up workin...
45
  {
527f96d4   Peter M. Groen   Setting up workin...
46
      if( modbus != nullptr )
cadcf24a   Peter M. Groen   Setting up workin...
47
      {
527f96d4   Peter M. Groen   Setting up workin...
48
          if( connected )
cadcf24a   Peter M. Groen   Setting up workin...
49
          {
527f96d4   Peter M. Groen   Setting up workin...
50
51
              modbus_close( modbus );
              modbus_free( modbus );
cadcf24a   Peter M. Groen   Setting up workin...
52
53
          }
          // Clean up after ourselves.
527f96d4   Peter M. Groen   Setting up workin...
54
          modbus = nullptr;
cadcf24a   Peter M. Groen   Setting up workin...
55
      }
b85a3e4a   Peter M. Groen   Setting up workin...
56
  
527f96d4   Peter M. Groen   Setting up workin...
57
      connected = false;
b85a3e4a   Peter M. Groen   Setting up workin...
58
      return true;
cadcf24a   Peter M. Groen   Setting up workin...
59
60
61
62
  }
  
  modbusData ModbusAdapter::ModbusReadData( int slaveId, int functionCode, int startAddress, int noOfItems )
  {
527f96d4   Peter M. Groen   Setting up workin...
63
      if( modbus == nullptr )
cadcf24a   Peter M. Groen   Setting up workin...
64
65
66
67
68
      {
          // No context
          return modbusData();
      }
  
cadcf24a   Peter M. Groen   Setting up workin...
69
70
71
      int resultValue = -1;
      bool is16Bit = false;
  
527f96d4   Peter M. Groen   Setting up workin...
72
      modbus_set_slave( modbus, slaveId );
cadcf24a   Peter M. Groen   Setting up workin...
73
74
75
76
77
  
      // Request data from modbus.
      switch( functionCode )
      {
          case MODBUS_FC_READ_COILS:
527f96d4   Peter M. Groen   Setting up workin...
78
              resultValue = modbus_read_bits( modbus, startAddress, noOfItems, m_dest );
cadcf24a   Peter M. Groen   Setting up workin...
79
80
              break;
          case MODBUS_FC_READ_DISCRETE_INPUTS:
527f96d4   Peter M. Groen   Setting up workin...
81
              resultValue = modbus_read_input_bits( modbus, startAddress, noOfItems, m_dest );
cadcf24a   Peter M. Groen   Setting up workin...
82
83
              break;
          case MODBUS_FC_READ_HOLDING_REGISTERS:
527f96d4   Peter M. Groen   Setting up workin...
84
              resultValue = modbus_read_registers( modbus, startAddress, noOfItems, m_dest16 );
cadcf24a   Peter M. Groen   Setting up workin...
85
86
              break;
          case MODBUS_FC_READ_INPUT_REGISTERS:
527f96d4   Peter M. Groen   Setting up workin...
87
              resultValue = modbus_read_input_registers( modbus, startAddress, noOfItems, m_dest16 );
cadcf24a   Peter M. Groen   Setting up workin...
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
              break;
  
          default:
              break;
  
      }
  
      // Read the databuffers
      if( resultValue != noOfItems )
      {
          return modbusData();
      }
  
      modbusData resultData;
      for( int index = 0; index < noOfItems; ++index )
      {
          resultData.push_back( (is16Bit ? static_cast<uint16_t>(m_dest16[index]) : static_cast<uint16_t>(m_dest[index])) );
      }
  
527f96d4   Peter M. Groen   Setting up workin...
107
      modbus_flush( modbus );   // flush data
cadcf24a   Peter M. Groen   Setting up workin...
108
109
110
111
112
113
114
      this->ClearBuffers();
  
      return resultData;
  }
  
  modbusData ModbusAdapter::ModbusReadHoldReg( int slaveId, int startAddress, int noOfItems )
  {
527f96d4   Peter M. Groen   Setting up workin...
115
      if( modbus == nullptr )
cadcf24a   Peter M. Groen   Setting up workin...
116
117
118
119
120
121
122
123
      {
          return modbusData();
      }
  
      int resultValue = -1;       /// Return value from read functions
  
      // -- Start Critical Section --- ?? //
  
527f96d4   Peter M. Groen   Setting up workin...
124
      modbus_set_slave( modbus, slaveId );
cadcf24a   Peter M. Groen   Setting up workin...
125
      /// Request data from modbus
527f96d4   Peter M. Groen   Setting up workin...
126
      resultValue = modbus_read_registers( modbus, startAddress, noOfItems, m_dest16 );
cadcf24a   Peter M. Groen   Setting up workin...
127
128
129
130
131
132
133
134
135
136
137
138
139
  
      /// Read the databuffers
      if( resultValue != noOfItems )
      {
          return modbusData();
      }
  
      modbusData resultData;
      for( int index = 0; index < noOfItems; ++index )
      {
          resultData.push_back( static_cast<uint16_t>(m_dest16[index]) );
      }
  
527f96d4   Peter M. Groen   Setting up workin...
140
      modbus_flush( modbus );
5735b406   Peter M. Groen   Revert "Setting u...
141
      this->ClearBuffers();
cadcf24a   Peter M. Groen   Setting up workin...
142
143
144
145
146
147
148
149
  
      // -- End Critical Section --- ?? //
  
      return resultData;
  }
  
  void ModbusAdapter::ModBusWriteData( int slaveId, int functionCode, int startAddress, int noOfItems, std::vector<int>values )
  {
527f96d4   Peter M. Groen   Setting up workin...
150
      if( modbus == nullptr )
cadcf24a   Peter M. Groen   Setting up workin...
151
      {
b85a3e4a   Peter M. Groen   Setting up workin...
152
          // No modbus context. Sensible log-line and exit.
cadcf24a   Peter M. Groen   Setting up workin...
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
          return;
      }
  
      // ------------------------------------------------
      // Create 8 bits databuffer
      auto * data8 = new uint8_t[noOfItems];
      for( int index = 0; index < noOfItems; ++index )
      {
          data8[index] = values[index];
      }
  
      // Create same buffer for 16 bits data
      auto * data16 = new uint8_t[noOfItems];
      for( int index = 0; index < noOfItems; ++index )
      {
          data16[index] = values[index];
      }
      // ------------------------------------------------
  
      int resultValue = -1;
  
527f96d4   Peter M. Groen   Setting up workin...
174
      modbus_set_slave( modbus, slaveId );
cadcf24a   Peter M. Groen   Setting up workin...
175
176
177
178
179
  
      // Request data from modbus
      switch( functionCode )
      {
          case MODBUS_FC_WRITE_SINGLE_COIL:
527f96d4   Peter M. Groen   Setting up workin...
180
              resultValue = modbus_write_bit( modbus, startAddress, values[0] );
cadcf24a   Peter M. Groen   Setting up workin...
181
182
183
              noOfItems = 1;
              break;
          case MODBUS_FC_WRITE_SINGLE_REGISTER:
527f96d4   Peter M. Groen   Setting up workin...
184
              resultValue = modbus_write_register( modbus, startAddress, values[0] );
cadcf24a   Peter M. Groen   Setting up workin...
185
186
187
188
              noOfItems = 1;
              break;
          case MODBUS_FC_WRITE_MULTIPLE_COILS:
  
527f96d4   Peter M. Groen   Setting up workin...
189
              resultValue = modbus_write_bits( modbus, startAddress, noOfItems, data8 );
cadcf24a   Peter M. Groen   Setting up workin...
190
191
              break;
          case MODBUS_FC_WRITE_MULTIPLE_REGISTERS:
527f96d4   Peter M. Groen   Setting up workin...
192
              resultValue = modbus_write_bits( modbus, startAddress, noOfItems, data16 );
cadcf24a   Peter M. Groen   Setting up workin...
193
194
195
              break;
      }
  
5735b406   Peter M. Groen   Revert "Setting u...
196
197
      delete[]  data8;
      delete[] data16;
527f96d4   Peter M. Groen   Setting up workin...
198
      modbus_flush(modbus); // Flush data.
cadcf24a   Peter M. Groen   Setting up workin...
199
200
201
202
203
204
205
206
207
  
      if( resultValue != noOfItems )
      {
          // Create a log line that writing the register failed. Maybe increment ErrorCounter(s)
      }
  }
  
  bool ModbusAdapter::isConnected() const
  {
527f96d4   Peter M. Groen   Setting up workin...
208
      return connected;
cadcf24a   Peter M. Groen   Setting up workin...
209
210
  }
  
46785270   Peter M. Groen   Setting up workin...
211
  std::string ModbusAdapter::ErrorString( int errnum ) const
cadcf24a   Peter M. Groen   Setting up workin...
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
  {
      switch(errnum)
      {
          case EINVAL:
              return "Protocol context is NULL";
              break;
          case ETIMEDOUT:
              return "Timeout";
              break;
          case ECONNRESET:
              return "Connection reset";
              break;
          case ECONNREFUSED:
              return "Connection refused";
              break;
          case EPIPE:
              return "Socket error";
              break;
          default:
              return modbus_strerror(errno);
      }
  }
  
  /* ============= PRIVATE METHODS ============= */
5735b406   Peter M. Groen   Revert "Setting u...
236
  bool ModbusAdapter::ModbusConnectRTU( const std::string &serialport, int baud, char parity, int dataBits, int stopBits, int RTS, int timeOut )
cadcf24a   Peter M. Groen   Setting up workin...
237
  {
527f96d4   Peter M. Groen   Setting up workin...
238
      modbus = modbus_new_rtu( serialport.c_str(), baud, parity, dataBits, stopBits, RTS );
cadcf24a   Peter M. Groen   Setting up workin...
239
240
241
242
243
  
  #ifdef LIB_MODBUS_DEBUG_OUTPUT
      // Do sensible logging through PRIVA_LOG
      m_modbus_set_debug( m_modbus, 1 );
  #endif
527f96d4   Peter M. Groen   Setting up workin...
244
      if( modbus == nullptr )
cadcf24a   Peter M. Groen   Setting up workin...
245
246
247
      {
          // We can stop here. Nothing to be done as we don't have a valid context
          // Log to PRIVA_LOG
527f96d4   Peter M. Groen   Setting up workin...
248
249
          connected = false;
          return connected;
cadcf24a   Peter M. Groen   Setting up workin...
250
      }
527f96d4   Peter M. Groen   Setting up workin...
251
      else if( modbus && modbus_connect( modbus ) == -1 )
cadcf24a   Peter M. Groen   Setting up workin...
252
253
254
      {
          // We could not connect to the selected serial port.
          // We can stop here. Nothing to be done as we don't have a valid connection
527f96d4   Peter M. Groen   Setting up workin...
255
256
257
          modbus_free( modbus );
          connected = false;
          return connected;
cadcf24a   Peter M. Groen   Setting up workin...
258
259
      }
  
527f96d4   Peter M. Groen   Setting up workin...
260
      connected = true;
b85a3e4a   Peter M. Groen   Setting up workin...
261
  
cadcf24a   Peter M. Groen   Setting up workin...
262
      // Set recovery mode
527f96d4   Peter M. Groen   Setting up workin...
263
      modbus_set_error_recovery( modbus, MODBUS_ERROR_RECOVERY_PROTOCOL );
cadcf24a   Peter M. Groen   Setting up workin...
264
265
  
      // Set the response timeout
527f96d4   Peter M. Groen   Setting up workin...
266
      modbus_set_response_timeout( modbus, timeOut, 0 );
cadcf24a   Peter M. Groen   Setting up workin...
267
  
527f96d4   Peter M. Groen   Setting up workin...
268
      return connected;
cadcf24a   Peter M. Groen   Setting up workin...
269
270
  }
  
b85a3e4a   Peter M. Groen   Setting up workin...
271
  bool ModbusAdapter::ModbusConnectTCP( const std::string &ip, int port, int timeOut )
cadcf24a   Peter M. Groen   Setting up workin...
272
273
274
275
  {
      if( ip.empty() )
      {
          // Nothing to be done. Set an Logline to PRIVA_LOG and exit.
b85a3e4a   Peter M. Groen   Setting up workin...
276
          return false;
cadcf24a   Peter M. Groen   Setting up workin...
277
278
      }
  
527f96d4   Peter M. Groen   Setting up workin...
279
      modbus = modbus_new_tcp( ip.c_str(), port );
cadcf24a   Peter M. Groen   Setting up workin...
280
281
282
283
284
285
  
  #ifdef LIB_MODBUS_DEBUG_OUTPUT
      // Do sensible logging through PRIVA_LOG
      m_modbus_set_debug( m_modbus, 1 );
  #endif
  
527f96d4   Peter M. Groen   Setting up workin...
286
      if( modbus == nullptr )
cadcf24a   Peter M. Groen   Setting up workin...
287
288
289
      {
          // We can stop here. Nothing to be done as we don't have a valid context
          // Log to PRIVA_LOG
b85a3e4a   Peter M. Groen   Setting up workin...
290
          return false;
cadcf24a   Peter M. Groen   Setting up workin...
291
      }
527f96d4   Peter M. Groen   Setting up workin...
292
      else if( modbus && modbus_connect( modbus ) == -1 )
cadcf24a   Peter M. Groen   Setting up workin...
293
294
295
      {
          // We could not connect to the selected serial port.
          // We can stop here. Nothing to be done as we don't have a valid connection
527f96d4   Peter M. Groen   Setting up workin...
296
297
298
          modbus_free( modbus );
          connected = false;
          return connected;
cadcf24a   Peter M. Groen   Setting up workin...
299
300
      }
  
527f96d4   Peter M. Groen   Setting up workin...
301
      connected = true;
b85a3e4a   Peter M. Groen   Setting up workin...
302
  
cadcf24a   Peter M. Groen   Setting up workin...
303
      // Set recovery mode
527f96d4   Peter M. Groen   Setting up workin...
304
      modbus_set_error_recovery( modbus, MODBUS_ERROR_RECOVERY_PROTOCOL );
cadcf24a   Peter M. Groen   Setting up workin...
305
306
  
      // Set the response timeout
527f96d4   Peter M. Groen   Setting up workin...
307
      modbus_set_response_timeout( modbus, timeOut, 0 );
cadcf24a   Peter M. Groen   Setting up workin...
308
  
527f96d4   Peter M. Groen   Setting up workin...
309
      return connected;
cadcf24a   Peter M. Groen   Setting up workin...
310
  }
5735b406   Peter M. Groen   Revert "Setting u...
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
  
  void ModbusAdapter::InitBuffers()
  {
      // Setup memory for Data.
      m_dest = (uint8_t *)malloc(2000 + sizeof(uint8_t));
      m_dest16 = (uint16_t *)malloc(125 * sizeof(uint16_t));
  
      this->ClearBuffers();
  }
  
  void ModbusAdapter::ClearBuffers()
  {
      memset(m_dest, 0, 2000 * sizeof(uint8_t));
      memset(m_dest16, 0, 125 * sizeof(uint16_t));
  }