iop_serial.cpp

Go to the documentation of this file.
00001 /** @file iop_serial.h Process image via serial line */
00002 
00003 /*
00004    FAU Discrete Event Systems Library (libfaudes)
00005 
00006    Copyright (C) 2011, Thomas Moor.
00007 
00008 */
00009 
00010 
00011 // include header
00012 #include "iop_serial.h"
00013 
00014 // only compile for use with spi configured
00015 #ifdef FAUDES_IODEVICE_SERIAL
00016 
00017 namespace faudes {
00018 
00019 /*
00020  **********************************************
00021  **********************************************
00022  **********************************************
00023 
00024  implementation: serial helpers
00025 
00026  **********************************************
00027  **********************************************
00028  **********************************************
00029  */
00030 
00031 
00032 // hardcoded block size (64bit)
00033 #define PSIZE 8
00034 
00035 // hardcoded timeout (usec) for serial transmission
00036 #define PUSECPB ((int) 1000000.0* (8.0+1.0+1.0) / 115200.0)
00037 #define PUSECXX PUSECPB
00038 
00039 
00040 // open serial port, return fd or -1 on error
00041 int serialOpen(const std::string& devname) {
00042   // open device / det filedescriptor
00043   int fd=-1;
00044   fd = open(devname.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
00045   if(fd == -1) return -1;
00046   // set termio options (raw 115200 8N1, no flow control)
00047   // see also "Serial Programming Guide for POSIX Operating Systems" by Michael R. Sweet
00048   struct termios options;
00049   tcgetattr(fd, &options);
00050   cfsetispeed(&options, B115200);
00051   cfsetospeed(&options, B115200);
00052   options.c_cflag |= (CLOCAL | CREAD);
00053   options.c_cflag &= ~CSIZE;        // 8 data bit
00054   options.c_cflag |= CS8;           
00055   options.c_cflag &= ~PARENB;       // no parity
00056   options.c_cflag &= ~CSTOPB;       // one stop bit
00057   options.c_cflag &= ~CRTSCTS;      // no harware flow control
00058   options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // raw input (no line editing, no signals)
00059   options.c_iflag &= ~(IXON | IXOFF | IXANY);  // no software flow control
00060   options.c_iflag &= ~(INLCR | IGNCR |  ICRNL | IUCLC | IMAXBEL); // no CR/LF mapping and thelike
00061   options.c_oflag &= ~OPOST;        // raw output (no delays after CR etc)
00062   tcsetattr(fd, TCSANOW, &options);
00063   // configure via fcntl
00064   fcntl(fd, F_SETFL, FNDELAY);  // nonblocking read (should be by open option anyway)
00065   // done
00066   return fd; 
00067 }
00068 
00069 // close serial port
00070 void serialClose(int fd) {
00071   close(fd);
00072 }
00073 
00074 // write block (-1 on error, else 0)
00075 int serialWriteBlock(int fd, char* data, int len) {
00076   // debug 
00077   /*
00078   static char mdata[1024];
00079   if(len>1024) len=1024;
00080   for(int i=0; i<len; i++) mdata[i]= data[i]+'A';
00081   data=mdata;
00082   */
00083   while(len>0) {
00084     int n=write(fd, data, len);
00085     if(n<0) break;
00086     len-=n;
00087   }
00088   if(len!=0) 
00089    FD_DH("spiDevice()::serialWriteBlock(): cannot transmit");
00090   return len == 0 ? 0 : -1;
00091 }
00092 
00093 // flush input buffer
00094 void serialFlush(int fd) {
00095   char d;
00096   int cnt=0;
00097   while(read(fd, &d,1)==1) cnt++;
00098   if(cnt!=0)  FD_DH("spiDevice()::serialFlush(): rec/drop #" << cnt);
00099 }
00100 
00101 // read block (return -1 on error, else 0)
00102 // Note: this fnct reads the data if the amount of characters available 
00103 // exactly matches the specified block length; in any other case,
00104 // the read buffer is flushed and -1 is returned to indicate an
00105 // error.
00106 int serialReadBlock(int fd, char* data, int len) {
00107   int n1= read(fd, data,len);
00108   // error / no data at all
00109   if(n1<=0) return -1;
00110   // bytes missing, allow for transmission to complete
00111   if(n1<len){
00112     usleep( (len-n1)* PUSECPB + PUSECXX );
00113     int n2= read(fd, data+n1,len-n1);
00114     if(n2<=0) {
00115       FD_DH("spiDevice()::serialReadBlock(): rec/drop #" << n1)
00116       return -1;
00117     }
00118     n1+=n2;
00119   }
00120   // test for empty buffer
00121   int n3= read(fd, data,1);
00122   if(n3==1) {
00123     FD_DH("spiDevice()::serialReadBlock(): rec/drop #" << n1);
00124     serialFlush(fd);
00125     return -1;
00126   }
00127   // success
00128   return 0;
00129 }
00130 
00131 
00132 
00133 /*
00134  **********************************************
00135  **********************************************
00136  **********************************************
00137 
00138  implementation: spiDevice
00139 
00140  **********************************************
00141  **********************************************
00142  **********************************************
00143  */
00144 
00145 
00146 // std faudes, incl dummy
00147 FAUDES_TYPE_IMPLEMENTATION(SpiDevice,spiDevice,sDevice)
00148 
00149 // autoregister (not functional, see xdevice constructor)
00150 AutoRegisterType<spiDevice> gRtiRegisterSpiDevice("SpiDevice");
00151 
00152 //constructor
00153 spiDevice::spiDevice(void) : sDevice() {
00154   FD_DHV("spiDevice(" << mName << ")::spiDevice()");
00155   // have appropriate default label for token io
00156   mDefaultLabel = "SpiDevice";
00157   // pointer to internal I/O-image 
00158   mpImage=0;
00159   pOutputImage=0;
00160   pInputImage=0;
00161   mpOutputMask=0;
00162   // behavioural defaults
00163   mMaster=false;
00164   mSyncWrite=true;
00165 }
00166 
00167 //deconstructor
00168 spiDevice::~spiDevice(void) {
00169   FD_DHV("spiDevice(" << mName << ")::~spiDevice()");
00170   Stop();
00171 }
00172 
00173 // Clear
00174 void spiDevice::Clear(void) {
00175  // clear base
00176  sDevice::Clear();
00177  // my configuration
00178  mMaster=false;
00179  mDeviceFiles.clear();
00180  mPorts.clear();
00181  mSyncWrite=true;
00182 }
00183 
00184 
00185 //Compile(void)
00186 void spiDevice::Compile(void){
00187   //setup up internal data structure
00188   FD_DHV("spiDevice(" << mName << ")::Compile()");
00189   // call base
00190   sDevice::Compile();
00191   // test for illegal address range
00192   if(mMaxBitAddress+1 > PSIZE*8) {
00193     std::stringstream errstr;
00194     errstr << "Invalid address range (must not exceed " << PSIZE*8 << ")";
00195     throw Exception("spiDevice:Compile", errstr.str(), 52);  
00196   }
00197   // slave must have exactly one serial device
00198   if((!mMaster) && (mDeviceFiles.size()!=1)) {
00199     std::stringstream errstr;
00200     errstr << "Slave must have exactly one device file specified";
00201     throw Exception("spiDevice:Compile()", errstr.str(), 52);  
00202   }
00203   // master must have at least  one serial device
00204   if((mMaster) && (mDeviceFiles.size()==0)) {
00205     std::stringstream errstr;
00206     errstr << "Master must have at least one device file specified";
00207     throw Exception("spiDevice:Compile()", errstr.str(), 52);  
00208   }
00209 }
00210 
00211 
00212 //DoWrite(rTr,rLabel,pContext)
00213 void spiDevice::DoWritePreface(TokenWriter& rTw, const std::string& rLabel,  const Type* pContext) const {
00214   FD_DHV("spiDevice("<<mName<<")::DoWritePreface()");
00215   //call base
00216   sDevice::DoWritePreface(rTw,"",pContext);
00217   // role
00218   Token ftoken;
00219   ftoken.SetEmpty("Role");
00220   if(mMaster) {
00221     ftoken.InsAttributeString("value","master");
00222   } else {
00223     ftoken.InsAttributeString("value","slave");
00224   }
00225   rTw << ftoken;
00226   // devicefiles
00227   for(unsigned int i=0; i<mDeviceFiles.size(); i++) {
00228     Token dtoken;
00229     ftoken.SetEmpty("DeviceFile");
00230     ftoken.InsAttributeString("value",mDeviceFiles.at(i));
00231     rTw << ftoken;
00232   }
00233 }
00234 
00235 
00236 //DoReadPreface(rTr,rLabel,pContext)
00237 void spiDevice::DoReadPreface(TokenReader& rTr,const std::string& rLabel, const Type* pContext){
00238   //dummy for token-input
00239   FD_DHV("spiDevice("<<mName<<")::DoReadPreface()");
00240   //call base
00241   sDevice::DoReadPreface(rTr,"",pContext);
00242   // my global configuration
00243   Token token;
00244   while(rTr.Peek(token)) {
00245     // role
00246     if(token.IsBegin("Role")) {
00247       rTr.ReadBegin("Role");
00248       mMaster=false;
00249       if(!token.ExistsAttributeString("value")) {
00250         std::stringstream errstr;
00251         errstr << "Invalid role tag" << rTr.FileLine();
00252         throw Exception("spiDevice:Read", errstr.str(), 52);  
00253       }
00254       std::string val=token.AttributeStringValue("value");
00255       std::transform(val.begin(), val.end(), val.begin(), tolower);
00256       if(val=="master") mMaster =true;
00257       else if(val=="slave") mMaster =false;
00258       else {
00259         std::stringstream errstr;
00260         errstr << "Invalid role tag" << rTr.FileLine();
00261         throw Exception("spiDevice:Read", errstr.str(), 52);  
00262       }
00263       rTr.ReadEnd("Role");
00264       continue;
00265     }
00266     // device file
00267     if(token.IsBegin("DeviceFile")) {
00268       rTr.ReadBegin("DeviceFile");
00269       if(!token.ExistsAttributeString("value")) {
00270         std::stringstream errstr;
00271         errstr << "Invalid device tag" << rTr.FileLine();
00272         throw Exception("spiDevice:Read", errstr.str(), 52);  
00273       } 
00274       mDeviceFiles.push_back(token.AttributeStringValue("value"));
00275       rTr.ReadEnd("DeviceFile");
00276       continue;
00277     }
00278     // unknown: break
00279     break;
00280   }
00281 }
00282 
00283 
00284 // Start(void)
00285 void spiDevice::Start(void) {
00286   //open wago-device
00287   if(mState!=Down) return;
00288   FD_DH("spiDevice(" << mName << ")::Start(): open devices #" << mDeviceFiles.size());
00289   // initialize serial line(s)
00290   for(unsigned int i=0; i<mDeviceFiles.size(); i++) {
00291     int fd=serialOpen(mDeviceFiles.at(i));
00292     if(fd<0) {
00293       std::stringstream errstr;
00294       errstr << "cannot open serial line " << mDeviceFiles.at(i);
00295       throw Exception("spiDevice()::Start()", errstr.str(), 552);
00296     }
00297     mPorts.push_back(fd);
00298   }
00299   // initialize images
00300   mpImage = new char[PSIZE];
00301   memset(mpImage,0,PSIZE);
00302   pOutputImage=mpImage;
00303   pInputImage=mpImage;
00304   // initialize output mask
00305   mpOutputMask = new char[PSIZE];
00306   memset(mpOutputMask,0,PSIZE);
00307   for(int bit=0; bit<=mMaxBitAddress; bit++) 
00308     if(!mOutputLevelIndexMap[bit].Empty()) 
00309       mpOutputMask[bit/8] |= (0x01 << (bit %8));
00310   // call base (incl. reset)
00311   sDevice::Start();
00312   // pessimistic: let background thread figure presence of other nodes
00313   mState=StartUp;
00314 }
00315 
00316 // Stop()
00317 void spiDevice::Stop(void) {
00318   //close serial interface
00319   if(mState != Up && mState != StartUp) return;
00320   FD_DHV("spiDevice(" << mName << ")::Stop()");
00321   // call base
00322   sDevice::Stop();
00323   // close lines
00324   for(unsigned int i=0; i< mPorts.size(); i++)
00325     serialClose(mPorts.at(i));
00326   mPorts.clear();
00327   // invalidate images
00328   if(mpImage) delete mpImage;
00329   mpImage=0;
00330   pOutputImage=0;
00331   pInputImage=0;
00332   if(mpOutputMask) delete mpOutputMask;
00333   mpOutputMask=0;
00334   // down
00335   mState=Down;
00336 }
00337 
00338 
00339 // loopcall-back for serial comminucation
00340 void spiDevice::DoLoopCallback(void) {
00341   // bail out
00342   if(mState!=Up && mState!=StartUp) return;
00343   //FD_DHV("spiDevice(" << mName << ")::DoLoopCallBack()");
00344   // master: send image block to each client and await reply
00345   if(mMaster) {
00346     FD_DHV("spiDevice()::DoLoopCallBack(): master send images #" << mPorts.size());
00347     for(unsigned int i=0; i< mPorts.size(); i++) {
00348       // discard input buffer
00349       serialFlush(mPorts.at(i));
00350       // write
00351       serialWriteBlock(mPorts.at(i),mpImage,PSIZE); 
00352       // await reply
00353       usleep(3000); // hardcoded, conservative ... here the cheap design shows :-(
00354       char buffer[PSIZE];
00355       int err=serialReadBlock(mPorts.at(i),buffer,PSIZE);
00356       if(err!=0) continue;
00357       FD_DHV("spiDevice()::DoLoopCallBack(): master received image");
00358       // copy to my image, except for my outputs
00359       for(int i=0; i<PSIZE; i++) 
00360         mpImage[i]= (buffer[i] & ~mpOutputMask[i]) | (mpImage[i] & mpOutputMask[i]);
00361     }
00362   }
00363   // slave: receive image block if available, send reply
00364   if(!mMaster) {
00365     //FD_DHV("spiDevice()::DoLoopCallBack(): slave await images #" << mPorts.size());
00366     // test for image
00367     char buffer[PSIZE];
00368     int err=serialReadBlock(mPorts.at(0),buffer,PSIZE);
00369     if(err==0) {
00370       FD_DHV("spiDevice()::DoLoopCallBack(): slave received image");
00371       // copy to my image, except for my outputs
00372       for(int i=0; i<PSIZE; i++) 
00373         mpImage[i]= (buffer[i] & ~mpOutputMask[i]) | (mpImage[i] & mpOutputMask[i]);
00374       // write
00375       serialWriteBlock(mPorts.at(0),mpImage,PSIZE); 
00376     }
00377   } // end: slave
00378 }
00379 
00380 
00381 // DoReadSignalsPre(void)
00382 bool spiDevice::DoReadSignalsPre(void) {
00383   return pInputImage!=0;
00384 }
00385 
00386 
00387 // DoReadSignalsPost(void)
00388 void spiDevice::DoReadSignalsPost(void) {
00389 }
00390 
00391 
00392 //ReadSignal(int)
00393 bool spiDevice::DoReadSignal(int bit){
00394   // Read one input value, addressed by bit number (0 to 63);
00395 
00396   // Determine byte and bit address
00397   int byte = bit / 8;
00398   bit = bit % 8;
00399 
00400   // Read bit
00401   return ( pInputImage[byte] & (0x01 << (bit)) ) != 0x00;
00402 }
00403 
00404 
00405 // DoWriteSignalsPre(void)
00406 bool spiDevice::DoWriteSignalsPre(void) {
00407   return pOutputImage!=0;
00408 }
00409 
00410 // DoWrtieSignalsPost(void)
00411 void spiDevice::DoWriteSignalsPost(void) {
00412 }
00413 
00414 
00415 //DoWriteSignal(int,int)
00416 void spiDevice::DoWriteSignal(int bit, bool value){
00417 
00418   // Write one actor value, adressed by bit number (0 to 63);
00419   FD_DHV("spiDevice("<<mName<<")::DoWriteSignal(" << bit << ", " << value <<")");
00420 
00421   // Determine byte and bit addresse.
00422   int byte = (bit) / 8;
00423   bit = (bit) % 8;
00424 
00425   // Write value to output-image using bit-operations
00426   if(value) pOutputImage[byte] |= (0x01 << (bit));
00427   else pOutputImage[byte] &= ~(0x01 << (bit));
00428 
00429 }
00430 
00431 
00432 } // namespace
00433 
00434 
00435 
00436 #endif // end serial support

libFAUDES 2.23h --- 2014.04.03 --- c++ api documentaion by doxygen