API
 
Loading...
Searching...
No Matches
modbus.cpp
Go to the documentation of this file.
1/** \file modbus.cpp
2 * \brief TCP transport implementation for the MagAO-X Modbus client.
3 *
4 * \author Jared R. Males (jaredmales@gmail.com)
5 */
6
7#include "modbus.hpp"
8
9#include <cerrno>
10#include <cstring>
11
12namespace
13{
14
15/// Close a failed modbus socket and throw a connection exception.
16[[noreturn]] void modbusConnectionError( modbus &mb, const char *operation )
17{
18 const int savedErrno = errno;
19
20 mb.modbus_close();
21
23 exc.msg = std::string( operation ) + " failed: " + std::strerror( savedErrno );
24
25 throw exc;
26}
27
28/// The Linux-specific flag that prevents `send` from raising `SIGPIPE`.
29#ifdef MSG_NOSIGNAL
30constexpr int modbusNoSignalFlag = MSG_NOSIGNAL;
31#else
32constexpr int modbusNoSignalFlag = 0;
33#endif
34
35} // namespace
36
37modbus::modbus( const std::string &host, uint16_t port ) : PORT{ port }, HOST{ host }
38{
39}
40
41modbus::modbus( const std::string &host ) : HOST{ host }
42{
43}
44
46{
47}
48
50{
51 _slaveid = id;
52}
53
55{
56 if( HOST == "" || PORT == 0 )
57 {
58 // std::cout << "Missing Host and Port" << std::endl;
59 return false;
60 }
61 else
62 {
63 // std::cout << "Found Proper Host "<< HOST << " and Port " <<PORT <<std::endl;
64 }
65
66 _socket = socket( AF_INET, SOCK_STREAM, 0 );
67 if( _socket == -1 )
68 {
69 // std::cout <<"Error Opening Socket" <<std::endl;
70 return false;
71 }
72 else
73 {
74 // std::cout <<"Socket Opened Successfully" << std::endl;
75 }
76
77 _server.sin_family = AF_INET;
78 _server.sin_addr.s_addr = inet_addr( HOST.c_str() );
79 _server.sin_port = htons( PORT );
80
81 if( connect( _socket, (struct sockaddr *)&_server, sizeof( _server ) ) < 0 )
82 {
83 // std::cout<< "Connection Error" << std::endl;
84 close( _socket );
85 _socket = -1;
86 return false;
87 }
88
89 // std::cout<< "Connected" <<std::endl;
90 _connected = true;
91 return true;
92}
93
95{
96 if( _socket >= 0 )
97 {
98 close( _socket );
99 }
100
101 _socket = -1;
102 _connected = false;
103}
104
105bool modbus::modbus_set_timeouts( int seconds, int microseconds )
106{
107 if( !_connected || _socket < 0 )
108 {
109 return false;
110 }
111
112 struct timeval timeout;
113 timeout.tv_sec = seconds;
114 timeout.tv_usec = microseconds;
115
116 if( setsockopt( _socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof( timeout ) ) != 0 )
117 {
118 return false;
119 }
120
121 if( setsockopt( _socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof( timeout ) ) != 0 )
122 {
123 return false;
124 }
125
126 return true;
127}
128
129void modbus::modbus_build_request( uint8_t *to_send, int address, int func )
130{
131 to_send[0] = (uint8_t)_msg_id >> 8;
132 to_send[1] = (uint8_t)( _msg_id & 0x00FF );
133 to_send[2] = 0;
134 to_send[3] = 0;
135 to_send[4] = 0;
136 to_send[6] = (uint8_t)_slaveid;
137 to_send[7] = (uint8_t)func;
138 to_send[8] = (uint8_t)( address >> 8 );
139 to_send[9] = (uint8_t)( address & 0x00FF );
140}
141
142void modbus::modbus_write( int address, int amount, int func, uint16_t *value )
143{
144 if( func == WRITE_COIL || func == WRITE_REG )
145 {
146 uint8_t to_send[12];
147 modbus_build_request( to_send, address, func );
148 to_send[5] = 6;
149 to_send[10] = (uint8_t)( value[0] >> 8 );
150 to_send[11] = (uint8_t)( value[0] & 0x00FF );
151 modbus_send( to_send, 12 );
152 }
153 else if( func == WRITE_REGS )
154 {
155 uint8_t to_send[13 + 2 * amount];
156 modbus_build_request( to_send, address, func );
157 to_send[5] = (uint8_t)( 5 + 2 * amount );
158 to_send[10] = (uint8_t)( amount >> 8 );
159 to_send[11] = (uint8_t)( amount & 0x00FF );
160 to_send[12] = (uint8_t)( 2 * amount );
161 for( int i = 0; i < amount; i++ )
162 {
163 to_send[13 + 2 * i] = (uint8_t)( value[i] >> 8 );
164 to_send[14 + 2 * i] = (uint8_t)( value[i] & 0x00FF );
165 }
166 modbus_send( to_send, 13 + 2 * amount );
167 }
168 else if( func == WRITE_COILS )
169 {
170 uint8_t to_send[14 + ( amount - 1 ) / 8];
171 modbus_build_request( to_send, address, func );
172 to_send[5] = (uint8_t)( 7 + ( amount - 1 ) / 8 );
173 to_send[10] = (uint8_t)( amount >> 8 );
174 to_send[11] = (uint8_t)( amount >> 8 );
175 to_send[12] = (uint8_t)( ( amount + 7 ) / 8 );
176 for( int i = 0; i < amount; i++ )
177 {
178 to_send[13 + ( i - 1 ) / 8] += (uint8_t)( value[i] << ( i % 8 ) );
179 }
180 modbus_send( to_send, 14 + ( amount - 1 ) / 8 );
181 }
182}
183
184void modbus::modbus_read( int address, int amount, int func )
185{
186 uint8_t to_send[12];
187 modbus_build_request( to_send, address, func );
188 to_send[5] = 6;
189 to_send[10] = (uint8_t)( amount >> 8 );
190 to_send[11] = (uint8_t)( amount & 0x00FF );
191 modbus_send( to_send, 12 );
192}
193
194void modbus::modbus_read_holding_registers( int address, int amount, uint16_t *buffer )
195{
196 if( _connected )
197 {
198 if( amount > 65535 || address > 65535 )
199 {
201 }
202 modbus_read( address, amount, READ_REGS );
203 uint8_t to_rec[MAX_MSG_LENGTH];
204 modbus_receive( to_rec );
205 try
206 {
208 for( int i = 0; i < amount; i++ )
209 {
210 buffer[i] = ( (uint16_t)to_rec[9 + 2 * i] ) << 8;
211 buffer[i] += (uint16_t)to_rec[10 + 2 * i];
212 }
213 }
214 catch( std::exception &e )
215 {
216 throw; // e;
217 }
218 }
219 else
220 {
222 }
223}
224
225void modbus::modbus_read_input_registers( int address, int amount, uint16_t *buffer )
226{
227 if( _connected )
228 {
229 if( amount > 65535 || address > 65535 )
230 {
232 }
233 modbus_read( address, amount, READ_INPUT_REGS );
234 uint8_t to_rec[MAX_MSG_LENGTH];
235 modbus_receive( to_rec );
236 try
237 {
239 for( int i = 0; i < amount; i++ )
240 {
241 buffer[i] = ( (uint16_t)to_rec[9 + 2 * i] ) << 8;
242 buffer[i] += (uint16_t)to_rec[10 + 2 * i];
243 }
244 }
245 catch( std::exception &e )
246 {
247 throw; // e;
248 }
249 }
250 else
251 {
253 }
254}
255
256void modbus::modbus_read_coils( int address, int amount, bool *buffer )
257{
258 if( _connected )
259 {
260 if( amount > 2040 || address > 65535 )
261 {
263 }
264 modbus_read( address, amount, READ_COILS );
265 uint8_t to_rec[MAX_MSG_LENGTH];
266 modbus_receive( to_rec );
267 try
268 {
270 for( int i = 0; i < amount; i++ )
271 {
272 buffer[i] = (bool)( ( to_rec[9 + i / 8] >> ( i % 8 ) ) & 1 );
273 }
274 }
275 catch( std::exception &e )
276 {
277 throw; // e;
278 }
279 }
280 else
281 {
283 }
284}
285
286void modbus::modbus_read_input_bits( int address, int amount, bool *buffer )
287{
288 if( _connected )
289 {
290 if( amount > 2040 || address > 65535 )
291 {
293 }
294 modbus_read( address, amount, READ_INPUT_BITS );
295 uint8_t to_rec[MAX_MSG_LENGTH];
296 modbus_receive( to_rec );
297 try
298 {
300 for( int i = 0; i < amount; i++ )
301 {
302 buffer[i] = (bool)( ( to_rec[9 + i / 8] >> ( i % 8 ) ) & 1 );
303 }
304 }
305 catch( std::exception &e )
306 {
307 throw; // e;
308 }
309 }
310 else
311 {
313 }
314}
315
316void modbus::modbus_write_coil( int address, bool to_write )
317{
318 if( _connected )
319 {
320 if( address > 65535 )
321 {
323 }
324 int value = to_write * 0xFF00;
325 modbus_write( address, 1, WRITE_COIL, (uint16_t *)&value );
326 uint8_t to_rec[MAX_MSG_LENGTH];
327 modbus_receive( to_rec );
328 try
329 {
331 }
332 catch( std::exception &e )
333 {
334 throw; // e;
335 }
336 }
337 else
338 {
340 }
341}
342
343void modbus::modbus_write_register( int address, uint16_t value )
344{
345 if( _connected )
346 {
347 if( address > 65535 )
348 {
350 }
351 modbus_write( address, 1, WRITE_REG, &value );
352 uint8_t to_rec[MAX_MSG_LENGTH];
353 modbus_receive( to_rec );
354 try
355 {
357 }
358 catch( std::exception &e )
359 {
360 throw; // e;
361 }
362 }
363 else
364 {
366 }
367}
368
369void modbus::modbus_write_coils( int address, int amount, bool *value )
370{
371 if( _connected )
372 {
373 if( address > 65535 || amount > 65535 )
374 {
376 }
377 uint16_t temp[amount];
378 for( int i = 0; i < 4; i++ )
379 {
380 temp[i] = (uint16_t)value[i];
381 }
382 modbus_write( address, amount, WRITE_COILS, temp );
383 uint8_t to_rec[MAX_MSG_LENGTH];
384 modbus_receive( to_rec );
385 try
386 {
388 }
389 catch( std::exception &e )
390 {
391 throw; // e;
392 }
393 }
394 else
395 {
397 }
398}
399
400void modbus::modbus_write_registers( int address, int amount, uint16_t *value )
401{
402 if( _connected )
403 {
404 if( address > 65535 || amount > 65535 )
405 {
407 }
408 modbus_write( address, amount, WRITE_REGS, value );
409 uint8_t to_rec[MAX_MSG_LENGTH];
410 modbus_receive( to_rec );
411 try
412 {
414 }
415 catch( std::exception &e )
416 {
417 throw; // e;
418 }
419 }
420 else
421 {
423 }
424}
425
426ssize_t modbus::modbus_send( uint8_t *to_send, int length )
427{
428 _msg_id++;
429 errno = 0;
430 ssize_t sent = send( _socket, to_send, (size_t)length, modbusNoSignalFlag );
431
432 if( sent < 0 )
433 {
434 modbusConnectionError( *this, "send" );
435 }
436
437 if( sent != length )
438 {
439 errno = EIO;
440 modbusConnectionError( *this, "send" );
441 }
442
443 return sent;
444}
445
446ssize_t modbus::modbus_receive( uint8_t *buffer )
447{
448 errno = 0;
449 ssize_t received = recv( _socket, (char *)buffer, MAX_MSG_LENGTH, 0 );
450
451 if( received <= 0 )
452 {
453 if( received == 0 )
454 {
455 errno = ECONNRESET;
456 }
457
458 modbusConnectionError( *this, "recv" );
459 }
460
461 return received;
462}
463
464void modbus::modbus_error_handle( uint8_t *msg, int func )
465{
466 if( msg[7] == func + 0x80 )
467 {
468 switch( msg[8] )
469 {
474 case EX_ILLEGAL_VALUE:
478 case EX_ACKNOWLEDGE:
480 case EX_SERVER_BUSY:
485 default:
486 break;
487 }
488 }
489}
Modbus Acknowledge Exception.
Modbus Amount Exception.
Modbus Connect Exception.
Modbus Gate Way Problem Exception.
Modbus Illegal Address Exception.
Modbus Illegal Data Value Exception.
Modbus Illgal Function Exception.
Modbus Server Busy Exception.
Modbus Server Failure Exception.
Modbus Operator Class.
Definition modbus.hpp:53
int _msg_id
Definition modbus.hpp:58
void modbus_read(int address, int amount, int func)
Definition modbus.cpp:184
bool modbus_connect()
Definition modbus.cpp:54
void modbus_read_input_registers(int address, int amount, uint16_t *buffer)
Definition modbus.cpp:225
void modbus_read_input_bits(int address, int amount, bool *buffer)
Definition modbus.cpp:286
void modbus_write_coils(int address, int amount, bool *value)
Definition modbus.cpp:369
~modbus()
Definition modbus.cpp:45
void modbus_read_holding_registers(int address, int amount, uint16_t *buffer)
Definition modbus.cpp:194
bool _connected
Definition modbus.hpp:55
ssize_t modbus_send(uint8_t *to_send, int length)
Definition modbus.cpp:426
void modbus_build_request(uint8_t *to_send, int address, int func)
Definition modbus.cpp:129
modbus(const std::string &host, uint16_t port)
Definition modbus.cpp:37
void modbus_set_slave_id(int id)
Definition modbus.cpp:49
bool modbus_set_timeouts(int seconds, int microseconds=0)
Definition modbus.cpp:105
ssize_t modbus_receive(uint8_t *buffer)
Definition modbus.cpp:446
void modbus_write_coil(int address, bool to_write)
Definition modbus.cpp:316
uint16_t PORT
Definition modbus.hpp:56
void modbus_close()
Definition modbus.cpp:94
struct sockaddr_in _server
Definition modbus.hpp:61
int _socket
Definition modbus.hpp:57
void modbus_write_register(int address, uint16_t value)
Definition modbus.cpp:343
void modbus_error_handle(uint8_t *msg, int func)
Definition modbus.cpp:464
std::string HOST
Definition modbus.hpp:60
void modbus_write(int address, int amount, int func, uint16_t *value)
Definition modbus.cpp:142
void modbus_write_registers(int address, int amount, uint16_t *value)
Definition modbus.cpp:400
void modbus_read_coils(int address, int amount, bool *buffer)
Definition modbus.cpp:256
int _slaveid
Definition modbus.hpp:59
The MagAO-X Modbus TCP client interface.
@ EX_SERVER_FAILURE
Definition modbus.hpp:40
@ EX_GATEWAY_PROBLEMP
Definition modbus.hpp:43
@ EX_ACKNOWLEDGE
Definition modbus.hpp:41
@ EX_ILLEGAL_FUNCTION
Definition modbus.hpp:37
@ EX_GATEWYA_PROBLEMF
Definition modbus.hpp:44
@ EX_SERVER_BUSY
Definition modbus.hpp:42
@ EX_ILLEGAL_VALUE
Definition modbus.hpp:39
@ EX_ILLEGAL_ADDRESS
Definition modbus.hpp:38
#define MAX_MSG_LENGTH
Definition modbus.hpp:19
@ READ_COILS
Definition modbus.hpp:24
@ WRITE_REG
Definition modbus.hpp:29
@ WRITE_COIL
Definition modbus.hpp:28
@ READ_INPUT_BITS
Definition modbus.hpp:25
@ READ_REGS
Definition modbus.hpp:26
@ WRITE_COILS
Definition modbus.hpp:30
@ WRITE_REGS
Definition modbus.hpp:31
@ READ_INPUT_REGS
Definition modbus.hpp:27