iop_modbus.cpp
Go to the documentation of this file.
1/** @file iop_modbus.cpp Process image via modbus/tcp */
2
3/*
4 FAU Discrete Event Systems Library (libfaudes)
5
6 Copyright (C) 2011, 2026 Thomas Moor.
7
8*/
9
10
11// include header
12#include "iop_modbus.h"
13
14// only compile for use with spi configured
15#ifdef FAUDES_IODEVICE_MODBUS
16
17
18
19namespace faudes {
20
21
22
23/*
24 **********************************************
25 **********************************************
26 **********************************************
27
28 implementation: mbDevice
29
30 **********************************************
31 **********************************************
32 **********************************************
33 */
34
35
36// std faudes, incl dummy
37FAUDES_TYPE_IMPLEMENTATION(ModbusDevice,mbDevice,sDevice)
38
39// autoregister (not functional, see xdevice constructor)
41
42//constructor
44 FD_DHV("mbDevice(" << mName << ")::mbDevice()");
45 // have appropriate default label for token io
46 mDefaultLabel = "ModbusDevice";
47 // pointer to internal I/O-image
48 mpImage=0;
49 pOutputImage=0;
50 pInputImage=0;
51 mpOutputMask=0;
52 // modbus/tcp io buffers
53 mMessage= new char[260];
54 // behavioural defaults
55 mMasterRole=true;
56 mSlaveAddress.IpColonPort("localhost:502");
57 mSyncWrite=true;
58}
59
60//deconstructor
62 FD_DHV("mbDevice(" << mName << ")::~mbDevice()");
63 Stop();
64 // must wait for thread to terminate
65 while(Status()!=Down);
66 // free buffers
67 if(mpImage) delete mpImage;
68 if(mpOutputMask) delete mpOutputMask;
69 delete mMessage;
70}
71
72// Clear
73void mbDevice::Clear(void) {
74 // clear base
76 // my configuration
77 mSlaveIoRanges.clear();
78 mSlaveAddress.IpColonPort("localhost:502");
79 mSyncWrite=true;
80}
81
82
83//Compile(void)
85 //setup up internal data structure
86 FD_DHV("mbDevice(" << mName << ")::Compile()");
87 // call base
89 // implement consistency tests here
91 for(unsigned int i=0; i< mSlaveIoRanges.size(); i++) {
92 const IoRange& ior=mSlaveIoRanges.at(i);
93 if(ior.mFdAddress + ior.mCount > mImageSize)
95 if(ior.mCount>2000) {
96 std::stringstream errstr;
97 errstr << "image size must not exceed 2000 bits (image #" << i+1 << ")";
98 throw Exception("mbDevice:Read", errstr.str(), 52);
99 }
100 }
101 mImageSize = ( (mImageSize-1) / 16 ) *16 + 16;
102 if(mImageSize<=0) mImageSize=16;
103 /*
104 if(mImageSize > mMaxBitAddress+1) fail=true;
105 if(fail) {
106 std::stringstream errstr;
107 errstr << "Invalid remote address ranges";
108 throw Exception("mbDevice:Compile()", errstr.str(), 52);
109 }
110 */
111 //
112 if(!mMasterRole) if(mSlaveAddress.Ip()!="localhost") {
113 std::stringstream errstr;
114 errstr << "Slave must have localhost as SlaveAddress";
115 throw Exception("mbDevice:Compile()", errstr.str(), 52);
116 }
117 // (re-)initialize images
118 if(mpImage) delete mpImage;
119 mpImage = new char[mImageSize];
120 memset(mpImage,0,mImageSize);
123 // initialize output mask
124 mpOutputMask = new char[mImageSize];
125 memset(mpOutputMask,0,mImageSize);
126 for(int bit=0; bit< mImageSize; bit++)
127 if(!mOutputLevelIndexMap[bit].Empty())
128 mpOutputMask[bit]=1;
129 // debug
130 //Write();
131}
132
133
134// programmatic config: append remote image
135void mbDevice::AppendRemoteInputs(int mbid, int mbaddr, int cnt, int fdaddr) {
136 IoRange ior;
137 ior.mInputs=true;
138 ior.mMbId=mbid;
139 ior.mMbAddress=mbaddr;
140 ior.mCount=cnt;
141 ior.mFdAddress=fdaddr;
142 mSlaveIoRanges.push_back(ior);
143}
144
145// programmatic config: append remote image
146void mbDevice::AppendRemoteOutputs(int mbid, int mbaddr, int cnt, int fdaddr) {
147 IoRange ior;
148 ior.mInputs=false;
149 ior.mMbId=mbid;
150 ior.mMbAddress=mbaddr;
151 ior.mCount=cnt;
152 ior.mFdAddress=fdaddr;
153 mSlaveIoRanges.push_back(ior);
154}
155
156// programmatic config: slave address
157void mbDevice::SlaveAddress(const std::string& rAddr) {
158 if(mState!=Down) return;
160}
161
162//DoWrite(rTr,rLabel,pContext)
163void mbDevice::DoWritePreface(TokenWriter& rTw, const std::string& rLabel, const Type* pContext) const {
164 FD_DHV("mbDevice("<<mName<<")::DoWritePreface()");
165 //call base
166 sDevice::DoWritePreface(rTw,"",pContext);
167 // role
168 Token stoken;
169 stoken.SetEmpty("Role");
170 if(mMasterRole) {
171 stoken.InsAttributeString("value","master");
172 } else {
173 stoken.InsAttributeString("value","slave");
174 }
175 rTw << stoken;
176 // slave address
177 Token ftoken;
178 ftoken.SetEmpty("SlaveAddress");
180 rTw << ftoken;
181 // ranges
182 rTw.WriteBegin("RemoteImage");
183 for(unsigned int i=0; i<mSlaveIoRanges.size(); i++) {
184 Token rtoken;
185 if(mSlaveIoRanges.at(i).mInputs)
186 rtoken.SetEmpty("Inputs");
187 else
188 rtoken.SetEmpty("Outputs");
189 if(mSlaveIoRanges.at(i).mMbId!=1)
190 rtoken.InsAttributeInteger("mbid",mSlaveIoRanges.at(i).mMbId);
191 rtoken.InsAttributeInteger("mbaddr",mSlaveIoRanges.at(i).mMbAddress);
192 if(mSlaveIoRanges.at(i).mFdAddress!=mSlaveIoRanges.at(i).mMbAddress)
193 rtoken.InsAttributeInteger("fdaddr",mSlaveIoRanges.at(i).mFdAddress);
194 rtoken.InsAttributeInteger("count",mSlaveIoRanges.at(i).mCount);
195 rTw << rtoken;
196 }
197 rTw.WriteEnd("RemoteImage");
198}
199
200
201//DoReadPreface(rTr,rLabel,pContext)
202void mbDevice::DoReadPreface(TokenReader& rTr,const std::string& rLabel, const Type* pContext){
203 //dummy for token-input
204 FD_DHV("mbDevice("<<mName<<")::DoReadPreface()");
205 //call base
206 sDevice::DoReadPreface(rTr,"",pContext);
207 // my global configuration
208 Token token;
209 while(rTr.Peek(token)) {
210 // role
211 if(token.IsBegin("Role")) {
212 rTr.ReadBegin("Role");
213 mMasterRole=true;
214 if(!token.ExistsAttributeString("value")) {
215 std::stringstream errstr;
216 errstr << "Invalid role tag, value attribute missing" << rTr.FileLine();
217 throw Exception("mpiDevice:Read", errstr.str(), 52);
218 }
219 std::string val=token.AttributeStringValue("value");
220 std::transform(val.begin(), val.end(), val.begin(), tolower);
221 if(val=="master") mMasterRole =true;
222 else if(val=="slave") mMasterRole =false;
223 else {
224 std::stringstream errstr;
225 errstr << "Invalid value attribute in role tag" << rTr.FileLine();
226 throw Exception("spiDevice:Read", errstr.str(), 52);
227 }
228 rTr.ReadEnd("Role");
229 continue;
230 }
231 // address
232 if(token.IsBegin("SlaveAddress")) {
233 FD_DHV("mBDevice::DoRead(): found slave address");
234 rTr.ReadBegin("SlaveAddress");
235 if(!token.ExistsAttributeString("value")) {
236 std::stringstream errstr;
237 errstr << "Invalid ip address" << rTr.FileLine();
238 throw Exception("mbDevice:Read", errstr.str(), 52);
239 }
241 rTr.ReadEnd("SlaveAddress");
242 continue;
243 }
244 // process image
245 if(token.IsBegin("RemoteImage")) {
246 rTr.ReadBegin("RemoteImage");
247 while(!rTr.Eos("RemoteImage")) {
248 Token rtoken;
249 rTr.Get(rtoken);
250 if(!rtoken.IsBegin("Inputs"))
251 if(!rtoken.IsBegin("Outputs")) {
252 std::stringstream errstr;
253 errstr << "invalid io range" << rTr.FileLine();
254 throw Exception("mbDevice:Read", errstr.str(), 52);
255 }
256 IoRange iorange;
257 iorange.mMbId=1;
258 iorange.mFdAddress=-1;
259 iorange.mInputs = rtoken.IsBegin("Inputs");
260 if(rtoken.ExistsAttributeInteger("mbid"))
261 iorange.mMbId=rtoken.AttributeIntegerValue("mbid");
262 if(!rtoken.ExistsAttributeInteger("mbaddr")) {
263 std::stringstream errstr;
264 errstr << "missing remote address" << rTr.FileLine();
265 throw Exception("mbDevice:Read", errstr.str(), 52);
266 }
267 iorange.mMbAddress=rtoken.AttributeIntegerValue("mbaddr");
268 if(rtoken.ExistsAttributeInteger("fdaddr"))
269 iorange.mFdAddress=rtoken.AttributeIntegerValue("fdaddr");
270 if(iorange.mFdAddress<0) iorange.mFdAddress=iorange.mMbAddress;
271 iorange.mCount=1;
272 if(rtoken.ExistsAttributeInteger("count"))
273 iorange.mCount=rtoken.AttributeIntegerValue("count");
274 rTr.ReadEnd(rtoken.StringValue());
275 if(iorange.mCount>2000) {
276 std::stringstream errstr;
277 errstr << "image size must not exceed 2000 bits" << rTr.FileLine();
278 throw Exception("mbDevice:Read", errstr.str(), 52);
279 }
280 mSlaveIoRanges.push_back(iorange);
281 }
282 rTr.ReadEnd("RemoteImage");
283 continue;
284 }
285 // unknown: break
286 break;
287 }
288}
289
290
291// Start(void)
292void mbDevice::Start(void) {
293 // bail out
294 if(mState!=Down) return;
295 // initialize modbus/tcp io data
296 mSlaveSocket=-1;
298 // as a slave, we listen for masters to connect
299 if(!mMasterRole) {
300 // clear connected masters
301 mMasterSockets.clear();
302 // open a tcp port to listen: create socket
303 mSlaveSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
304 if(mSlaveSocket<=0) {
305 std::stringstream errstr;
306 errstr << "Modbus fatal network error (cannot open server socket)";
307 throw Exception("mbDevice::Start", errstr.str(), 553);
308 }
309 int reuse=1;
310 faudes_setsockopt(mSlaveSocket,SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
311 // open a tcp port to listen: set up address
312 struct sockaddr_in slaveaddress;
313 memset(&slaveaddress, 0, sizeof(slaveaddress));
314 slaveaddress.sin_family = AF_INET;
315 slaveaddress.sin_addr.s_addr = htonl(INADDR_ANY);
316 slaveaddress.sin_port = htons(mSlaveAddress.Port());
317 // open a tcp port to listen: bind socket to address
318 if(bind(mSlaveSocket, (struct sockaddr *) &slaveaddress,sizeof(slaveaddress)) <0) {
319 std::stringstream errstr;
320 errstr << "Modbus fatal network error (cannot bind socket)";
321 throw Exception("nDevice::Start", errstr.str(), 553);
322 }
323 // open a tcp port to listen: start to listen
324 if(listen(mSlaveSocket, 77) < 0) { // todo: max pending connections
325 std::stringstream errstr;
326 errstr << "Simplenet fatal network error (cannot listen from socket)";
327 throw Exception("mbDevice::Start", errstr.str(), 553);
328 }
329 }
330 // call base (incl. reset)
332 // pessimistic master: let background thread figure presence of the slave
334}
335
336// Stop()
337void mbDevice::Stop(void) {
338 //close serial interface
339 if(mState != Up && mState != StartUp) return;
340 FD_DHV("mbDevice(" << mName << ")::Stop()");
341 // call base (this will join the thread)
343 // close connections to masters (effective only when we are slave)
344 for(unsigned int i=0; i<mMasterSockets.size(); i++) {
345 if(mMasterSockets.at(i)>0) {
346 FD_DH("mbDevice::Stop(): closing master socket #" << mMasterSockets.at(i));
347 faudes_closesocket(mMasterSockets.at(i));
348 }
349 }
350 mMasterSockets.clear();
351 // close my socket
352 if(mSlaveSocket>0) {
353 FD_DH("mbDevice::Stop(): closing slave socket");
354 faudes_closesocket(mSlaveSocket);
355 }
356 mSlaveSocket=-1;
357 // done
358 FD_DH("mbDevice::Stop(): done");
359}
360
361
362// modbus access macros
363#define MB_PDUOFF 7
364#define MB_SETINT(p,v) { mMessage[p] = ((v)>>8); mMessage[p+1] = ((v) & 0xff); }
365#define MB_GETINT(p) ( ( mMessage[p] << 8) + ((int) mMessage[p+1]) )
366#define MB_SETBYTE(p,v) { mMessage[p] = (v);}
367#define MB_GETBYTE(p) ( mMessage[p] )
368
369
370
371// helper: flush buffers
373// flush recv buffer
374 while(1) {
375 struct timeval tv;
376 tv.tv_sec = 0;
377 tv.tv_usec = 0;
378 fd_set mysocks;
379 FD_ZERO(&mysocks);
380 FD_SET(mSlaveSocket, &mysocks);
381 int avail= select(mSlaveSocket+1, &mysocks, NULL, NULL, &tv);
382 if(avail<=0) break;
383 if(!FD_ISSET(mSlaveSocket,&mysocks)) break;
384 FD_DH("mbDevice::MbFlushBuffers(): flush recv buffer");
385 char data;
386 int rc = recv(mSlaveSocket, &data, 1, 0);
387 if(rc==1) continue;
388 FD_DH("mbDevice::MbFlushBuffers(): flush recv buffer: fatal error?");
389 return -1;
390 }
391 FD_DHV("mbDevice::MbFlushBuffers(): ok");
392 return 0;
393}
394
395// helper: send modbus request
397 // flush buffers
398 if(MbFlushBuffers()!=0) return -1;
399 // setup mbab header
402 MB_SETINT(2,0);
404 MB_SETBYTE(6,id);
405 // sync send
406 int from=0;
407 int left=mMessageLen+MB_PDUOFF;
408 while(left>0) {
409 int rc=send(mSlaveSocket, mMessage+from, left, 0);
410 if(rc<0) return rc;
411 left-=rc;
412 from+=rc;
413 }
414 return 0;
415}
416
417
418// helper: receive modbus response
420 // prepare relevant source
421 fd_set mysocks;
422 int mysocks_max=0;
423 FD_ZERO(&mysocks);
424 FD_SET(mSlaveSocket, &mysocks);
425 if(mysocks_max< mSlaveSocket) mysocks_max=mSlaveSocket;
426 // set moderate timeout
427 struct timeval tv;
428 tv.tv_sec = 0;
429 tv.tv_usec = 500000;
430 // wait for message
431 int avail=select(mysocks_max+1, &mysocks, NULL, NULL, &tv);
432 // read availabe data (expect only one packet)
433 mMessageLen=0;
434 if(avail<=0) return -1;
435 if(!FD_ISSET(mSlaveSocket,&mysocks)) return -1;
436 mMessageLen = recv(mSlaveSocket, mMessage, 260, 0);
437 if(mMessageLen<7) return -1;
438 int mbablen = MB_GETINT(4);
439 if(mbablen+6 != mMessageLen) {
440 FD_DH("mbDevice::MbReceiveResponse(): invalid MBAB header (size mismatch)");
441 return -1;
442 }
443 return 0;
444}
445
446
447// helper: receive request
448int mbDevice::MbReceiveRequest(int mastersock) {
449 // read availabe data (expect only one packet)
450 mMessageLen = recv(mastersock, mMessage, 260, 0);
451 // test Modbus compliance
452 if(mMessageLen<7) return -1; // perhaps connection closed
453 int mbablen = MB_GETINT(4);
454 if(mbablen+6 != mMessageLen) {
455 FD_DH("mbDevice::MbReceiveRequest(): invalid MBAB header (size mismatch)");
456 return -1;
457 }
458 // set net length
460 return 0;
461}
462
463// helper: send modbus request
464int mbDevice::MbSendResponse(int mastersock) {
465 // update mbab header
467 // sync send
468 int from=0;
469 int left=mMessageLen+MB_PDUOFF;
470 while(left>0) {
471 int rc=send(mastersock, mMessage+from, left, 0);
472 if(rc<0) return rc;
473 left-=rc;
474 from+=rc;
475 }
476 return 0;
477}
478
479// loopcall-back for serial comminucation
481
482 // master role: try to connect to remote slave
483 if((mMasterRole) && (mState==StartUp) && (mSlaveSocket<0)) {
484
485 // dont congest network / dont mess up console
486 static int ctimer=0;
487 ctimer+=CycleTime();
488 if(ctimer< 1000000) return;
489 ctimer=0;
490 // report
491 FD_DH("mbDevice::LoopCallBack(): connecting to remote slave " << mSlaveAddress.IpColonPort());
492 // open a tcp port: create socket
493 int slavesock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
494 if(slavesock<=0) {
495 FD_DH("mbDevice::LoopCallBack(): connection to slave failed: cannot create socket");
496 return;
497 }
498 // open a tcp port: set up internet address
499 unsigned long int slaveinaddr = INADDR_NONE;
500 if(slaveinaddr==INADDR_NONE) {
501 FD_DHV("mbDevice::LoopCallBack(): connecting to remote slave: using provided address literaly");
502 slaveinaddr = inet_addr(mSlaveAddress.Ip().c_str());
503 }
504 if(slaveinaddr==INADDR_NONE) {
505 struct hostent *host;
506 host = gethostbyname(mSlaveAddress.Ip().c_str());
507 if(host!=0) {
508 FD_DHV("mbDevice::LooCallBack(): connecting to remote slave: retrieve address by name-lookup");
509 slaveinaddr = *(unsigned long int*) host->h_addr;
510 }
511 }
512 if(slaveinaddr==INADDR_NONE) {
513 FD_DH("mbDevice::DoLooCallBack():: connection to slave failed: no valid address " << mSlaveAddress.Ip());
514 faudes_closesocket(slavesock);
515 return;
516 }
517 // open a tcp port: set up socket address
518 struct sockaddr_in slaveaddress;
519 memset(&slaveaddress, 0, sizeof(slaveaddress));
520 slaveaddress.sin_family = AF_INET;
521 slaveaddress.sin_addr.s_addr=slaveinaddr;
522 slaveaddress.sin_port = htons(mSlaveAddress.Port());
523 // make my socket nonblocking
524 int rc = faudes_setsocket_nonblocking(slavesock, true);
525 if(rc<0) {
526 FD_DH("mbDevice::LoopCallBack():: connection to slave failed: socker error A1");
527 faudes_closesocket(slavesock);
528 return;
529 }
530 // try to connect
531 int rcc= connect(slavesock, (struct sockaddr*) &slaveaddress, sizeof(slaveaddress));
532 // wait for host to accept
533#ifdef FAUDES_POSIX
534 if(rcc<0) {
535 if(errno!=EINPROGRESS) {
536 FD_DH("mbDevice::LoopCallBack(): connection to slave failed: system errno " << errno);
537 } else {
538 FD_DH("mbDevice::LoopCallBack(): connecting to slave: wait for slave to accept");
539 // sense success via select befor timeout
540 struct timeval tv;
541 tv.tv_sec = 0;
542 tv.tv_usec = 500000;
543 fd_set mysocks;
544 FD_ZERO(&mysocks);
545 FD_SET(slavesock, &mysocks);
546 rcc= select(slavesock+1, NULL, &mysocks, NULL, &tv);
547 rcc--; // map 1 to no err aka 0 ;-)
548 if(rcc<0) FD_DH("mbDevice::LoopCallBack(): connection to slave failed: timeout");
549 }
550 }
551#endif
552#ifdef FAUDES_WINDOWS
553 if(rcc<0) {
554 int lerr = WSAGetLastError();
555 if(lerr!=WSAEWOULDBLOCK) {
556 FD_DH("mbDevice::LoopCallBack(): connection to slave failed: system errno " << lerr);
557 } else {
558 FD_DH("mbDevice::LoopCallBack(): connecting to slave: wait for slave to accept");
559 // sense success via select befor timeout
560 struct timeval tv;
561 tv.tv_sec = 0;
562 tv.tv_usec = 500000;
563 fd_set mysocks;
564 FD_ZERO(&mysocks);
565 FD_SET(slavesock, &mysocks);
566 rcc= select(slavesock+1, NULL, &mysocks, NULL, &tv);
567 rcc--; // map 1 to no err aka 0 ;-)
568 if(rcc<0) FD_DH("mbDevice::LoopCallBack(): connection to slave failed: timeout");
569 }
570 }
571#endif
572 // connection failed
573 if(rcc<0) {
574 FD_DH("mbDevice::LoopCallBack(): connection to slave failed: slave unreachable");
575 faudes_closesocket(slavesock);
576 return;
577 }
578 // sense errors on socket level
579 if(faudes_getsocket_error(slavesock)<0) {
580 FD_DH("mbDevice::LoopCallBack(): connection to slave failed: socket error A2");
581 faudes_closesocket(slavesock);
582 return;
583 }
584 // restore blocking socket
585 rc = faudes_setsocket_nonblocking(slavesock, false);
586 if(rc<0) {
587 FD_DH("mbDevice::LoopCallBack():: connection to slave failed: socket error A3");
588 faudes_closesocket(slavesock);
589 return;
590 }
591 // record success
592 FD_DH("mbDevice::LoopCallBack(): connected to remote slave: using socket #" << slavesock);
593 mSlaveSocket=slavesock;
594 }
595
596
597 // slave role: accept remote master connection
598 if((!mMasterRole) && (mState==Up)) {
599
600 // sense connection requests
601 struct timeval tv;
602 tv.tv_sec = 0;
603 tv.tv_usec = 0;
604 fd_set mysocks;
605 FD_ZERO(&mysocks);
606 FD_SET(mSlaveSocket,&mysocks);
607 int avail = select(mSlaveSocket+1, &mysocks, NULL, NULL, &tv);
608
609 // accept
610 if(avail==1)
611 if(FD_ISSET(mSlaveSocket,&mysocks)) {
612 FD_DH("mbDevice::LoopCallBack(): accepting remote master to connect");
613 struct sockaddr_in masteraddr;
614 socklen_t masteraddr_len = sizeof(masteraddr);
615 int mastersock=accept(mSlaveSocket, (struct sockaddr *) &masteraddr, &masteraddr_len );
616 if(mastersock<0) {
617 FD_DH("mbDevice::LoopCallback(): failed to accept incomming connection");
618 } else {
619 mMasterSockets.push_back(mastersock);
620 }
621 }
622
623 }
624
625
626 // master role: sync image with remote slave
627 if((mMasterRole) && (mSlaveSocket>0) && (mState!=Down) && (mState!=ShutDown) ) {
628 FD_DHV("mbDevice::DoLooCallBack(): update image from remote slave");
629 // read all inputs
630 for(unsigned int i=0; i<mSlaveIoRanges.size(); i++) {
631 const IoRange& ior=mSlaveIoRanges.at(i);
632 if(!ior.mInputs) continue;
633 // assemble request
634 MB_SETBYTE(MB_PDUOFF,0x02); // read multiple digital inputs
636 MB_SETINT(MB_PDUOFF+3,ior.mCount);
637 mMessageLen=5;
638 // send request
639 FD_DHV("mbDevice::LoopCallBack(): sending read request");
640 if(MbSendRequest(ior.mMbId)!=0) {
641 FD_DH("mbDevice::LoopCallBack(): sending read request to slave: failed");
643 faudes_closesocket(mSlaveSocket);
644 mSlaveSocket=-1;
645 return;
646 };
647 FD_DHV("mbDevice::LoopCallBack(): read response");
648 if(MbReceiveResponse()!=0) {
649 FD_DH("mbDevice::LoopCallBack(): reading reply to read request from slave: failed");
651 faudes_closesocket(mSlaveSocket);
652 mSlaveSocket=-1;
653 return;
654 };
655 FD_DHV("mbDevice::LoopCallBack(): received responde #" << mMessageLen);
656 // interpret response
657 if(MB_GETBYTE(MB_PDUOFF)==0x02) { // no error
658 FD_DHV("mbDevice::LoopCallBack(): input image received");
659 int count=MB_GETBYTE(MB_PDUOFF+1); // todo: verify
660 count=ior.mCount;
661 int src=MB_PDUOFF+2;
662 int data=MB_GETBYTE(src);
663 int addr=ior.mFdAddress;
664 int shft=0x01;
665 while(count) {
666 if(!mpOutputMask[addr]) mpImage[addr]= (( data & shft) != 0);
667 addr++; count--; shft = shft <<1;
668 if(shft==0x100) { shft=0x01; data=MB_GETBYTE(++src);};
669 }
670 } else {
671 FD_DH("mbDevice::LoopCallBack(): received error when reading inputs");
672 }
673 }
674 // write all outputs
675 for(unsigned int i=0; i<mSlaveIoRanges.size(); i++) {
676 const IoRange& ior=mSlaveIoRanges.at(i);
677 if(ior.mInputs) continue;
678 // assemble request
679 MB_SETBYTE(MB_PDUOFF,0x0f); // write multiple coils
681 MB_SETINT(MB_PDUOFF+3,ior.mCount);
682 int bcount= (ior.mCount-1)/8 +1;
683 MB_SETBYTE(MB_PDUOFF+5,bcount);
684 int dst=MB_PDUOFF+6;
685 int data=0x00;
686 int shft=0x01;
687 int addr=ior.mFdAddress;
688 int count=ior.mCount;
689 while(count) {
690 if(mpImage[addr]) data |= shft;
691 addr++; count--; shft = shft <<1;
692 if(shft==0x100) { shft=0x01; MB_SETBYTE(dst,data); data=0x00; dst++;}
693 }
694 if(shft!=0x01) { MB_SETBYTE(dst,data);};
695 mMessageLen=6+bcount;
696 // send request
697 FD_DHV("mbDevice::LoopCallBack(): sending write request");
698 if(MbSendRequest(ior.mMbId)!=0) {
699 FD_DHV("mbDevice::LoopCallBack(): sending write failed");
701 faudes_closesocket(mSlaveSocket);
702 mSlaveSocket=-1;
703 return;
704 };
705 if(MbReceiveResponse()!=0) {
706 FD_DH("mbDevice::LoopCallBack(): reading reply to write request from slave: failed");
708 faudes_closesocket(mSlaveSocket);
709 mSlaveSocket=-1; return;
710 };
711 if(MB_GETBYTE(MB_PDUOFF)!=0x0f) {
712 FD_DH("mbDevice::LoopCallBack(): received error response on write request");
713 }
714 }
715 }
716
717
718 // slave role: sync image with remote masters
719
720 if((!mMasterRole) && (mState==Up)) {
721
722 // prepare relevant sources
723 fd_set mysocks;
724 int mysocks_max=0;
725 FD_ZERO(&mysocks);
726 for(unsigned int i=0; i< mMasterSockets.size(); i++) {
727 int mastersock=mMasterSockets.at(i);
728 if(mastersock<0) continue;
729 FD_SET(mastersock, &mysocks);
730 if(mysocks_max< mastersock) mysocks_max=mastersock;
731 }
732
733 // sense requests
734 struct timeval tv;
735 tv.tv_sec = 0;
736 tv.tv_usec = 0;
737 int avail=select(mysocks_max+1, &mysocks, NULL, NULL, &tv);
738
739 // loop master requests
740 if(avail>0)
741 for(unsigned int i=0; i< mMasterSockets.size(); i++) {
742 int mastersock=mMasterSockets.at(i);
743 if(mastersock<0) continue;
744 if(!FD_ISSET(mastersock, &mysocks)) continue;
745 FD_DHV("mbDevice::LoopCallback(): received message on sock " << mastersock);
746 if(MbReceiveRequest(mastersock)<0) {
747 FD_DH("mbDevice::LoopCallback(): receive error on sock " << mastersock);
748 faudes_closesocket(mastersock);
749 mMasterSockets.at(i)=-1; // todo: remove
750 continue;
751 }
752
753 // interpret request
754 int fnct=MB_GETBYTE(MB_PDUOFF);
755 int errcode = 0x01;
756 // read inputs or coils
757 if((fnct==0x01) || (fnct==0x02)) {
758 FD_DHV("mbDevice::LoopCallback(): coil-read or input read request");
759 int addr = MB_GETINT(MB_PDUOFF+1);
760 int count = MB_GETINT(MB_PDUOFF+3);
761 int bcount= ((count-1)/8+1);
762 FD_DHV("mbDevice::LoopCallback(): address range: @" << addr << " #" << count);
763 // test validity
764 errcode=0x00;
765 if(addr+count>mImageSize) errcode=0x02;
766 if(count>2000) errcode=0x02;
767 // perform
768 if(errcode==0x00) {
769 // fill in bits
770 int dst=MB_PDUOFF+2;
771 int data=0x00;
772 int shft=0x01;
773 while(count) {
774 if(mpImage[addr]) data |= shft;
775 addr++; count--; shft = shft <<1;
776 if(shft==0x100) { shft=0x01; MB_SETBYTE(dst,data); dst++; data=0x00;}
777 }
778 if(shft!=0x01) { MB_SETBYTE(dst++,data);};
779 MB_SETBYTE(MB_PDUOFF+1,bcount);
780 // set nessage length
781 mMessageLen=bcount+2;
782 }
783 }
784 // read input registers or holding registers
785 if((fnct==0x03) || (fnct==0x04)) {
786 FD_DHV("mbDevice::LoopCallback(): register or holding register read request");
787 int addr = MB_GETINT(MB_PDUOFF+1);
788 int count = MB_GETINT(MB_PDUOFF+3);
789 FD_DHV("mbDevice::LoopCallback(): address range: @" << addr << " #" << count);
790 // test validity
791 errcode=0x00;
792 if(16*addr+16*count>mImageSize)
793 errcode=0x02;
794 // perform
795 if(errcode==0x00) {
796 // set header length
797 mMessageLen=2*count+2;
798 MB_SETBYTE(MB_PDUOFF+1,2*count);
799 // fill in bits
800 int src= addr*16;
801 int dst=MB_PDUOFF+2;
802 for(;count>0; count--) {
803 int shft=0x01;
804 int lbyte=0x00;
805 for(;src<mImageSize && shft!=0x100; src++, shft = shft << 1)
806 if(mpImage[src]) lbyte |= shft;
807 shft=0x01;
808 int hbyte=0x00;
809 for(;src<mImageSize && shft!=0x100; src++, shft = shft << 1)
810 if(mpImage[src]) hbyte |= shft;
811 MB_SETBYTE(dst,hbyte); dst++;
812 MB_SETBYTE(dst,lbyte); dst++;
813 }
814 }
815 }
816 // write single coil
817 if(fnct==0x05) {
818 FD_DHV("mbDevice::LoopCallback(): write single coil request");
819 int addr = MB_GETINT(MB_PDUOFF+1);
820 bool val = ( ((unsigned char) MB_GETBYTE(MB_PDUOFF+3))==0xff);
821 FD_DHV("mbDevice::LoopCallback(): write single coil request: " << addr << " to " << val);
822 // test
823 errcode=0x00;
824 if(addr>=mImageSize) errcode=0x02;
825 // perform
826 if(errcode==0x00) {
827 if(mpOutputMask[addr]) mpImage[addr] = val;
828 mMessageLen=5;
829 }
830 }
831 // write single register
832 if(fnct==0x06) {
833 FD_DHV("mbDevice::LoopCallback(): write holding register request");
834 int addr = MB_GETINT(MB_PDUOFF+1);
835 int val = MB_GETINT(MB_PDUOFF+3);
836 FD_DHV("mbDevice::LoopCallback(): set @" << addr << " to " << val);
837 // test validity
838 errcode=0x00;
839 if(16*addr+16 >mImageSize)
840 errcode=0x02;
841 // perform
842 if(errcode==0x00) {
843 // extract bits
844 int dst=16*addr;
845 int hbyte= (val >> 8); // :-)
846 int lbyte= (val & 0xff);
847 int shft;
848 for(shft=0x01; shft!=0x100; shft = shft << 1, dst++)
849 mpImage[dst] = (( lbyte & shft) != 0);
850 for(shft=0x01; shft!=0x100; shft = shft << 1, dst++)
851 mpImage[dst] = (( hbyte & shft) != 0);
852 // setup reply
853 mMessageLen=5;
854 }
855 }
856 // write multiple coils
857 if(fnct==0x0f) {
858 FD_DHV("mbDevice::LoopCallback(): write multiple coils request");
859 int addr = MB_GETINT(MB_PDUOFF+1);
860 int count = MB_GETINT(MB_PDUOFF+3);
861 int bcount= MB_GETBYTE(MB_PDUOFF+5);
862 FD_DHV("mbDevice::LoopCallback(): address range: @" << addr << " #" << count << "(" << bcount << ")");
863 // test validity
864 errcode=0x00;
865 if(addr+count>mImageSize) errcode=0x02;
866 if( (bcount < ((count-1)/8+1)) || (mMessageLen < 6+bcount) ) errcode=0x03;
867 // perform
868 if(errcode==0x00) {
869 // extract bits
870 int src=MB_PDUOFF+6;
871 int data=0;
872 int shft=0x100;
873 while(count) {
874 if(shft==0x100) { shft=0x01; data=MB_GETBYTE(src);src++;};
875 if(!mpOutputMask[addr]) mpImage[addr]= (( data & shft) != 0);
876 addr++; count--; shft = shft <<1;
877 }
878 // setup reply
879 mMessageLen=5;
880 }
881 }
882 // write multiple holding registers
883 if(fnct==0x10) {
884 FD_DHV("mbDevice::LoopCallback(): write multiple holding registers request");
885 int addr = MB_GETINT(MB_PDUOFF+1);
886 int count = MB_GETINT(MB_PDUOFF+3);
887 int bcount= MB_GETBYTE(MB_PDUOFF+5);
888 FD_DHV("mbDevice::LoopCallback(): address range: @" << addr << " #" << count << "(" << bcount << ")");
889 // test validity
890 errcode=0x00;
891 if(16*addr+16*count>mImageSize)
892 errcode=0x02;
893 if( bcount != 2* count)
894 errcode=0x03;
895 // perform
896 if(errcode==0x00) {
897 // extract bits
898 int src=MB_PDUOFF+6;
899 int dst=16*addr;
900 for(;count>0;count--) {
901 int hbyte=MB_GETBYTE(src); src++;
902 int lbyte=MB_GETBYTE(src); src++;
903 int shft;
904 for(shft=0x01; shft!=0x100; shft = shft << 1, dst++)
905 mpImage[dst] = (( lbyte & shft) != 0);
906 for(shft=0x01; shft!=0x100; shft = shft << 1, dst++)
907 mpImage[dst] = (( hbyte & shft) != 0);
908 }
909 // setup reply
910 mMessageLen=5;
911 }
912 }
913 // send reply
914 if(errcode==0x00) {
915 FD_DHV("mbDevice::LoopCallback(): sending reply #" << mMessageLen);
916 MbSendResponse(mastersock);
917 }
918 // send error
919 if(errcode!=0x00) {
920 FD_DH("mbDevice::LoopCallback(): sending error reply, code " << errcode);
921 MB_SETBYTE(MB_PDUOFF, fnct | 0x80);
922 MB_SETBYTE(MB_PDUOFF+1, errcode);
923 mMessageLen=2;
924 MbSendResponse(mastersock);
925 }
926
927
928 } // end: slave role loops all clients for requests
929 } // end: slave role receiving requests
930
931}
932
933
934// DoReadSignalsPre(void)
936 return mSlaveSocket!=-1;
937}
938
939
940// DoReadSignalsPost(void)
943
944
945//ReadSignal(int)
947 return pInputImage[bit];
948}
949
950
951// DoWriteSignalsPre(void)
953 return mSlaveSocket!=-1;
954}
955
956// DoWrtieSignalsPost(void)
959
960
961//DoWriteSignal(int,int)
962void mbDevice::DoWriteSignal(int bit, bool value){
963
964 // Write one actuator value, adressed by bit number (0 to 63);
965 //FD_DHV("mbDevice("<<mName<<")::DoWriteSignal(" << bit << ", " << value <<")");
966
967 pOutputImage[bit]=value;
968
969}
970
971
972} // namespace
973
974
975
976#endif // end modbus support
#define FAUDES_TYPE_IMPLEMENTATION(ftype, ctype, cbase)
Definition cfl_types.h:1017
std::string IpColonPort(void) const
std::string Ip(void) const
std::string FileLine(void) const
bool Eos(const std::string &rLabel)
void ReadEnd(const std::string &rLabel)
void ReadBegin(const std::string &rLabel)
bool Get(Token &token)
bool Peek(Token &token)
void WriteEnd(const std::string &rLabel)
void WriteBegin(const std::string &rLabel)
const std::string & StringValue(void) const
Int AttributeIntegerValue(const std::string &name)
bool ExistsAttributeString(const std::string &name)
bool IsBegin(void) const
void SetEmpty(const std::string &rName)
bool ExistsAttributeInteger(const std::string &name)
void InsAttributeInteger(const std::string &name, Int value)
void InsAttributeString(const std::string &name, const std::string &value)
const std::string & AttributeStringValue(const std::string &name)
virtual ~mbDevice(void)
void DoReadPreface(TokenReader &rTr, const std::string &rLabel="", const Type *pContext=0)
virtual bool DoReadSignal(int bitaddr)
int MbSendResponse(int mastersock)
int MbReceiveRequest(int mastersock)
virtual void DoReadSignalsPost(void)
void SlaveAddress(const std::string &rAddr)
virtual void Compile(void)
virtual void Stop(void)
virtual void Start(void)
int MbReceiveResponse(void)
int MbFlushBuffers(void)
virtual bool DoWriteSignalsPre(void)
void DoWritePreface(TokenWriter &rTw, const std::string &rLabel, const Type *pContext) const
std::vector< IoRange > mSlaveIoRanges
Definition iop_modbus.h:283
virtual void Clear(void)
virtual void DoLoopCallback(void)
virtual void DoWriteSignal(int bitaddr, bool value)
virtual bool DoReadSignalsPre(void)
virtual void DoWriteSignalsPost(void)
int MbSendRequest(int id)
std::vector< int > mMasterSockets
Definition iop_modbus.h:300
void AppendRemoteInputs(int mbid, int mbaddr, int cnt, int fdaddr)
SimplenetAddress mSlaveAddress
Definition iop_modbus.h:273
void AppendRemoteOutputs(int mbid, int mbaddr, int cnt, int fdaddr)
virtual int CycleTime() const
virtual void Stop(void)
virtual void DoReadPreface(TokenReader &rTr, const std::string &rLabel="", const Type *pContext=0)
virtual void Clear(void)
void DoWritePreface(TokenWriter &rTw, const std::string &rLabel, const Type *pContext=0) const
virtual void Start(void)
std::map< int, EventSet > mOutputLevelIndexMap
virtual void Compile(void)
virtual DeviceState Status(void)
std::string mName
DeviceState mState
#define MB_SETINT(p, v)
#define MB_SETBYTE(p, v)
#define MB_PDUOFF
#define MB_GETBYTE(p)
#define MB_GETINT(p)
#define FD_DHV(message)
Definition iop_vdevice.h:37
#define FD_DH(message)
Definition iop_vdevice.h:27
AutoRegisterType< mbDevice > gRtiRegisterSpiDevice("ModbusDevice")

libFAUDES 2.34e --- 2026.03.16 --- c++ api documentaion by doxygen