LTKCPP-- LLRP Toolkit C Plus Plus Library
|
00001 00002 /* 00003 ***************************************************************************** 00004 * * 00005 * IMPINJ CONFIDENTIAL AND PROPRIETARY * 00006 * * 00007 * This source code is the sole property of Impinj, Inc. Reproduction or * 00008 * utilization of this source code in whole or in part is forbidden without * 00009 * the prior written consent of Impinj, Inc. * 00010 * * 00011 * (c) Copyright Impinj, Inc. 2007,2010. All rights reserved. * 00012 * * 00013 *****************************************************************************/ 00014 00029 #include <stdio.h> 00030 #include "ltkcpp.h" 00031 #include "impinj_ltkcpp.h" 00032 #include "time.h" 00033 00034 using namespace LLRP; 00035 00036 /* 00037 ** Sorry, we use this linux safe method 00038 ** to print buffers. WIndows has the same 00039 ** method, but by a different name 00040 */ 00041 #if (WIN32) 00042 #define snprintf _snprintf 00043 #endif 00044 00045 class CMyApplication 00046 { 00047 private: 00048 00049 unsigned int m_modelNumber; 00050 unsigned int m_messageID; 00051 00052 public: 00054 int m_Verbose; 00055 00057 CConnection * m_pConnectionToReader; 00058 00059 inline 00060 CMyApplication (void) 00061 : m_Verbose(0), m_pConnectionToReader(NULL) 00062 { 00063 m_messageID = 0; 00064 } 00065 00066 int 00067 run ( 00068 char * pReaderHostName); 00069 00070 int 00071 checkConnectionStatus (void); 00072 00073 int 00074 enableImpinjExtensions (void); 00075 00076 int 00077 resetConfigurationToFactoryDefaults (void); 00078 00079 int 00080 getReaderCapabilities(void); 00081 00082 int 00083 setImpinjReaderConfig(void); 00084 00085 int 00086 addROSpec (void); 00087 00088 int 00089 enableROSpec (void); 00090 00091 int 00092 startROSpec (void); 00093 00094 int 00095 stopROSpec (void); 00096 00097 int 00098 awaitAndPrintReport (int timeoutSec); 00099 00100 void 00101 printTagReportData ( 00102 CRO_ACCESS_REPORT * pRO_ACCESS_REPORT); 00103 00104 void 00105 printOneTagReportData ( 00106 CTagReportData * pTagReportData); 00107 00108 int 00109 formatOneEPC ( 00110 CParameter * pEpcParameter, 00111 char * buf, 00112 int buflen, 00113 char * startStr); 00114 00115 int getOnePhaseAngle( 00116 CImpinjRFPhaseAngle *pRfPhase, 00117 double *out); 00118 00119 int 00120 getOnePeakRSSI ( 00121 CImpinjPeakRSSI *pPeakRSSI, 00122 double *out); 00123 00124 int 00125 getOneTimestamp ( 00126 CParameter *pTimestamp, 00127 unsigned long long *out); 00128 00129 int 00130 getOneAntenna ( 00131 CAntennaID *pAntenna, 00132 unsigned short *out); 00133 00134 int 00135 getOneChannelIndex ( 00136 CChannelIndex *pChannelIndex, 00137 unsigned short *out); 00138 00139 int 00140 estimateVelocity( 00141 char * epcStr, 00142 double rssi, 00143 double phase, 00144 unsigned short channelIndex, 00145 unsigned short antenna, 00146 unsigned long long time, 00147 double *outVelocity); 00148 00149 void 00150 handleReaderEventNotification ( 00151 CReaderEventNotificationData *pNtfData); 00152 00153 void 00154 handleAntennaEvent ( 00155 CAntennaEvent * pAntennaEvent); 00156 00157 void 00158 handleReaderExceptionEvent ( 00159 CReaderExceptionEvent * pReaderExceptionEvent); 00160 00161 int 00162 checkLLRPStatus ( 00163 CLLRPStatus * pLLRPStatus, 00164 char * pWhatStr); 00165 00166 CMessage * 00167 transact ( 00168 CMessage * pSendMsg); 00169 00170 CMessage * 00171 recvMessage ( 00172 int nMaxMS); 00173 00174 int 00175 sendMessage ( 00176 CMessage * pSendMsg); 00177 00178 void 00179 printXMLMessage ( 00180 CMessage * pMessage); 00181 }; 00182 00183 00184 /* BEGIN forward declarations */ 00185 int 00186 main ( 00187 int ac, 00188 char * av[]); 00189 00190 void 00191 usage ( 00192 char * pProgName); 00193 /* END forward declarations */ 00194 00195 00211 int 00212 main ( 00213 int ac, 00214 char * av[]) 00215 { 00216 CMyApplication myApp; 00217 char * pReaderHostName; 00218 int rc; 00219 00220 /* 00221 * Process comand arguments, determine reader name 00222 * and verbosity level. 00223 */ 00224 if(ac == 2) 00225 { 00226 pReaderHostName = av[1]; 00227 } 00228 else if(ac == 3) 00229 { 00230 char * p = av[1]; 00231 00232 while(*p) 00233 { 00234 switch(*p++) 00235 { 00236 case '-': /* linux conventional option warn char */ 00237 case '/': /* Windows/DOS conventional option warn char */ 00238 break; 00239 00240 case 'v': 00241 case 'V': 00242 myApp.m_Verbose++; 00243 break; 00244 00245 default: 00246 usage(av[0]); 00247 /* no return */ 00248 break; 00249 } 00250 } 00251 00252 pReaderHostName = av[2]; 00253 } 00254 else 00255 { 00256 usage(av[0]); 00257 /* no return */ 00258 } 00259 00260 /* 00261 * Run application, capture return value for exit status 00262 */ 00263 rc = myApp.run(pReaderHostName); 00264 00265 printf("INFO: Done\n"); 00266 00267 /* 00268 * Exit with the right status. 00269 */ 00270 if(0 == rc) 00271 { 00272 exit(0); 00273 } 00274 else 00275 { 00276 exit(2); 00277 } 00278 /*NOTREACHED*/ 00279 } 00280 00281 00293 void 00294 usage ( 00295 char * pProgName) 00296 { 00297 #ifdef linux 00298 printf("Usage: %s [-v[v]] READERHOSTNAME\n", pProgName); 00299 printf("\n"); 00300 printf("Each -v increases verbosity level\n"); 00301 #endif /* linux */ 00302 #ifdef WIN32 00303 printf("Usage: %s [/v[v]] READERHOSTNAME\n", pProgName); 00304 printf("\n"); 00305 printf("Each /v increases verbosity level\n"); 00306 #endif /* WIN32 */ 00307 exit(1); 00308 } 00309 00310 00352 int 00353 CMyApplication::run ( 00354 char * pReaderHostName) 00355 { 00356 CTypeRegistry * pTypeRegistry; 00357 CConnection * pConn; 00358 int rc; 00359 00360 /* 00361 * Allocate the type registry. This is needed 00362 * by the connection to decode. 00363 */ 00364 pTypeRegistry = getTheTypeRegistry(); 00365 if(NULL == pTypeRegistry) 00366 { 00367 printf("ERROR: getTheTypeRegistry failed\n"); 00368 return -1; 00369 } 00370 00371 /* 00372 * Enroll impinj extension types into the 00373 * type registry, in preparation for using 00374 * Impinj extension params. 00375 */ 00376 LLRP::enrollImpinjTypesIntoRegistry(pTypeRegistry); 00377 00378 /* 00379 * Construct a connection (LLRP::CConnection). 00380 * Using a 32kb max frame size for send/recv. 00381 * The connection object is ready for business 00382 * but not actually connected to the reader yet. 00383 */ 00384 pConn = new CConnection(pTypeRegistry, 32u*1024u); 00385 if(NULL == pConn) 00386 { 00387 printf("ERROR: new CConnection failed\n"); 00388 return -2; 00389 } 00390 00391 /* 00392 * Open the connection to the reader 00393 */ 00394 if(m_Verbose) 00395 { 00396 printf("INFO: Connecting to %s....\n", pReaderHostName); 00397 } 00398 00399 rc = pConn->openSecureConnectionToReader(pReaderHostName); 00400 if(0 != rc) 00401 { 00402 printf("ERROR: connect: %s (%d)\n", pConn->getConnectError(), rc); 00403 delete pConn; 00404 return -3; 00405 } 00406 00407 /* 00408 * Record the pointer to the connection object so other 00409 * routines can use it. 00410 */ 00411 m_pConnectionToReader = pConn; 00412 00413 if(m_Verbose) 00414 { 00415 printf("INFO: Connected, checking status....\n"); 00416 } 00417 00418 /* 00419 * Commence the sequence and check for errors as we go. 00420 * See comments for each routine for details. 00421 * Each routine prints messages. 00422 */ 00423 rc = 1; 00424 if(0 == checkConnectionStatus()) 00425 { 00426 rc = 2; 00427 if(0 == enableImpinjExtensions()) 00428 { 00429 rc = 3; 00430 if(0 == resetConfigurationToFactoryDefaults()) 00431 { 00432 rc = 4; 00433 if(0 == getReaderCapabilities()) 00434 { 00435 rc = 6; 00436 if(0 == setImpinjReaderConfig()) 00437 { 00438 rc = 7; 00439 if(0 == addROSpec()) 00440 { 00441 rc = 8; 00442 if(0 == enableROSpec()) 00443 { 00444 rc = 9; 00445 if(0 == startROSpec()) 00446 { 00447 rc = 10; 00448 if(0 == awaitAndPrintReport(60)) 00449 { 00450 rc = 11; 00451 if(0 == stopROSpec()) 00452 { 00453 rc = 0; 00454 } 00455 } 00456 } 00457 } 00458 } 00459 } 00460 } 00461 } 00462 00463 /* 00464 * After we're done, try to leave the reader 00465 * in a clean state for next use. This is best 00466 * effort and no checking of the result is done. 00467 */ 00468 if(m_Verbose) 00469 { 00470 printf("INFO: Clean up reader configuration...\n"); 00471 } 00472 resetConfigurationToFactoryDefaults(); 00473 } 00474 } 00475 00476 if(m_Verbose) 00477 { 00478 printf("INFO: Finished\n"); 00479 } 00480 00481 /* 00482 * Close the connection and release its resources 00483 */ 00484 pConn->closeConnectionToReader(); 00485 delete pConn; 00486 00487 /* 00488 * Done with the registry. 00489 */ 00490 delete pTypeRegistry; 00491 00492 /* 00493 * When we get here all allocated memory should have been deallocated. 00494 */ 00495 00496 return rc; 00497 } 00498 00499 00530 int 00531 CMyApplication::checkConnectionStatus (void) 00532 { 00533 CMessage * pMessage; 00534 CREADER_EVENT_NOTIFICATION *pNtf; 00535 CReaderEventNotificationData *pNtfData; 00536 CConnectionAttemptEvent * pEvent; 00537 00538 /* 00539 * Expect the notification within 10 seconds. 00540 * It is suppose to be the very first message sent. 00541 */ 00542 pMessage = recvMessage(10000); 00543 00544 /* 00545 * recvMessage() returns NULL if something went wrong. 00546 */ 00547 if(NULL == pMessage) 00548 { 00549 /* recvMessage already tattled */ 00550 goto fail; 00551 } 00552 00553 /* 00554 * Check to make sure the message is of the right type. 00555 * The type label (pointer) in the message should be 00556 * the type descriptor for READER_EVENT_NOTIFICATION. 00557 */ 00558 if(&CREADER_EVENT_NOTIFICATION::s_typeDescriptor != pMessage->m_pType) 00559 { 00560 goto fail; 00561 } 00562 00563 /* 00564 * Now that we are sure it is a READER_EVENT_NOTIFICATION, 00565 * traverse to the ReaderEventNotificationData parameter. 00566 */ 00567 pNtf = (CREADER_EVENT_NOTIFICATION *) pMessage; 00568 pNtfData = pNtf->getReaderEventNotificationData(); 00569 if(NULL == pNtfData) 00570 { 00571 goto fail; 00572 } 00573 00574 /* 00575 * The ConnectionAttemptEvent parameter must be present. 00576 */ 00577 pEvent = pNtfData->getConnectionAttemptEvent(); 00578 if(NULL == pEvent) 00579 { 00580 goto fail; 00581 } 00582 00583 /* 00584 * The status in the ConnectionAttemptEvent parameter 00585 * must indicate connection success. 00586 */ 00587 if(ConnectionAttemptStatusType_Success != pEvent->getStatus()) 00588 { 00589 goto fail; 00590 } 00591 00592 /* 00593 * Done with the message 00594 */ 00595 delete pMessage; 00596 00597 if(m_Verbose) 00598 { 00599 printf("INFO: Connection status OK\n"); 00600 } 00601 00602 /* 00603 * Victory. 00604 */ 00605 return 0; 00606 00607 fail: 00608 /* 00609 * Something went wrong. Tattle. Clean up. Return error. 00610 */ 00611 printf("ERROR: checkConnectionStatus failed\n"); 00612 delete pMessage; 00613 return -1; 00614 } 00615 00633 int 00634 CMyApplication::enableImpinjExtensions (void) 00635 { 00636 CIMPINJ_ENABLE_EXTENSIONS * pCmd; 00637 CMessage * pRspMsg; 00638 CIMPINJ_ENABLE_EXTENSIONS_RESPONSE *pRsp; 00639 00640 /* 00641 * Compose the command message 00642 */ 00643 pCmd = new CIMPINJ_ENABLE_EXTENSIONS(); 00644 pCmd->setMessageID(m_messageID++); 00645 /* 00646 * Send the message, expect the response of certain type 00647 */ 00648 pRspMsg = transact(pCmd); 00649 00650 /* 00651 * Done with the command message 00652 */ 00653 delete pCmd; 00654 00655 /* 00656 * transact() returns NULL if something went wrong. 00657 */ 00658 if(NULL == pRspMsg) 00659 { 00660 /* transact already tattled */ 00661 return -1; 00662 } 00663 00664 /* 00665 * Cast to a CIMPINJ_ENABLE_EXTENSIONS_RESPONSE message. 00666 */ 00667 pRsp = (CIMPINJ_ENABLE_EXTENSIONS_RESPONSE *) pRspMsg; 00668 00669 /* 00670 * Check the LLRPStatus parameter. 00671 */ 00672 if(0 != checkLLRPStatus(pRsp->getLLRPStatus(), 00673 "enableImpinjExtensions")) 00674 { 00675 /* checkLLRPStatus already tattled */ 00676 delete pRspMsg; 00677 return -1; 00678 } 00679 00680 /* 00681 * Done with the response message. 00682 */ 00683 delete pRspMsg; 00684 00685 /* 00686 * Tattle progress, maybe 00687 */ 00688 if(m_Verbose) 00689 { 00690 printf("INFO: Impinj Extensions are enabled\n"); 00691 } 00692 00693 /* 00694 * Victory. 00695 */ 00696 return 0; 00697 } 00698 00719 int 00720 CMyApplication::resetConfigurationToFactoryDefaults (void) 00721 { 00722 CSET_READER_CONFIG * pCmd; 00723 CMessage * pRspMsg; 00724 CSET_READER_CONFIG_RESPONSE *pRsp; 00725 00726 /* 00727 * Compose the command message 00728 */ 00729 pCmd = new CSET_READER_CONFIG(); 00730 pCmd->setMessageID(m_messageID++); 00731 pCmd->setResetToFactoryDefault(1); 00732 00733 /* 00734 * Send the message, expect the response of certain type 00735 */ 00736 pRspMsg = transact(pCmd); 00737 00738 /* 00739 * Done with the command message 00740 */ 00741 delete pCmd; 00742 00743 /* 00744 * transact() returns NULL if something went wrong. 00745 */ 00746 if(NULL == pRspMsg) 00747 { 00748 /* transact already tattled */ 00749 return -1; 00750 } 00751 00752 /* 00753 * Cast to a SET_READER_CONFIG_RESPONSE message. 00754 */ 00755 pRsp = (CSET_READER_CONFIG_RESPONSE *) pRspMsg; 00756 00757 /* 00758 * Check the LLRPStatus parameter. 00759 */ 00760 if(0 != checkLLRPStatus(pRsp->getLLRPStatus(), 00761 "resetConfigurationToFactoryDefaults")) 00762 { 00763 /* checkLLRPStatus already tattled */ 00764 delete pRspMsg; 00765 return -1; 00766 } 00767 00768 /* 00769 * Done with the response message. 00770 */ 00771 delete pRspMsg; 00772 00773 /* 00774 * Tattle progress, maybe 00775 */ 00776 if(m_Verbose) 00777 { 00778 printf("INFO: Configuration reset to factory defaults\n"); 00779 } 00780 00781 /* 00782 * Victory. 00783 */ 00784 return 0; 00785 } 00786 00803 int 00804 CMyApplication::getReaderCapabilities(void) 00805 { 00806 CGET_READER_CAPABILITIES *pCmd; 00807 CMessage * pRspMsg; 00808 CGET_READER_CAPABILITIES_RESPONSE *pRsp; 00809 CGeneralDeviceCapabilities *pDeviceCap; 00810 std::list<CTransmitPowerLevelTableEntry *>::iterator PwrLvl; 00811 unsigned int bMajorVersion, bMinorVersion, bDevVersion, bBuildVersion = 0; 00812 00813 /* 00814 * Compose the command message 00815 */ 00816 pCmd = new CGET_READER_CAPABILITIES(); 00817 pCmd->setMessageID(m_messageID++); 00818 pCmd->setRequestedData(GetReaderCapabilitiesRequestedData_All); 00819 00820 /* 00821 * Send the message, expect the response of certain type 00822 */ 00823 pRspMsg = transact(pCmd); 00824 00825 /* 00826 * Done with the command message 00827 */ 00828 delete pCmd; 00829 00830 /* 00831 * transact() returns NULL if something went wrong. 00832 */ 00833 if(NULL == pRspMsg) 00834 { 00835 /* transact already tattled */ 00836 return -1; 00837 } 00838 00839 /* 00840 * Cast to a CGET_READER_CAPABILITIES_RESPONSE message. 00841 */ 00842 pRsp = (CGET_READER_CAPABILITIES_RESPONSE *) pRspMsg; 00843 00844 /* 00845 * Check the LLRPStatus parameter. 00846 */ 00847 if(0 != checkLLRPStatus(pRsp->getLLRPStatus(), 00848 "getReaderCapabilities")) 00849 { 00850 /* checkLLRPStatus already tattled */ 00851 delete pRspMsg; 00852 return -1; 00853 } 00854 00855 /* if this parameter is missing, or if this is not an Impinj 00856 ** reader, we can't determine its capabilities so we exit 00857 ** Impinj Private Enterprise NUmber is 25882 */ 00858 if( (NULL == (pDeviceCap = pRsp->getGeneralDeviceCapabilities())) || 00859 (25882 != pDeviceCap->getDeviceManufacturerName())) 00860 { 00861 delete pRspMsg; 00862 return -1; 00863 } 00864 00865 00866 /* 00867 * Get the version information from the reader and make sure we are 4.4 or better. 00868 */ 00869 if ( pDeviceCap->getReaderFirmwareVersion().m_nValue < 3) 00870 { 00871 printf("ERROR: Must have Firmware 4.4 or later for low level data example \n"); 00872 delete pRspMsg; 00873 return -1; 00874 } 00875 00876 /* 00877 * Parse to make sure it is really 4.4 or better 00878 */ 00879 sscanf((char *) pDeviceCap->getReaderFirmwareVersion().m_pValue, "%u.%u.%u.%u", &bMajorVersion, &bMinorVersion, &bDevVersion, &bBuildVersion); 00880 00881 if( (bMajorVersion < 4) && (bMinorVersion < 4) ) 00882 { 00883 printf("ERROR: Must have Firmware 4.4 or later for low level data example \n"); 00884 delete pRspMsg; 00885 return -1; 00886 } 00887 00888 if(1 < m_Verbose) 00889 { 00890 printf("INFO: Reader Model Name %u\n", m_modelNumber); 00891 } 00892 00893 /* 00894 * Done with the response message. 00895 */ 00896 delete pRspMsg; 00897 00898 /* 00899 * Tattle progress, maybe 00900 */ 00901 if(m_Verbose) 00902 { 00903 printf("INFO: Found LLRP Capabilities \n"); 00904 } 00905 00906 /* 00907 * Victory. 00908 */ 00909 return 0; 00910 } 00911 00991 int 00992 CMyApplication::setImpinjReaderConfig(void) 00993 { 00994 CSET_READER_CONFIG *pCmd; 00995 CMessage * pRspMsg; 00996 CSET_READER_CONFIG_RESPONSE *pRsp; 00997 00998 /* 00999 * Compose the command message 01000 */ 01001 pCmd = new CSET_READER_CONFIG(); 01002 pCmd->setMessageID(m_messageID++); 01003 01004 CAntennaConfiguration *pAnt = new(CAntennaConfiguration); 01005 01006 /* 01007 ** Apply this configuration to all antennas 01008 */ 01009 pAnt->setAntennaID(0); 01010 01011 01012 CC1G2InventoryCommand *pC1G2Inv = new CC1G2InventoryCommand(); 01013 01014 /* set the mode to auto-set max throughput */ 01015 CC1G2RFControl *pC1G2Rf = new CC1G2RFControl(); 01016 pC1G2Rf->setModeIndex(2); /* DRM M=4 */ 01017 pC1G2Rf->setTari(0); /* tari is ignored by the reader */ 01018 pC1G2Inv->setC1G2RFControl(pC1G2Rf); 01019 01020 CC1G2SingulationControl *pC1G2Sing = new CC1G2SingulationControl(); 01021 pC1G2Sing->setSession(2); 01022 pC1G2Sing->setTagPopulation(1); 01023 pC1G2Sing->setTagTransitTime(0); 01024 pC1G2Inv->setC1G2SingulationControl(pC1G2Sing); 01025 01026 pC1G2Inv->setTagInventoryStateAware(false); 01027 01028 /* set the Impinj Inventory search mode as per the use case */ 01029 CImpinjInventorySearchMode *pImpIsm = new CImpinjInventorySearchMode(); 01030 pImpIsm->setInventorySearchMode(ImpinjInventorySearchType_Dual_Target); 01031 pC1G2Inv->addCustom(pImpIsm); 01032 01033 /* set the Impinj Low Duty Cycle mode as per the use case */ 01034 CImpinjLowDutyCycle *pImpLdc = new CImpinjLowDutyCycle(); 01035 pImpLdc->setEmptyFieldTimeout(10000); 01036 pImpLdc->setFieldPingInterval(200); 01037 pImpLdc->setLowDutyCycleMode(ImpinjLowDutyCycleMode_Enabled); 01038 pC1G2Inv->addCustom(pImpLdc); 01039 01040 pAnt->addAirProtocolInventoryCommandSettings(pC1G2Inv); 01041 pCmd->addAntennaConfiguration(pAnt); 01042 01043 /* report every tag (N=1) since that is required for tag direction */ 01044 CROReportSpec *pROrs = new CROReportSpec(); 01045 pROrs->setROReportTrigger(ROReportTriggerType_Upon_N_Tags_Or_End_Of_ROSpec); 01046 pROrs->setN(1); 01047 01048 /* lets turn off off report data that we don't need since our use 01049 ** case suggests we are bandwidth constrained */ 01050 CTagReportContentSelector *pROcontent = new CTagReportContentSelector(); 01051 pROcontent->setEnableAccessSpecID(false); 01052 01053 /* these are very handy to have with low level data */ 01054 pROcontent->setEnableAntennaID(true); 01055 pROcontent->setEnableChannelIndex(true); 01056 pROcontent->setEnableFirstSeenTimestamp(true); 01057 01058 pROcontent->setEnableInventoryParameterSpecID(false); 01059 pROcontent->setEnableLastSeenTimestamp(false); 01060 pROcontent->setEnablePeakRSSI(false); 01061 pROcontent->setEnableROSpecID(false); 01062 pROcontent->setEnableSpecIndex(false); 01063 pROcontent->setEnableTagSeenCount(false); 01064 CC1G2EPCMemorySelector *pC1G2Mem = new CC1G2EPCMemorySelector(); 01065 pC1G2Mem->setEnableCRC(false); 01066 pC1G2Mem->setEnablePCBits(false); 01067 pROcontent->addAirProtocolEPCMemorySelector(pC1G2Mem); 01068 01069 pROrs->setTagReportContentSelector(pROcontent); 01070 01071 /* Turn on the low level phase data in the ImpinjTagContentSelector*/ 01072 CImpinjTagReportContentSelector * pImpTagCnt = new CImpinjTagReportContentSelector(); 01073 01074 CImpinjEnableRFPhaseAngle * pEnableRfPhase = new CImpinjEnableRFPhaseAngle(); 01075 pEnableRfPhase->setRFPhaseAngleMode(ImpinjRFPhaseAngleMode_Enabled); 01076 pImpTagCnt->setImpinjEnableRFPhaseAngle(pEnableRfPhase); 01077 01078 CImpinjEnablePeakRSSI * pEnablePeakRssi = new CImpinjEnablePeakRSSI(); 01079 pEnablePeakRssi->setPeakRSSIMode(ImpinjPeakRSSIMode_Enabled); 01080 pImpTagCnt->setImpinjEnablePeakRSSI(pEnablePeakRssi); 01081 01082 CImpinjEnableSerializedTID * pEnableSerializedTID = new CImpinjEnableSerializedTID(); 01083 pEnableSerializedTID->setSerializedTIDMode(ImpinjSerializedTIDMode_Disabled); 01084 pImpTagCnt->setImpinjEnableSerializedTID(pEnableSerializedTID); 01085 01086 pROrs->addCustom(pImpTagCnt); 01087 01088 pCmd->setROReportSpec(pROrs); 01089 01090 /* 01091 * Send the message, expect the response of certain type 01092 */ 01093 pRspMsg = transact(pCmd); 01094 01095 /* 01096 * Done with the command message 01097 */ 01098 delete pCmd; 01099 01100 /* 01101 * transact() returns NULL if something went wrong. 01102 */ 01103 if(NULL == pRspMsg) 01104 { 01105 /* transact already tattled */ 01106 return -1; 01107 } 01108 01109 /* 01110 * Cast to a CSET_READER_CONFIG_RESPONSE message. 01111 */ 01112 pRsp = (CSET_READER_CONFIG_RESPONSE *) pRspMsg; 01113 01114 /* 01115 * Check the LLRPStatus parameter. 01116 */ 01117 if(0 != checkLLRPStatus(pRsp->getLLRPStatus(), 01118 "setImpinjReaderConfig")) 01119 { 01120 /* checkLLRPStatus already tattled */ 01121 delete pRspMsg; 01122 return -1; 01123 } 01124 01125 /* 01126 * Done with the response message. 01127 */ 01128 delete pRspMsg; 01129 01130 /* 01131 * Tattle progress, maybe 01132 */ 01133 if(m_Verbose) 01134 { 01135 printf("INFO: Set Impinj Reader Configuration \n"); 01136 } 01137 01138 /* 01139 * Victory. 01140 */ 01141 return 0; 01142 } 01143 01193 int 01194 CMyApplication::addROSpec (void) 01195 { 01196 CROSpecStartTrigger * pROSpecStartTrigger = 01197 new CROSpecStartTrigger(); 01198 pROSpecStartTrigger->setROSpecStartTriggerType( 01199 ROSpecStartTriggerType_Null); 01200 01201 CROSpecStopTrigger * pROSpecStopTrigger = new CROSpecStopTrigger(); 01202 pROSpecStopTrigger->setROSpecStopTriggerType(ROSpecStopTriggerType_Null); 01203 pROSpecStopTrigger->setDurationTriggerValue(0); /* n/a */ 01204 01205 CROBoundarySpec * pROBoundarySpec = new CROBoundarySpec(); 01206 pROBoundarySpec->setROSpecStartTrigger(pROSpecStartTrigger); 01207 pROBoundarySpec->setROSpecStopTrigger(pROSpecStopTrigger); 01208 01209 CAISpecStopTrigger * pAISpecStopTrigger = new CAISpecStopTrigger(); 01210 pAISpecStopTrigger->setAISpecStopTriggerType( 01211 AISpecStopTriggerType_Null); 01212 pAISpecStopTrigger->setDurationTrigger(0); 01213 01214 CInventoryParameterSpec * pInventoryParameterSpec = 01215 new CInventoryParameterSpec(); 01216 pInventoryParameterSpec->setInventoryParameterSpecID(1234); 01217 pInventoryParameterSpec->setProtocolID(AirProtocols_EPCGlobalClass1Gen2); 01218 01219 /* 01220 ** configure to use two antennas to be compatible with 01221 ** our tag direction settings 01222 */ 01223 llrp_u16v_t AntennaIDs = llrp_u16v_t(1); 01224 AntennaIDs.m_pValue[0] = 2; 01225 01226 CAISpec * pAISpec = new CAISpec(); 01227 pAISpec->setAntennaIDs(AntennaIDs); 01228 pAISpec->setAISpecStopTrigger(pAISpecStopTrigger); 01229 pAISpec->addInventoryParameterSpec(pInventoryParameterSpec); 01230 01231 CROSpec * pROSpec = new CROSpec(); 01232 pROSpec->setROSpecID(1111); 01233 pROSpec->setPriority(0); 01234 pROSpec->setCurrentState(ROSpecState_Disabled); 01235 pROSpec->setROBoundarySpec(pROBoundarySpec); 01236 pROSpec->addSpecParameter(pAISpec); 01237 01238 CADD_ROSPEC * pCmd; 01239 CMessage * pRspMsg; 01240 CADD_ROSPEC_RESPONSE * pRsp; 01241 01242 /* 01243 * Compose the command message. 01244 * N.B.: After the message is composed, all the parameters 01245 * constructed, immediately above, are considered "owned" 01246 * by the command message. When it is destructed so 01247 * too will the parameters be. 01248 */ 01249 pCmd = new CADD_ROSPEC(); 01250 pCmd->setMessageID(m_messageID++); 01251 pCmd->setROSpec(pROSpec); 01252 01253 /* 01254 * Send the message, expect the response of certain type 01255 */ 01256 pRspMsg = transact(pCmd); 01257 01258 /* 01259 * Done with the command message. 01260 * N.B.: And the parameters 01261 */ 01262 delete pCmd; 01263 01264 /* 01265 * transact() returns NULL if something went wrong. 01266 */ 01267 if(NULL == pRspMsg) 01268 { 01269 /* transact already tattled */ 01270 return -1; 01271 } 01272 01273 /* 01274 * Cast to a ADD_ROSPEC_RESPONSE message. 01275 */ 01276 pRsp = (CADD_ROSPEC_RESPONSE *) pRspMsg; 01277 01278 /* 01279 * Check the LLRPStatus parameter. 01280 */ 01281 if(0 != checkLLRPStatus(pRsp->getLLRPStatus(), "addROSpec")) 01282 { 01283 /* checkLLRPStatus already tattled */ 01284 delete pRspMsg; 01285 return -1; 01286 } 01287 01288 /* 01289 * Done with the response message. 01290 */ 01291 delete pRspMsg; 01292 01293 /* 01294 * Tattle progress, maybe 01295 */ 01296 if(m_Verbose) 01297 { 01298 printf("INFO: ROSpec added\n"); 01299 } 01300 01301 /* 01302 * Victory. 01303 */ 01304 return 0; 01305 } 01306 01307 01325 int 01326 CMyApplication::enableROSpec (void) 01327 { 01328 CENABLE_ROSPEC * pCmd; 01329 CMessage * pRspMsg; 01330 CENABLE_ROSPEC_RESPONSE * pRsp; 01331 01332 /* 01333 * Compose the command message 01334 */ 01335 pCmd = new CENABLE_ROSPEC(); 01336 pCmd->setMessageID(m_messageID++); 01337 pCmd->setROSpecID(1111); 01338 01339 /* 01340 * Send the message, expect the response of certain type 01341 */ 01342 pRspMsg = transact(pCmd); 01343 01344 /* 01345 * Done with the command message 01346 */ 01347 delete pCmd; 01348 01349 /* 01350 * transact() returns NULL if something went wrong. 01351 */ 01352 if(NULL == pRspMsg) 01353 { 01354 /* transact already tattled */ 01355 return -1; 01356 } 01357 01358 /* 01359 * Cast to a ENABLE_ROSPEC_RESPONSE message. 01360 */ 01361 pRsp = (CENABLE_ROSPEC_RESPONSE *) pRspMsg; 01362 01363 /* 01364 * Check the LLRPStatus parameter. 01365 */ 01366 if(0 != checkLLRPStatus(pRsp->getLLRPStatus(), "enableROSpec")) 01367 { 01368 /* checkLLRPStatus already tattled */ 01369 delete pRspMsg; 01370 return -1; 01371 } 01372 01373 /* 01374 * Done with the response message. 01375 */ 01376 delete pRspMsg; 01377 01378 /* 01379 * Tattle progress, maybe 01380 */ 01381 if(m_Verbose) 01382 { 01383 printf("INFO: ROSpec enabled\n"); 01384 } 01385 01386 /* 01387 * Victory. 01388 */ 01389 return 0; 01390 } 01391 01392 01410 int 01411 CMyApplication::startROSpec (void) 01412 { 01413 CSTART_ROSPEC * pCmd; 01414 CMessage * pRspMsg; 01415 CSTART_ROSPEC_RESPONSE * pRsp; 01416 01417 /* 01418 * Compose the command message 01419 */ 01420 pCmd = new CSTART_ROSPEC(); 01421 pCmd->setMessageID(m_messageID++); 01422 pCmd->setROSpecID(1111); 01423 01424 /* 01425 * Send the message, expect the response of certain type 01426 */ 01427 pRspMsg = transact(pCmd); 01428 01429 /* 01430 * Done with the command message 01431 */ 01432 delete pCmd; 01433 01434 /* 01435 * transact() returns NULL if something went wrong. 01436 */ 01437 if(NULL == pRspMsg) 01438 { 01439 /* transact already tattled */ 01440 return -1; 01441 } 01442 01443 /* 01444 * Cast to a START_ROSPEC_RESPONSE message. 01445 */ 01446 pRsp = (CSTART_ROSPEC_RESPONSE *) pRspMsg; 01447 01448 /* 01449 * Check the LLRPStatus parameter. 01450 */ 01451 if(0 != checkLLRPStatus(pRsp->getLLRPStatus(), "startROSpec")) 01452 { 01453 /* checkLLRPStatus already tattled */ 01454 delete pRspMsg; 01455 return -1; 01456 } 01457 01458 /* 01459 * Done with the response message. 01460 */ 01461 delete pRspMsg; 01462 01463 /* 01464 * Tattle progress 01465 */ 01466 if(m_Verbose) 01467 { 01468 printf("INFO: ROSpec started\n"); 01469 } 01470 01471 /* 01472 * Victory. 01473 */ 01474 return 0; 01475 } 01476 01494 int 01495 CMyApplication::stopROSpec (void) 01496 { 01497 CSTOP_ROSPEC * pCmd; 01498 CMessage * pRspMsg; 01499 CSTOP_ROSPEC_RESPONSE * pRsp; 01500 01501 /* 01502 * Compose the command message 01503 */ 01504 pCmd = new CSTOP_ROSPEC(); 01505 pCmd->setMessageID(m_messageID++); 01506 pCmd->setROSpecID(1111); 01507 01508 /* 01509 * Send the message, expect the response of certain type 01510 */ 01511 pRspMsg = transact(pCmd); 01512 01513 /* 01514 * Done with the command message 01515 */ 01516 delete pCmd; 01517 01518 /* 01519 * transact() returns NULL if something went wrong. 01520 */ 01521 if(NULL == pRspMsg) 01522 { 01523 /* transact already tattled */ 01524 return -1; 01525 } 01526 01527 /* 01528 * Cast to a STOP_ROSPEC_RESPONSE message. 01529 */ 01530 pRsp = (CSTOP_ROSPEC_RESPONSE *) pRspMsg; 01531 01532 /* 01533 * Check the LLRPStatus parameter. 01534 */ 01535 if(0 != checkLLRPStatus(pRsp->getLLRPStatus(), "stopROSpec")) 01536 { 01537 /* checkLLRPStatus already tattled */ 01538 delete pRspMsg; 01539 return -1; 01540 } 01541 01542 /* 01543 * Done with the response message. 01544 */ 01545 delete pRspMsg; 01546 01547 /* 01548 * Tattle progress 01549 */ 01550 if(m_Verbose) 01551 { 01552 printf("INFO: ROSpec stopped\n"); 01553 } 01554 01555 /* 01556 * Victory. 01557 */ 01558 return 0; 01559 } 01560 01561 01581 int 01582 CMyApplication::awaitAndPrintReport (int timeout) 01583 { 01584 int bDone = 0; 01585 int retVal = 0; 01586 time_t startTime = time(NULL); 01587 time_t tempTime; 01588 /* 01589 * Keep receiving messages until done or until 01590 * something bad happens. 01591 */ 01592 while(!bDone) 01593 { 01594 CMessage * pMessage; 01595 const CTypeDescriptor * pType; 01596 01597 /* 01598 * Wait up to 1 second for a report. Check 01599 * That way, we can check the timestamp even if 01600 * there are no reports coming in 01601 */ 01602 pMessage = recvMessage(1000); 01603 01604 /* validate the timestamp */ 01605 tempTime = time(NULL); 01606 if(difftime(tempTime, startTime) > timeout) 01607 { 01608 bDone=1; 01609 } 01610 01611 if(NULL == pMessage) 01612 { 01613 continue; 01614 } 01615 01616 /* 01617 * What happens depends on what kind of message 01618 * received. Use the type label (m_pType) to 01619 * discriminate message types. 01620 */ 01621 pType = pMessage->m_pType; 01622 01623 /* 01624 * Is it a tag report? If so, print it out. 01625 */ 01626 if(&CRO_ACCESS_REPORT::s_typeDescriptor == pType) 01627 { 01628 CRO_ACCESS_REPORT * pNtf; 01629 01630 pNtf = (CRO_ACCESS_REPORT *) pMessage; 01631 01632 printTagReportData(pNtf); 01633 } 01634 01635 /* 01636 * Is it a reader event? This example only recognizes 01637 * AntennaEvents. 01638 */ 01639 else if(&CREADER_EVENT_NOTIFICATION::s_typeDescriptor == pType) 01640 { 01641 CREADER_EVENT_NOTIFICATION *pNtf; 01642 CReaderEventNotificationData *pNtfData; 01643 01644 pNtf = (CREADER_EVENT_NOTIFICATION *) pMessage; 01645 01646 pNtfData = pNtf->getReaderEventNotificationData(); 01647 if(NULL != pNtfData) 01648 { 01649 handleReaderEventNotification(pNtfData); 01650 } 01651 else 01652 { 01653 /* 01654 * This should never happen. Using continue 01655 * to keep indent depth down. 01656 */ 01657 printf("WARNING: READER_EVENT_NOTIFICATION without data\n"); 01658 } 01659 } 01660 01661 /* 01662 * Hmmm. Something unexpected. Just tattle and keep going. 01663 */ 01664 else 01665 { 01666 printf("WARNING: Ignored unexpected message during monitor: %s\n", 01667 pType->m_pName); 01668 } 01669 01670 /* 01671 * Done with the received message 01672 */ 01673 delete pMessage; 01674 } 01675 01676 return retVal; 01677 } 01678 01679 01694 void 01695 CMyApplication::printTagReportData ( 01696 CRO_ACCESS_REPORT * pRO_ACCESS_REPORT) 01697 { 01698 std::list<CTagReportData *>::iterator Cur; 01699 std::list<CParameter *>::iterator CustCur; 01700 01701 unsigned int nEntry = 0; 01702 01703 /* 01704 * Loop through and count the number of entries 01705 */ 01706 for( 01707 Cur = pRO_ACCESS_REPORT->beginTagReportData(); 01708 Cur != pRO_ACCESS_REPORT->endTagReportData(); 01709 Cur++) 01710 { 01711 nEntry++; 01712 } 01713 01714 if(m_Verbose) 01715 { 01716 printf("INFO: %u tag report entries\n", nEntry); 01717 } 01718 01719 /* 01720 * Loop through again and print each entry. 01721 */ 01722 for( 01723 Cur = pRO_ACCESS_REPORT->beginTagReportData(); 01724 Cur != pRO_ACCESS_REPORT->endTagReportData(); 01725 Cur++) 01726 { 01727 printOneTagReportData(*Cur); 01728 } 01729 } 01730 01739 int 01740 CMyApplication::formatOneEPC ( 01741 CParameter *pEPCParameter, 01742 char *buf, 01743 int buflen, 01744 char * startStr) 01745 { 01746 char * p = buf; 01747 int bufsize = buflen; 01748 int written = 0; 01749 01750 written = snprintf(p, bufsize, "%s", startStr); 01751 bufsize -= written; 01752 p += written; 01753 01754 if(NULL != pEPCParameter) 01755 { 01756 const CTypeDescriptor * pType; 01757 llrp_u96_t my_u96; 01758 llrp_u1v_t my_u1v; 01759 llrp_u8_t * pValue = NULL; 01760 unsigned int n, i; 01761 01762 pType = pEPCParameter->m_pType; 01763 if(&CEPC_96::s_typeDescriptor == pType) 01764 { 01765 CEPC_96 *pEPC_96; 01766 01767 pEPC_96 = (CEPC_96 *) pEPCParameter; 01768 my_u96 = pEPC_96->getEPC(); 01769 pValue = my_u96.m_aValue; 01770 n = 12u; 01771 } 01772 else if(&CEPCData::s_typeDescriptor == pType) 01773 { 01774 CEPCData * pEPCData; 01775 01776 pEPCData = (CEPCData *) pEPCParameter; 01777 my_u1v = pEPCData->getEPC(); 01778 pValue = my_u1v.m_pValue; 01779 n = (my_u1v.m_nBit + 7u) / 8u; 01780 } 01781 01782 if(NULL != pValue) 01783 { 01784 for(i = 0; i < n; i++) 01785 { 01786 if(0 < i && i%2 == 0 && 1 < bufsize) 01787 { 01788 *p++ = '-'; 01789 bufsize--; 01790 } 01791 if(bufsize > 2) 01792 { 01793 written = snprintf(p, bufsize, "%02X", pValue[i]); 01794 bufsize -= written; 01795 p+= written; 01796 } 01797 } 01798 } 01799 else 01800 { 01801 written = snprintf(p, bufsize, "%s", "---unknown-epc-data-type---"); 01802 bufsize -= written; 01803 p += written; 01804 } 01805 } 01806 else 01807 { 01808 written = snprintf(p, bufsize, "%s", "--null epc---"); 01809 bufsize -= written; 01810 p += written; 01811 } 01812 01813 // null terminate this for good practice 01814 buf[buflen-1] = '\0'; 01815 01816 return buflen - bufsize; 01817 } 01818 01819 01828 int 01829 CMyApplication::getOnePhaseAngle( 01830 CImpinjRFPhaseAngle *pRfPhase, 01831 double *out) 01832 { 01833 if(NULL != pRfPhase) 01834 { 01835 llrp_u16_t phase = pRfPhase->getPhaseAngle(); 01836 *out = ((double) phase * 360)/4096; 01837 return 1; 01838 } 01839 return 0; 01840 } 01841 01850 int 01851 CMyApplication::getOnePeakRSSI ( 01852 CImpinjPeakRSSI *pPeakRSSI, 01853 double *out) 01854 { 01855 if(NULL != pPeakRSSI) 01856 { 01857 llrp_s16_t rssival = pPeakRSSI->getRSSI(); 01858 *out = ((double) rssival / 100); 01859 return 1; 01860 } 01861 return 0; 01862 } 01863 01872 int 01873 CMyApplication::getOneTimestamp ( 01874 CParameter *pTimestamp, 01875 unsigned long long *out) 01876 { 01877 llrp_u64_t ttime; 01878 01879 if(NULL == pTimestamp) 01880 { 01881 return 0; 01882 } 01883 01884 if(&CFirstSeenTimestampUTC::s_typeDescriptor == pTimestamp->m_pType) 01885 { 01886 CFirstSeenTimestampUTC *pftutc = (CFirstSeenTimestampUTC*) pTimestamp; 01887 ttime = pftutc->getMicroseconds(); 01888 } else if (&CFirstSeenTimestampUptime::s_typeDescriptor == pTimestamp->m_pType) 01889 { 01890 CFirstSeenTimestampUptime *pftup = (CFirstSeenTimestampUptime*) pTimestamp; 01891 ttime = pftup->getMicroseconds(); 01892 } else if(&CLastSeenTimestampUTC::s_typeDescriptor == pTimestamp->m_pType) 01893 { 01894 CLastSeenTimestampUTC *pltutc = (CLastSeenTimestampUTC*) pTimestamp; 01895 ttime = pltutc->getMicroseconds(); 01896 } else if (&CLastSeenTimestampUTC::s_typeDescriptor == pTimestamp->m_pType) 01897 { 01898 CLastSeenTimestampUptime *pltup = (CLastSeenTimestampUptime*) pTimestamp; 01899 ttime = pltup->getMicroseconds(); 01900 } 01901 01902 *out = ttime; 01903 return 1; 01904 } 01905 01914 int 01915 CMyApplication::getOneAntenna ( 01916 CAntennaID *pAntenna, 01917 unsigned short *out) 01918 { 01919 if(NULL != pAntenna) 01920 { 01921 *out = pAntenna->getAntennaID(); 01922 return 1; 01923 } 01924 return 0; 01925 } 01926 01935 int 01936 CMyApplication::getOneChannelIndex ( 01937 CChannelIndex *pChannelIndex, 01938 unsigned short *out) 01939 { 01940 if(NULL != pChannelIndex) 01941 { 01942 01943 *out = pChannelIndex->getChannelIndex(); 01944 return 1; 01945 } 01946 return 0; 01947 } 01948 01957 int 01958 CMyApplication::estimateVelocity( 01959 char * epcStr, 01960 double rssi, 01961 double phase, 01962 unsigned short channelIndex, 01963 unsigned short antenna, 01964 unsigned long long time, 01965 double *outVelocity) 01966 { 01967 int retVal = 0; 01968 static char lastEpcStr[128]; 01969 static double lastrssi = 0; 01970 static double lastphase = 0; 01971 static unsigned short lastchannelindex = 0; 01972 static unsigned short lastantenna = 0; 01973 static unsigned long long lasttime = 0; 01974 01975 /* only collect a velocity sample if we have 01976 ** been on the same EPC, antenna and channel. It's best 01977 ** to run this example with only one EPC */ 01978 if((0 == strcmp(epcStr, lastEpcStr)) && 01979 (lastantenna == antenna) && 01980 (lastchannelindex == channelIndex)) 01981 { 01982 /* positive velocity is moving towards the antenna */ 01983 double phaseChangeDegrees = (phase - lastphase); 01984 double timeChangeUsec = (double) (time - lasttime); 01985 01986 /* always wrap the phase to between -180 and 180 */ 01987 while( phaseChangeDegrees < -180) 01988 phaseChangeDegrees += 360; 01989 while( phaseChangeDegrees > 180) 01990 phaseChangeDegrees -= 360; 01991 01992 /* if our phase changes close to 180 degrees, you can see we 01993 ** have an ambiguity of whether the phase advanced or retarded by 01994 ** 180 degrees (or slightly over). There is no way to tell unless 01995 ** you use more advanced techiques with multiple channels. So just 01996 ** ignore any samples where phase change is > 90 */ 01997 01998 if( abs((int) phaseChangeDegrees) <= 90) 01999 { 02000 /* We can divide these two to get degrees/usec, but it would be more 02001 ** convenient to have this in a common unit like meters/second. 02002 ** Here's a straightforward conversion. NOTE: to be exact here, we 02003 ** should use the channel index to find the channel frequency/wavelength. 02004 ** For now, I'll just assume the wavelength corresponds to mid-band at 02005 ** 0.32786885245901635 meters. The formula below eports meters per second. 02006 ** Note that 360 degrees equals only 1/2 a wavelength of motion because 02007 ** we are computing the round trip phase change. 02008 ** 02009 ** phaseChange (degrees) 1/2 wavelength 0.327 meter 1000000 usec 02010 ** --------------------- * -------------- * ---------------- * ------------ 02011 ** timeChange (usec) 360 degrees 1 wavelength 1 second 02012 ** 02013 ** which should net out to estimated tag velocity in meters/second */ 02014 02015 *outVelocity = ((phaseChangeDegrees * 0.5 * 0.327868852 * 1000000)/(360 * timeChangeUsec )); 02016 02017 retVal = 1; 02018 } 02019 } 02020 02021 /* record these for next time */ 02022 strcpy(lastEpcStr, epcStr); 02023 lastrssi = rssi; 02024 lastphase = phase; 02025 lastchannelindex = channelIndex; 02026 lastantenna = antenna; 02027 lasttime = time; 02028 02029 return retVal; 02030 } 02031 02040 void 02041 CMyApplication::printOneTagReportData ( 02042 CTagReportData * pTagReportData) 02043 { 02044 char epcBuf[128]; 02045 char aBuf[128]; 02046 char *ptr = aBuf; 02047 int len = 128; 02048 int written; 02049 unsigned long long time; 02050 unsigned short antenna, channelIndex; 02051 double rssi, phase, velocityInst; 02052 02053 /* this is static to keep a moving average of velocity */ 02054 static double velocity = 0; 02055 std::list<CParameter *>::iterator Cur; 02056 /* 02057 * Print the EPC. It could be an 96-bit EPC_96 parameter 02058 * or an variable length EPCData parameter. 02059 */ 02060 02061 CParameter * pEPCParameter = 02062 pTagReportData->getEPCParameter(); 02063 02064 /* save a copy of the EPC */ 02065 memset(epcBuf, 0x00, sizeof(epcBuf)); 02066 formatOneEPC(pEPCParameter, epcBuf, 128, ""); 02067 02068 written = snprintf(ptr, len, " epc=%s", epcBuf); 02069 ptr += written; 02070 len -= written; 02071 02072 if(getOneTimestamp(pTagReportData->getFirstSeenTimestampUTC(), &time)) 02073 { 02074 written = snprintf(ptr, len, " tm=%010u", time); 02075 ptr += written; 02076 len -= written; 02077 } 02078 02079 if(getOneChannelIndex(pTagReportData->getChannelIndex(), &channelIndex)) 02080 { 02081 written = snprintf(ptr, len, " idx=%02u", channelIndex); 02082 ptr += written; 02083 len -= written; 02084 } 02085 02086 if(getOneAntenna(pTagReportData->getAntennaID(), &antenna)) 02087 { 02088 written = snprintf(ptr, len, " ant=%01u", antenna); 02089 ptr += written; 02090 len -= written; 02091 } 02092 02093 for( 02094 Cur = pTagReportData->beginCustom(); 02095 Cur != pTagReportData->endCustom(); 02096 Cur++) 02097 { 02098 /* look for our special Impinj Tag Report Data */ 02099 if(&CImpinjRFPhaseAngle::s_typeDescriptor == (*Cur)->m_pType) 02100 { 02101 if(getOnePhaseAngle((CImpinjRFPhaseAngle*) *Cur, &phase)) 02102 { 02103 written = snprintf(ptr, len, " ph=%+04d", (int) phase); 02104 ptr += written; 02105 len -= written; 02106 } 02107 } else if (&CImpinjPeakRSSI::s_typeDescriptor == (*Cur)->m_pType) 02108 { 02109 if (getOnePeakRSSI((CImpinjPeakRSSI*) *Cur, &rssi)) 02110 { 02111 written = snprintf(ptr, len, " rs=%+3.2f", rssi); 02112 ptr += written; 02113 len -= written; 02114 } 02115 } 02116 } 02117 02118 /* Pauls Test code for looking at low level data */ 02119 02120 if(estimateVelocity(&epcBuf[0], rssi, phase, channelIndex, antenna, time, &velocityInst)) 02121 { 02122 /* keep a filtered value. Use a 1 pole IIR here for simplicity */ 02123 velocity = (6*velocity + 4*velocityInst)/10.0; 02124 02125 char *str = " - "; 02126 if (velocity > 0.25) 02127 str = "---->"; 02128 if (velocity < -0.25) 02129 str = "<----"; 02130 02131 written =snprintf(ptr, len, " vel=%+2.2f filt=%+2.2f %s", 02132 velocityInst, velocity, str); 02133 ptr += written; 02134 len -= written; 02135 02136 /* 02137 * Only print if we have a velocity estimate 02138 */ 02139 printf("%s\n", aBuf); 02140 } 02141 } 02142 02143 02157 void 02158 CMyApplication::handleReaderEventNotification ( 02159 CReaderEventNotificationData *pNtfData) 02160 { 02161 CAntennaEvent * pAntennaEvent; 02162 CReaderExceptionEvent * pReaderExceptionEvent; 02163 int nReported = 0; 02164 02165 pAntennaEvent = pNtfData->getAntennaEvent(); 02166 if(NULL != pAntennaEvent) 02167 { 02168 handleAntennaEvent(pAntennaEvent); 02169 nReported++; 02170 } 02171 02172 pReaderExceptionEvent = pNtfData->getReaderExceptionEvent(); 02173 if(NULL != pReaderExceptionEvent) 02174 { 02175 handleReaderExceptionEvent(pReaderExceptionEvent); 02176 nReported++; 02177 } 02178 02179 /* 02180 * Similarly handle other events here: 02181 * HoppingEvent 02182 * GPIEvent 02183 * ROSpecEvent 02184 * ReportBufferLevelWarningEvent 02185 * ReportBufferOverflowErrorEvent 02186 * RFSurveyEvent 02187 * AISpecEvent 02188 * ConnectionAttemptEvent 02189 * ConnectionCloseEvent 02190 * Custom 02191 */ 02192 02193 if(0 == nReported) 02194 { 02195 printf("NOTICE: Unexpected (unhandled) ReaderEvent\n"); 02196 } 02197 } 02198 02199 02211 void 02212 CMyApplication::handleAntennaEvent ( 02213 CAntennaEvent * pAntennaEvent) 02214 { 02215 EAntennaEventType eEventType; 02216 llrp_u16_t AntennaID; 02217 char * pStateStr; 02218 02219 eEventType = pAntennaEvent->getEventType(); 02220 AntennaID = pAntennaEvent->getAntennaID(); 02221 02222 switch(eEventType) 02223 { 02224 case AntennaEventType_Antenna_Disconnected: 02225 pStateStr = "disconnected"; 02226 break; 02227 02228 case AntennaEventType_Antenna_Connected: 02229 pStateStr = "connected"; 02230 break; 02231 02232 default: 02233 pStateStr = "?unknown-event?"; 02234 break; 02235 } 02236 02237 printf("NOTICE: Antenna %d is %s\n", AntennaID, pStateStr); 02238 } 02239 02240 02253 void 02254 CMyApplication::handleReaderExceptionEvent ( 02255 CReaderExceptionEvent * pReaderExceptionEvent) 02256 { 02257 llrp_utf8v_t Message; 02258 02259 Message = pReaderExceptionEvent->getMessage(); 02260 02261 if(0 < Message.m_nValue && NULL != Message.m_pValue) 02262 { 02263 printf("NOTICE: ReaderException '%.*s'\n", 02264 Message.m_nValue, Message.m_pValue); 02265 } 02266 else 02267 { 02268 printf("NOTICE: ReaderException but no message\n"); 02269 } 02270 } 02271 02272 02291 int 02292 CMyApplication::checkLLRPStatus ( 02293 CLLRPStatus * pLLRPStatus, 02294 char * pWhatStr) 02295 { 02296 /* 02297 * The LLRPStatus parameter is mandatory in all responses. 02298 * If it is missing there should have been a decode error. 02299 * This just makes sure (remember, this program is a 02300 * diagnostic and suppose to catch LTKC mistakes). 02301 */ 02302 if(NULL == pLLRPStatus) 02303 { 02304 printf("ERROR: %s missing LLRP status\n", pWhatStr); 02305 return -1; 02306 02307 } 02308 02309 /* 02310 * Make sure the status is M_Success. 02311 * If it isn't, print the error string if one. 02312 * This does not try to pretty-print the status 02313 * code. To get that, run this program with -vv 02314 * and examine the XML output. 02315 */ 02316 if(StatusCode_M_Success != pLLRPStatus->getStatusCode()) 02317 { 02318 llrp_utf8v_t ErrorDesc; 02319 02320 ErrorDesc = pLLRPStatus->getErrorDescription(); 02321 02322 if(0 == ErrorDesc.m_nValue) 02323 { 02324 printf("ERROR: %s failed, no error description given\n", 02325 pWhatStr); 02326 } 02327 else 02328 { 02329 printf("ERROR: %s failed, %.*s\n", 02330 pWhatStr, ErrorDesc.m_nValue, ErrorDesc.m_pValue); 02331 } 02332 return -2; 02333 } 02334 02335 /* 02336 * Victory. Everything is fine. 02337 */ 02338 return 0; 02339 } 02340 02341 02365 CMessage * 02366 CMyApplication::transact ( 02367 CMessage * pSendMsg) 02368 { 02369 CConnection * pConn = m_pConnectionToReader; 02370 CMessage * pRspMsg; 02371 02372 /* 02373 * Print the XML text for the outbound message if 02374 * verbosity is 2 or higher. 02375 */ 02376 if(1 < m_Verbose) 02377 { 02378 printf("\n===================================\n"); 02379 printf("INFO: Transact sending\n"); 02380 printXMLMessage(pSendMsg); 02381 } 02382 02383 /* 02384 * Send the message, expect the response of certain type. 02385 * If LLRP::CConnection::transact() returns NULL then there was 02386 * an error. In that case we try to print the error details. 02387 */ 02388 pRspMsg = pConn->transact(pSendMsg, 5000); 02389 02390 if(NULL == pRspMsg) 02391 { 02392 const CErrorDetails * pError = pConn->getTransactError(); 02393 02394 printf("ERROR: %s transact failed, %s\n", 02395 pSendMsg->m_pType->m_pName, 02396 pError->m_pWhatStr ? pError->m_pWhatStr : "no reason given"); 02397 02398 if(NULL != pError->m_pRefType) 02399 { 02400 printf("ERROR: ... reference type %s\n", 02401 pError->m_pRefType->m_pName); 02402 } 02403 02404 if(NULL != pError->m_pRefField) 02405 { 02406 printf("ERROR: ... reference field %s\n", 02407 pError->m_pRefField->m_pName); 02408 } 02409 02410 return NULL; 02411 } 02412 02413 /* 02414 * Print the XML text for the inbound message if 02415 * verbosity is 2 or higher. 02416 */ 02417 if(1 < m_Verbose) 02418 { 02419 printf("\n- - - - - - - - - - - - - - - - - -\n"); 02420 printf("INFO: Transact received response\n"); 02421 printXMLMessage(pRspMsg); 02422 } 02423 02424 /* 02425 * If it is an ERROR_MESSAGE (response from reader 02426 * when it can't understand the request), tattle 02427 * and declare defeat. 02428 */ 02429 if(&CERROR_MESSAGE::s_typeDescriptor == pRspMsg->m_pType) 02430 { 02431 const CTypeDescriptor * pResponseType; 02432 02433 pResponseType = pSendMsg->m_pType->m_pResponseType; 02434 02435 printf("ERROR: Received ERROR_MESSAGE instead of %s\n", 02436 pResponseType->m_pName); 02437 delete pRspMsg; 02438 pRspMsg = NULL; 02439 } 02440 02441 return pRspMsg; 02442 } 02443 02444 02469 CMessage * 02470 CMyApplication::recvMessage ( 02471 int nMaxMS) 02472 { 02473 CConnection * pConn = m_pConnectionToReader; 02474 CMessage * pMessage; 02475 02476 /* 02477 * Receive the message subject to a time limit 02478 */ 02479 pMessage = pConn->recvMessage(nMaxMS); 02480 02481 /* 02482 * If LLRP::CConnection::recvMessage() returns NULL then there was 02483 * an error. In that case we try to print the error details. 02484 */ 02485 if(NULL == pMessage) 02486 { 02487 const CErrorDetails * pError = pConn->getRecvError(); 02488 02489 /* don't warn on timeout since this is a polling example */ 02490 if(pError->m_eResultCode != RC_RecvTimeout) 02491 { 02492 printf("ERROR: recvMessage failed, %s\n", 02493 pError->m_pWhatStr ? pError->m_pWhatStr : "no reason given"); 02494 } 02495 02496 if(NULL != pError->m_pRefType) 02497 { 02498 printf("ERROR: ... reference type %s\n", 02499 pError->m_pRefType->m_pName); 02500 } 02501 02502 if(NULL != pError->m_pRefField) 02503 { 02504 printf("ERROR: ... reference field %s\n", 02505 pError->m_pRefField->m_pName); 02506 } 02507 02508 return NULL; 02509 } 02510 02511 /* 02512 * Print the XML text for the inbound message if 02513 * verbosity is 2 or higher. 02514 */ 02515 if(1 < m_Verbose) 02516 { 02517 printf("\n===================================\n"); 02518 printf("INFO: Message received\n"); 02519 printXMLMessage(pMessage); 02520 } 02521 02522 return pMessage; 02523 } 02524 02525 02543 int 02544 CMyApplication::sendMessage ( 02545 CMessage * pSendMsg) 02546 { 02547 CConnection * pConn = m_pConnectionToReader; 02548 02549 /* 02550 * Print the XML text for the outbound message if 02551 * verbosity is 2 or higher. 02552 */ 02553 if(1 < m_Verbose) 02554 { 02555 printf("\n===================================\n"); 02556 printf("INFO: Sending\n"); 02557 printXMLMessage(pSendMsg); 02558 } 02559 02560 /* 02561 * If LLRP::CConnection::sendMessage() returns other than RC_OK 02562 * then there was an error. In that case we try to print 02563 * the error details. 02564 */ 02565 if(RC_OK != pConn->sendMessage(pSendMsg)) 02566 { 02567 const CErrorDetails * pError = pConn->getSendError(); 02568 02569 printf("ERROR: %s sendMessage failed, %s\n", 02570 pSendMsg->m_pType->m_pName, 02571 pError->m_pWhatStr ? pError->m_pWhatStr : "no reason given"); 02572 02573 if(NULL != pError->m_pRefType) 02574 { 02575 printf("ERROR: ... reference type %s\n", 02576 pError->m_pRefType->m_pName); 02577 } 02578 02579 if(NULL != pError->m_pRefField) 02580 { 02581 printf("ERROR: ... reference field %s\n", 02582 pError->m_pRefField->m_pName); 02583 } 02584 02585 return -1; 02586 } 02587 02588 /* 02589 * Victory 02590 */ 02591 return 0; 02592 } 02593 02594 02608 void 02609 CMyApplication::printXMLMessage ( 02610 CMessage * pMessage) 02611 { 02612 char aBuf[100*1024]; 02613 02614 /* 02615 * Convert the message to an XML string. 02616 * This fills the buffer with either the XML string 02617 * or an error message. The return value could 02618 * be checked. 02619 */ 02620 02621 pMessage->toXMLString(aBuf, sizeof aBuf); 02622 02623 /* 02624 * Print the XML Text to the standard output. 02625 */ 02626 printf("%s", aBuf); 02627 }