MOOS 0.2375
|
00001 00002 // 00003 // MOOS - Mission Oriented Operating Suite 00004 // 00005 // A suit of Applications and Libraries for Mobile Robotics Research 00006 // Copyright (C) 2001-2005 Massachusetts Institute of Technology and 00007 // Oxford University. 00008 // 00009 // This software was written by Paul Newman at MIT 2001-2002 and Oxford 00010 // University 2003-2005. email: pnewman@robots.ox.ac.uk. 00011 // 00012 // This file is part of a MOOS Core Component. 00013 // 00014 // This program is free software; you can redistribute it and/or 00015 // modify it under the terms of the GNU General Public License as 00016 // published by the Free Software Foundation; either version 2 of the 00017 // License, or (at your option) any later version. 00018 // 00019 // This program is distributed in the hope that it will be useful, 00020 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00021 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00022 // General Public License for more details. 00023 // 00024 // You should have received a copy of the GNU General Public License 00025 // along with this program; if not, write to the Free Software 00026 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 00027 // 02111-1307, USA. 00028 // 00030 // MOOSCommPkt.cpp: implementation of the MOOSCommPkt class. 00031 // 00033 #ifdef _WIN32 00034 #pragma warning(disable : 4786) 00035 #pragma warning(disable : 4503) 00036 #endif 00037 00038 00039 #include <MOOSGenLib/MOOSGenLib.h> 00040 #include "MOOSCommPkt.h" 00041 #include "MOOSMsg.h" 00042 #include "MOOSGlobalHelper.h" 00043 #include <assert.h> 00044 #include <cstring> 00045 #include <iostream> 00046 #include "MOOSException.h" 00047 00048 00049 #define DEFAULT_ASSUMMED_MAX_MOOS_MSG_SIZE 40000 00050 00051 00052 #ifdef COMPRESSED_MOOS_PROTOCOL 00053 00054 #define MAX_BELIEVABLE_ZLIB_COMPRESSION 256 //if we look like we are getting a compression ration of more than this then bail - there is an error. 00055 00056 #define COMPRESSION_PACKET_SIZE_THRESHOLD 1024 //packets below this size won't be compressed. 00057 00058 #include <zlib.h> 00059 #endif 00060 00061 using namespace std; 00062 00064 // Construction/Destruction 00066 00067 CMOOSCommPkt::CMOOSCommPkt() 00068 { 00069 m_pStream = DefaultStream; 00070 m_nStreamSpace = MOOS_PKT_DEFAULT_SPACE; 00071 m_pNextData = m_pStream; 00072 m_bAllocated = false; 00073 m_nByteCount = 0; 00074 m_nMsgLen = 0; 00075 m_dfCompression = 1.0; 00076 00077 00078 } 00079 00080 CMOOSCommPkt::~CMOOSCommPkt() 00081 { 00082 if(m_bAllocated) 00083 { 00084 delete [] m_pStream; 00085 } 00086 } 00087 00088 double CMOOSCommPkt::GetCompression() 00089 { 00090 return m_dfCompression; 00091 } 00092 00093 int CMOOSCommPkt::GetBytesRequired() 00094 { 00095 if(m_nByteCount==0) 00096 { 00097 return sizeof(int); 00098 } 00099 else 00100 { 00101 return m_nMsgLen-m_nByteCount; 00102 } 00103 } 00104 00105 bool CMOOSCommPkt::Fill(unsigned char *InData, int nData) 00106 { 00107 00108 if( m_nByteCount ==0) 00109 { 00110 //here we figure out how many bytes we are expecting 00111 assert(nData==sizeof(int)); 00112 00113 memcpy((void*)(&m_nMsgLen),(void*)InData,sizeof(int)); 00114 00115 //look to swap byte order if this machine is Big End in 00116 if(!IsLittleEndian()) 00117 { 00118 m_nMsgLen = SwapByteOrder<int>(m_nMsgLen); 00119 } 00120 00121 } 00122 00123 if(m_nByteCount+nData>=m_nStreamSpace) 00124 { 00125 InflateTo(2*(m_nStreamSpace+nData)); 00126 } 00127 memcpy(m_pNextData,InData,nData); 00128 m_pNextData+=nData; 00129 m_nByteCount+=nData; 00130 00131 return true; 00132 } 00133 00134 00135 int CMOOSCommPkt::GetStreamLength() 00136 { 00137 return m_nMsgLen; 00138 } 00139 00140 00142 bool CMOOSCommPkt::Serialize(MOOSMSG_LIST &List, bool bToStream, bool bNoNULL, double * pdfPktTime) 00143 { 00144 //note +1 is for indicator regarding compressed or not compressed 00145 unsigned int nHeaderSize = 2*sizeof(int)+1; 00146 00147 if(bToStream) 00148 { 00149 00150 m_nMsgLen=0; 00151 m_pNextData = m_pStream; 00152 m_nByteCount = 0; 00153 00154 //we need to leave space at the start of the packet for total 00155 //length and number of include messages 00156 00157 m_pNextData+=nHeaderSize; 00158 m_nByteCount+= nHeaderSize; 00159 00160 00161 //assume to start with that mesages are reasonably sized -if they aren't we'll make an adjustment 00162 unsigned int nWorkingMemoryCurrentSize = DEFAULT_ASSUMMED_MAX_MOOS_MSG_SIZE; 00163 unsigned char * pTmpBuffer =new unsigned char[nWorkingMemoryCurrentSize] ; 00164 00165 00166 00167 MOOSMSG_LIST::iterator p; 00168 int nCount = 0; 00169 for(p = List.begin();p!=List.end();p++,nCount++) 00170 { 00171 00172 unsigned int nRequiredSize = p->GetSizeInBytesWhenSerialised(); 00173 00174 if(nRequiredSize>nWorkingMemoryCurrentSize) 00175 { 00176 //std::cerr<<"making more space "<<nWorkingMemoryCurrentSize<<" -> "<<nRequiredSize<<std::endl; 00177 nWorkingMemoryCurrentSize = nRequiredSize; 00178 delete [] pTmpBuffer; 00179 00180 pTmpBuffer = new unsigned char [nWorkingMemoryCurrentSize]; 00181 } 00182 00183 //MOOSTrace("Sending %s \n",p->m_sKey.c_str()); 00184 //int nTmpSize = MAX_MOOS_MSG_SIZE; 00185 00186 int nCopied = (*p).Serialize(pTmpBuffer,nWorkingMemoryCurrentSize); 00187 00188 if(nCopied !=nRequiredSize ) 00189 { 00190 std::cerr<<"bad news expected "<<nWorkingMemoryCurrentSize<<" but serialisation took "<<nCopied<<std::endl; 00191 p->Trace(); 00192 } 00193 00194 if(nCopied!=-1) 00195 { 00196 //now copy to our stream... 00197 CopyToStream(pTmpBuffer,nCopied); 00198 } 00199 else 00200 { 00201 delete [] pTmpBuffer; 00202 return false; 00203 } 00204 00205 } 00206 00207 delete [] pTmpBuffer; 00208 00209 00210 00211 00212 00213 unsigned char bCompressed = 0; 00214 #ifdef COMPRESSED_MOOS_PROTOCOL 00215 00216 //we only compress if it is worth it 00217 if(m_nByteCount>COMPRESSION_PACKET_SIZE_THRESHOLD) 00218 { 00219 00220 unsigned long int nDataLength = m_nByteCount-nHeaderSize; 00221 00222 //we require .1% extra and 12 bytes 00223 unsigned long int nZBSize = (unsigned int) ((float)nDataLength*1.01+13); 00224 unsigned char * ZipBuffer = new unsigned char[nZBSize]; 00225 00226 int nZipResult = compress(ZipBuffer,&nZBSize,m_pStream+nHeaderSize,nDataLength); 00227 00228 00229 switch (nZipResult) { 00230 case Z_OK: 00231 break; 00232 case Z_BUF_ERROR: 00233 throw CMOOSException("failed to compress outgoing data - looks like compression actually expanded data!"); 00234 break; 00235 case Z_MEM_ERROR: 00236 throw CMOOSException("zlib memory error!"); 00237 break; 00238 default: 00239 break; 00240 } 00241 00242 //MOOSTrace("Compressed out going buffer is %d bytes and original is %d\n",nZBSize,nDataLength); 00243 00244 memcpy(m_pStream+nHeaderSize,ZipBuffer,nZBSize); 00245 00246 //maybe useful to record what compression ration we are getting 00247 m_dfCompression = (double)m_nByteCount/nZBSize; 00248 00249 //now we have a new byte count 00250 m_nByteCount = nZBSize+nHeaderSize; 00251 00252 delete ZipBuffer; 00253 00254 bCompressed = 1; 00255 00256 } 00257 00258 #endif 00259 00260 //finally write how many bytes we have written at the start 00261 //look for need to swap byte order if required 00262 m_pNextData = m_pStream; 00263 int nBC = IsLittleEndian() ? m_nByteCount : SwapByteOrder<int>(m_nByteCount); 00264 memcpy((void*)m_pNextData,(void*)(&nBC),sizeof(m_nByteCount)); 00265 m_pNextData+=sizeof(m_nByteCount); 00266 00267 00268 //and then how many messages are included 00269 //look for need to swap byte order if required 00270 int nMessages = List.size(); 00271 nMessages = IsLittleEndian() ? nMessages : SwapByteOrder<int>(nMessages); 00272 memcpy((void*)m_pNextData,(void*)(&nMessages),sizeof(nMessages)); 00273 m_pNextData+=sizeof(nMessages); 00274 00275 //and is this a compressed message or not? 00276 *m_pNextData = bCompressed; 00277 m_pNextData+=1; 00278 00279 00280 } 00281 else 00282 { 00283 00284 m_pNextData = m_pStream; 00285 m_nMsgLen = 0; 00286 m_nByteCount = 0; 00287 00288 //first figure out the length of the message 00289 //look to swap byte order as required 00290 memcpy((void*)(&m_nMsgLen),(void*)m_pNextData,sizeof(m_nMsgLen)); 00291 m_nMsgLen = IsLittleEndian() ? m_nMsgLen : SwapByteOrder<int>(m_nMsgLen); 00292 m_pNextData+=sizeof(m_nMsgLen); 00293 m_nByteCount+=sizeof(m_nMsgLen); 00294 00295 int nSpaceFree=m_nMsgLen - sizeof(m_nMsgLen); 00296 00297 //now figure out how many messages are packed in this packet 00298 //look to swap byte order as required 00299 int nMessages = 0; 00300 memcpy((void*)(&nMessages),(void*)m_pNextData,sizeof(nMessages)); 00301 nMessages = IsLittleEndian() ? nMessages : SwapByteOrder<int>(nMessages); 00302 m_pNextData+=sizeof(nMessages); 00303 nSpaceFree-=sizeof(nMessages); 00304 m_nByteCount+=sizeof(nMessages); 00305 00306 //now account for one byet of compression indication 00307 m_pNextData+=sizeof(unsigned char); 00308 nSpaceFree-=sizeof(unsigned char); 00309 m_nByteCount+=sizeof(unsigned char); 00310 00311 00312 00313 #ifdef COMPRESSED_MOOS_PROTOCOL 00314 unsigned char bCompressed = m_pStream[nHeaderSize-1]; 00315 //we will only have compressed if it is worth it 00316 00317 //MOOSTrace("Message is %s compressed\n",bCompressed? "":"not"); 00318 if(bCompressed) 00319 { 00320 00321 unsigned long int nDataLength = m_nMsgLen-nHeaderSize; 00322 00323 //we'd be surprised if we had more tha 90% compression 00324 int nCompressionFactor = 10; 00325 unsigned char * ZipBuffer = NULL; 00326 unsigned long int nZBSize; 00327 00328 int nZipResult = -1; 00329 00330 do 00331 { 00332 00333 nZBSize = nCompressionFactor*nDataLength; 00334 00335 ZipBuffer = new unsigned char[nZBSize]; 00336 00337 nZipResult = uncompress(ZipBuffer,&nZBSize,m_pStream+nHeaderSize,nDataLength); 00338 00339 switch (nZipResult) 00340 { 00341 case Z_MEM_ERROR: 00342 throw CMOOSException("ZLIB out of memory"); 00343 break; 00344 00345 case Z_BUF_ERROR: 00346 { 00347 //if we get here we need more space 00348 delete ZipBuffer; 00349 nCompressionFactor *=8; 00350 00351 if(nCompressionFactor>MAX_BELIEVABLE_ZLIB_COMPRESSION) 00352 { 00353 //this is very suspcious 00354 throw CMOOSException("error in decompressing CMOOSPkt stream"); 00355 } 00356 } 00357 break; 00358 00359 case Z_DATA_ERROR: 00360 throw CMOOSException("ZLIB received corrupted data - this is really bad news."); 00361 break; 00362 00363 00364 default: 00365 break; 00366 } 00367 }while(nZipResult!=Z_OK); 00368 00369 00370 //MOOSTrace("received %d data bytes and uncompressed them to %d bytes\n",nDataLength,nZBSize); 00371 00372 //maybe useful to record what compression ration we are getting 00373 m_dfCompression = (double)nZBSize/nDataLength; 00374 00375 00376 //we have a new message length 00377 m_nMsgLen = nZBSize+nHeaderSize; 00378 00379 //check we have allocated enough memory 00380 if(m_nMsgLen>m_nStreamSpace) 00381 { 00382 //interesting case we are out of memory - we need some more 00383 InflateTo(m_nMsgLen); 00384 } 00385 00386 //copy the uncompressed data into our stream 00387 memcpy(m_pStream+nHeaderSize,ZipBuffer,nZBSize); 00388 00389 //recalculate space free 00390 nSpaceFree= m_nMsgLen-nHeaderSize; 00391 00392 //be a good citizen 00393 delete ZipBuffer; 00394 } 00395 #endif 00396 00397 00398 for(int i = 0; i<nMessages;i++) 00399 { 00400 00401 CMOOSMsg Msg; 00402 int nUsed = Msg.Serialize(m_pNextData,nSpaceFree,false); 00403 00404 if(nUsed!=-1) 00405 { 00406 //allows us to not store NULL messages 00407 bool bOmit = bNoNULL && (Msg.m_cMsgType==MOOS_NULL_MSG); 00408 00409 if(Msg.m_cMsgType==MOOS_NULL_MSG && pdfPktTime!=NULL && i == 0 ) 00410 { 00411 *pdfPktTime = Msg.GetDouble(); 00412 } 00413 00414 if(!bOmit) 00415 { 00416 List.push_front(Msg); 00417 } 00418 00419 00420 m_pNextData+=nUsed; 00421 m_nByteCount+=nUsed; 00422 nSpaceFree-=nUsed; 00423 00424 } 00425 else 00426 { 00427 //bad news... 00428 break; 00429 } 00430 } 00431 } 00432 00433 //here at the last moment we can fill in our totalm length for safe keeping 00434 m_nMsgLen = m_nByteCount; 00435 00436 return true; 00437 } 00438 00439 bool CMOOSCommPkt::CopyToStream(unsigned char *pData, int nBytes) 00440 { 00441 //well do we have enough space to do this? 00442 if(m_nByteCount+nBytes>=m_nStreamSpace) 00443 { 00444 //no..better inflate ourselves... 00445 InflateTo(2*(m_nStreamSpace+nBytes)); 00446 } 00447 00448 //by this point we are guaranteed enough space.. 00449 //unless (new has failed which is really the end of the world and we will be dead :-( ) 00450 00451 //copy temporary buffer to our main buffer 00452 memcpy(m_pNextData,pData,nBytes); 00453 00454 //increment hot spot 00455 m_pNextData+=nBytes; 00456 00457 //increment byte count 00458 m_nByteCount+=nBytes; 00459 00460 return true; 00461 } 00462 00463 bool CMOOSCommPkt::InflateTo(int nNewStreamSize) 00464 { 00465 //make more space then.. 00466 m_nStreamSpace = nNewStreamSize; 00467 00468 unsigned char * pNew = new unsigned char[m_nStreamSpace]; 00469 00470 if(pNew==NULL) 00471 { 00472 //absolute disaster..nothing we can do ...whole process must exit 00473 MOOSTrace("memory allocation failed in CMOOSCommPkt::CopyToStream"); 00474 return false; 00475 } 00476 00477 //copy what we have already assembled into new space 00478 memcpy(pNew,m_pStream,m_nByteCount); 00479 00480 00481 if(m_bAllocated) 00482 { 00483 delete [] m_pStream; 00484 } 00485 else 00486 { 00487 m_bAllocated = true; 00488 } 00489 00490 //switch m_pStream to point to the new space; 00491 m_pStream = pNew; 00492 00493 //and set the next data point to be m_nMsgLen bytes from start 00494 //so we will start writin after are existing stuff 00495 m_pNextData=m_pStream+m_nByteCount; 00496 00497 return true; 00498 }