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, 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
484 if((mMasterRole) && (mState==StartUp) && (mSlaveSocket<0)) {
485
486 // dont congest network / dont mess up console
487 static int ctimer=0;
488 ctimer+=CycleTime();
489 if(ctimer< 1000000) return;
490 ctimer=0;
491 // report
492 FD_DH("mbDevice::DoLoopCallBack(): connecting to remote slave " << mSlaveAddress.IpColonPort());
493 // open a tcp port: create socket
494 int slavesock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
495 if(slavesock<=0) {
496 FD_DH("mbDevice::DoLoopCallBack(): connection to slave failed: internal err A0");
497 return;
498 }
499 // open a tcp port: set up internet address
500 unsigned long int slaveinaddr = INADDR_NONE;
501 if(slaveinaddr==INADDR_NONE) {
502 FD_DHV("mbDevice::DoLoopCallBack(): using provided address literaly");
503 slaveinaddr = inet_addr(mSlaveAddress.Ip().c_str());
504 }
505 if(slaveinaddr==INADDR_NONE) {
506 struct hostent *host;
507 host = gethostbyname(mSlaveAddress.Ip().c_str());
508 if(host!=0) {
509 FD_DHV("mbDevice::DoLooCallBack(): retrieve alternative address by name-lookup");
510 slaveinaddr = *(unsigned long int*) host->h_addr;
511 }
512 }
513 if(slaveinaddr==INADDR_NONE) {
514 FD_DH("mbDevice::DoLooCallBack():: connection to slave failed: invalid address " << mSlaveAddress.Ip());
515 faudes_closesocket(slavesock);
516 return;
517 }
518 // open a tcp port: set up socket address
519 struct sockaddr_in slaveaddress;
520 memset(&slaveaddress, 0, sizeof(slaveaddress));
521 slaveaddress.sin_family = AF_INET;
522 slaveaddress.sin_addr.s_addr=slaveinaddr;
523 slaveaddress.sin_port = htons(mSlaveAddress.Port());
524 // make my socket nonblocking
525 int rc = faudes_setsocket_nonblocking(slavesock, true);
526 if(rc<0) {
527 FD_DH("mbDevice::DoLoopCallBack():: connection to slave failed: internal error A1");
528 faudes_closesocket(slavesock);
529 return;
530 }
531 // try to connect
532 int rcc= connect(slavesock, (struct sockaddr*) &slaveaddress, sizeof(slaveaddress));
533 // wait for host to accept
534#ifdef FAUDES_POSIX
535 if(rcc<0) {
536 if(errno!=EINPROGRESS) {
537 FD_DH("mbDevice::DoLoopCallBack(): connection to slave failed: connect() errno " << errno);
538 } else {
539 FD_DH("mbDevice::DoLoopCallBack(): connection to slave: wait for host to accept");
540 // sense success via select befor timeout
541 struct timeval tv;
542 tv.tv_sec = 0;
543 tv.tv_usec = 500000;
544 fd_set mysocks;
545 FD_ZERO(&mysocks);
546 FD_SET(slavesock, &mysocks);
547 rcc= select(slavesock+1, NULL, &mysocks, NULL, &tv);
548 rcc--; // map 1 to no err aka 0 ;-)
549 if(rcc<0) FD_DH("mbDevice::DoLoopCallBack(): connection to slave failed: timeout");
550 }
551 }
552#endif
553#ifdef FAUDES_WINDOWS
554 if(rcc<0) {
555 int lerr = WSAGetLastError();
556 if(lerr!=WSAEWOULDBLOCK) {
557 FD_DH("mbDevice::DoLoopCallBack(): connection to slave failed: connect() errno " << lerr);
558 } else {
559 FD_DH("mbDevice::DoLoopCallBack(): wait for host to accept");
560 // sense success via select befor timeout
561 struct timeval tv;
562 tv.tv_sec = 0;
563 tv.tv_usec = 500000;
564 fd_set mysocks;
565 FD_ZERO(&mysocks);
566 FD_SET(slavesock, &mysocks);
567 rcc= select(slavesock+1, NULL, &mysocks, NULL, &tv);
568 rcc--; // map 1 to no err aka 0 ;-)
569 if(rcc<0) FD_DH("mbDevice::DoLoopCallBack(): connection to slave failed: timeout");
570 }
571 }
572#endif
573 // connection failed
574 if(rcc<0) {
575 FD_DH("mbDevice::DoLoopCallBack():: connection to slave failed: host unreachable");
576 faudes_closesocket(slavesock);
577 return;
578 }
579 // sense errors on socket level
580 if(faudes_getsocket_error(slavesock)<0) {
581 FD_DH("mbDevice::DoLoopCallBack():: connection to slave failed: internal error A2");
582 faudes_closesocket(slavesock);
583 return;
584 }
585 // restore blocking socket
586 rc = faudes_setsocket_nonblocking(slavesock, false);
587 if(rc<0) {
588 FD_DH("mbDevice::DoLoopCallBack():: connection to slave failed: internal error A3");
589 faudes_closesocket(slavesock);
590 return;
591 }
592 // record success
593 FD_DH("mbDevice::DoLoopCallBack(): connected to remote slave, using socket #" << slavesock);
594 mSlaveSocket=slavesock;
595 }
596
597
598 // slave role: accept remote master connection
599
600 if((!mMasterRole) && (mState==Up)) {
601
602 // sense connection requests
603 struct timeval tv;
604 tv.tv_sec = 0;
605 tv.tv_usec = 0;
606 fd_set mysocks;
607 FD_ZERO(&mysocks);
608 FD_SET(mSlaveSocket,&mysocks);
609 int avail = select(mSlaveSocket+1, &mysocks, NULL, NULL, &tv);
610
611 // accept
612 if(avail==1)
613 if(FD_ISSET(mSlaveSocket,&mysocks)) {
614 FD_DH("mbDevice::DoLoopCallBack(): accepting remote master to connect");
615 struct sockaddr_in masteraddr;
616 socklen_t masteraddr_len = sizeof(masteraddr);
617 int mastersock=accept(mSlaveSocket, (struct sockaddr *) &masteraddr, &masteraddr_len );
618 if(mastersock<0) {
619 FD_DH("mbDevice::DoLoopCallback(): failed to accept incomming connection");
620 } else {
621 mMasterSockets.push_back(mastersock);
622 }
623 }
624
625 }
626
627
628 // master role: sync image with remote slave
629
630 if((mMasterRole) && (mSlaveSocket>0) && (mState!=Down) && (mState!=ShutDown) ) {
631 FD_DHV("mbDevice::DoLooCallBack(): update image from remote slave");
632 // read all inputs
633 for(unsigned int i=0; i<mSlaveIoRanges.size(); i++) {
634 const IoRange& ior=mSlaveIoRanges.at(i);
635 if(!ior.mInputs) continue;
636 // assemble request<
637 MB_SETBYTE(MB_PDUOFF,0x02); // read multiple digital inputs
639 MB_SETINT(MB_PDUOFF+3,ior.mCount);
640 mMessageLen=5;
641 // send request
642 FD_DHV("mbDevice::DoLoopCallBack(): sending request");
643 if(MbSendRequest(ior.mMbId)!=0) {mState=StartUp; faudes_closesocket(mSlaveSocket); mSlaveSocket=-1; return;};
644 FD_DHV("mbDevice::DoLoopCallBack(): read response");
645 if(MbReceiveResponse()!=0) {mState=StartUp; faudes_closesocket(mSlaveSocket); mSlaveSocket=-1; return;};
646 FD_DHV("mbDevice::DoLoopCallBack(): received responde #" << mMessageLen);
647 // interpret response
648 if(MB_GETBYTE(MB_PDUOFF)==0x02) { // no error
649 FD_DHV("mbDevice::DoLoopCallBack(): input image received");
650 int count=MB_GETBYTE(MB_PDUOFF+1); // todo: verify
651 count=ior.mCount;
652 int src=MB_PDUOFF+2;
653 int data=MB_GETBYTE(src);
654 int addr=ior.mFdAddress;
655 int shft=0x01;
656 while(count) {
657 if(!mpOutputMask[addr]) mpImage[addr]= (( data & shft) != 0);
658 addr++; count--; shft = shft <<1;
659 if(shft==0x100) { shft=0x01; data=MB_GETBYTE(++src);};
660 }
661 } else {
662 FD_DH("mbDevice::DoLoopCallBack(): received error response on read inputs");
663 }
664 }
665 // write all outputs
666 for(unsigned int i=0; i<mSlaveIoRanges.size(); i++) {
667 const IoRange& ior=mSlaveIoRanges.at(i);
668 if(ior.mInputs) continue;
669 // assemble request
670 MB_SETBYTE(MB_PDUOFF,0x0f); // write multiple coils
672 MB_SETINT(MB_PDUOFF+3,ior.mCount);
673 int bcount= (ior.mCount-1)/8 +1;
674 MB_SETBYTE(MB_PDUOFF+5,bcount);
675 int dst=MB_PDUOFF+6;
676 int data=0x00;
677 int shft=0x01;
678 int addr=ior.mFdAddress;
679 int count=ior.mCount;
680 while(count) {
681 if(mpImage[addr]) data |= shft;
682 addr++; count--; shft = shft <<1;
683 if(shft==0x100) { shft=0x01; MB_SETBYTE(dst,data); data=0x00; dst++;}
684 }
685 if(shft!=0x01) { MB_SETBYTE(dst,data);};
686 mMessageLen=6+bcount;
687 // send request
688 FD_DHV("mbDevice::DoLoopCallBack(): sending request");
689 if(MbSendRequest(ior.mMbId)!=0) {mState=StartUp; faudes_closesocket(mSlaveSocket); mSlaveSocket=-1; return;};
690 if(MbReceiveResponse()!=0) {mState=StartUp; faudes_closesocket(mSlaveSocket); mSlaveSocket=-1; return;};
691 if(MB_GETBYTE(MB_PDUOFF)!=0x0f) {
692 FD_DH("mbDevice::DoLoopCallBack(): received error response on write coils");
693 }
694 }
695 }
696
697
698 // slave role: sync image with remote masters
699
700 if((!mMasterRole) && (mState==Up)) {
701
702 // prepare relevant sources
703 fd_set mysocks;
704 int mysocks_max=0;
705 FD_ZERO(&mysocks);
706 for(unsigned int i=0; i< mMasterSockets.size(); i++) {
707 int mastersock=mMasterSockets.at(i);
708 if(mastersock<0) continue;
709 FD_SET(mastersock, &mysocks);
710 if(mysocks_max< mastersock) mysocks_max=mastersock;
711 }
712
713 // sense requests
714 struct timeval tv;
715 tv.tv_sec = 0;
716 tv.tv_usec = 0;
717 int avail=select(mysocks_max+1, &mysocks, NULL, NULL, &tv);
718
719 // loop master requests
720 if(avail>0)
721 for(unsigned int i=0; i< mMasterSockets.size(); i++) {
722 int mastersock=mMasterSockets.at(i);
723 if(mastersock<0) continue;
724 if(!FD_ISSET(mastersock, &mysocks)) continue;
725 FD_DHV("mbDevice::DoLoopCallback(): received message on sock " << mastersock);
726 if(MbReceiveRequest(mastersock)<0) {
727 FD_DH("mbDevice::DoLoopCallback(): receive error on sock " << mastersock);
728 faudes_closesocket(mastersock);
729 mMasterSockets.at(i)=-1; // todo: remove
730 continue;
731 }
732
733 // interpret request
734 int fnct=MB_GETBYTE(MB_PDUOFF);
735 int errcode = 0x01;
736 // read inputs or coils
737 if((fnct==0x01) || (fnct==0x02)) {
738 FD_DHV("mbDevice::DoLoopCallback(): coil-read or input read request");
739 int addr = MB_GETINT(MB_PDUOFF+1);
740 int count = MB_GETINT(MB_PDUOFF+3);
741 int bcount= ((count-1)/8+1);
742 FD_DHV("mbDevice::DoLoopCallback(): address range: @" << addr << " #" << count);
743 // test validity
744 errcode=0x00;
745 if(addr+count>mImageSize) errcode=0x02;
746 if(count>2000) errcode=0x02;
747 // perform
748 if(errcode==0x00) {
749 // fill in bits
750 int dst=MB_PDUOFF+2;
751 int data=0x00;
752 int shft=0x01;
753 while(count) {
754 if(mpImage[addr]) data |= shft;
755 addr++; count--; shft = shft <<1;
756 if(shft==0x100) { shft=0x01; MB_SETBYTE(dst,data); dst++; data=0x00;}
757 }
758 if(shft!=0x01) { MB_SETBYTE(dst++,data);};
759 MB_SETBYTE(MB_PDUOFF+1,bcount);
760 // set nessage length
761 mMessageLen=bcount+2;
762 }
763 }
764 // read input registers or holding registers
765 if((fnct==0x03) || (fnct==0x04)) {
766 FD_DHV("mbDevice::DoLoopCallback(): register or holding register read request");
767 int addr = MB_GETINT(MB_PDUOFF+1);
768 int count = MB_GETINT(MB_PDUOFF+3);
769 FD_DHV("mbDevice::DoLoopCallback(): address range: @" << addr << " #" << count);
770 // test validity
771 errcode=0x00;
772 if(16*addr+16*count>mImageSize)
773 errcode=0x02;
774 // perform
775 if(errcode==0x00) {
776 // set header length
777 mMessageLen=2*count+2;
778 MB_SETBYTE(MB_PDUOFF+1,2*count);
779 // fill in bits
780 int src= addr*16;
781 int dst=MB_PDUOFF+2;
782 for(;count>0; count--) {
783 int shft=0x01;
784 int lbyte=0x00;
785 for(;src<mImageSize && shft!=0x100; src++, shft = shft << 1)
786 if(mpImage[src]) lbyte |= shft;
787 shft=0x01;
788 int hbyte=0x00;
789 for(;src<mImageSize && shft!=0x100; src++, shft = shft << 1)
790 if(mpImage[src]) hbyte |= shft;
791 MB_SETBYTE(dst,hbyte); dst++;
792 MB_SETBYTE(dst,lbyte); dst++;
793 }
794 }
795 }
796 // write single coil
797 if(fnct==0x05) {
798 FD_DHV("mbDevice::DoLoopCallback(): write single coil request");
799 int addr = MB_GETINT(MB_PDUOFF+1);
800 bool val = ( ((unsigned char) MB_GETBYTE(MB_PDUOFF+3))==0xff);
801 FD_DHV("mbDevice::DoLoopCallback(): write single coil request: " << addr << " to " << val);
802 // test
803 errcode=0x00;
804 if(addr>=mImageSize) errcode=0x02;
805 // perform
806 if(errcode==0x00) {
807 if(mpOutputMask[addr]) mpImage[addr] = val;
808 mMessageLen=5;
809 }
810 }
811 // write single register
812 if(fnct==0x06) {
813 FD_DHV("mbDevice::DoLoopCallback(): write holding register request");
814 int addr = MB_GETINT(MB_PDUOFF+1);
815 int val = MB_GETINT(MB_PDUOFF+3);
816 FD_DHV("mbDevice::DoLoopCallback(): set @" << addr << " to " << val);
817 // test validity
818 errcode=0x00;
819 if(16*addr+16 >mImageSize)
820 errcode=0x02;
821 // perform
822 if(errcode==0x00) {
823 // extract bits
824 int dst=16*addr;
825 int hbyte= (val >> 8); // :-)
826 int lbyte= (val & 0xff);
827 int shft;
828 for(shft=0x01; shft!=0x100; shft = shft << 1, dst++)
829 mpImage[dst] = (( lbyte & shft) != 0);
830 for(shft=0x01; shft!=0x100; shft = shft << 1, dst++)
831 mpImage[dst] = (( hbyte & shft) != 0);
832 // setup reply
833 mMessageLen=5;
834 }
835 }
836 // write multiple coils
837 if(fnct==0x0f) {
838 FD_DHV("mbDevice::DoLoopCallback(): write multiple coils request");
839 int addr = MB_GETINT(MB_PDUOFF+1);
840 int count = MB_GETINT(MB_PDUOFF+3);
841 int bcount= MB_GETBYTE(MB_PDUOFF+5);
842 FD_DHV("mbDevice::DoLoopCallback(): address range: @" << addr << " #" << count << "(" << bcount << ")");
843 // test validity
844 errcode=0x00;
845 if(addr+count>mImageSize) errcode=0x02;
846 if( (bcount < ((count-1)/8+1)) || (mMessageLen < 6+bcount) ) errcode=0x03;
847 // perform
848 if(errcode==0x00) {
849 // extract bits
850 int src=MB_PDUOFF+6;
851 int data=0;
852 int shft=0x100;
853 while(count) {
854 if(shft==0x100) { shft=0x01; data=MB_GETBYTE(src);src++;};
855 if(!mpOutputMask[addr]) mpImage[addr]= (( data & shft) != 0);
856 addr++; count--; shft = shft <<1;
857 }
858 // setup reply
859 mMessageLen=5;
860 }
861 }
862 // write multiple holding registers
863 if(fnct==0x10) {
864 FD_DHV("mbDevice::DoLoopCallback(): write multiple holding registers request");
865 int addr = MB_GETINT(MB_PDUOFF+1);
866 int count = MB_GETINT(MB_PDUOFF+3);
867 int bcount= MB_GETBYTE(MB_PDUOFF+5);
868 FD_DHV("mbDevice::DoLoopCallback(): address range: @" << addr << " #" << count << "(" << bcount << ")");
869 // test validity
870 errcode=0x00;
871 if(16*addr+16*count>mImageSize)
872 errcode=0x02;
873 if( bcount != 2* count)
874 errcode=0x03;
875 // perform
876 if(errcode==0x00) {
877 // extract bits
878 int src=MB_PDUOFF+6;
879 int dst=16*addr;
880 for(;count>0;count--) {
881 int hbyte=MB_GETBYTE(src); src++;
882 int lbyte=MB_GETBYTE(src); src++;
883 int shft;
884 for(shft=0x01; shft!=0x100; shft = shft << 1, dst++)
885 mpImage[dst] = (( lbyte & shft) != 0);
886 for(shft=0x01; shft!=0x100; shft = shft << 1, dst++)
887 mpImage[dst] = (( hbyte & shft) != 0);
888 }
889 // setup reply
890 mMessageLen=5;
891 }
892 }
893 // send reply
894 if(errcode==0x00) {
895 FD_DHV("mbDevice::DoLoopCallback(): sending reply #" << mMessageLen);
896 MbSendResponse(mastersock);
897 }
898 // send error
899 if(errcode!=0x00) {
900 FD_DH("mbDevice::DoLoopCallback(): sending error reply, code " << errcode);
901 MB_SETBYTE(MB_PDUOFF, fnct | 0x80);
902 MB_SETBYTE(MB_PDUOFF+1, errcode);
903 mMessageLen=2;
904 MbSendResponse(mastersock);
905 }
906
907
908 } // end: slave role loops all clients for requests
909 } // end: slave role receiving requests
910
911}
912
913
914// DoReadSignalsPre(void)
916 return mSlaveSocket!=-1;
917}
918
919
920// DoReadSignalsPost(void)
923
924
925//ReadSignal(int)
927 return pInputImage[bit];
928}
929
930
931// DoWriteSignalsPre(void)
933 return mSlaveSocket!=-1;
934}
935
936// DoWrtieSignalsPost(void)
939
940
941//DoWriteSignal(int,int)
942void mbDevice::DoWriteSignal(int bit, bool value){
943
944 // Write one actuator value, adressed by bit number (0 to 63);
945 //FD_DHV("mbDevice("<<mName<<")::DoWriteSignal(" << bit << ", " << value <<")");
946
947 pOutputImage[bit]=value;
948
949}
950
951
952} // namespace
953
954
955
956#endif // end modbus support
#define FAUDES_TYPE_IMPLEMENTATION(ftype, ctype, cbase)
Definition cfl_types.h:958
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.33k --- 2025.09.16 --- c++ api documentaion by doxygen