/* ***************************************************************************** * * * IMPINJ CONFIDENTIAL AND PROPRIETARY * * * * This source code is the sole property of Impinj, Inc. Reproduction or * * utilization of this source code in whole or in part is forbidden without * * the prior written consent of Impinj, Inc. * * * * (c) Copyright Impinj, Inc. 2007,2008. All rights reserved. * * * *****************************************************************************/ /** ***************************************************************************** ** ** @file docsample1.c ** ** @brief Simple example of LTKC that talks to an actual reader ** ** This is sometimes called the "LLRP Hello World" program ** ** The steps: ** - Connect to the reader (TCP) ** - Make sure the connection status is good ** - Clear (scrub) the reader configuration ** - Add and enable a ROSpec that does a simple operation ** - Uses mostly default settings except for ROReportSpec ** - Uses all antennas ** - Starts on command ** - Runs for 5 seconds ** - Reports all accumulated tag observations ** - Run the ROSpec 5 times ** - Clear (scrub) the reader configuration ** ** This program can be run with zero, one, or two verbose options (-v). ** no -v -- Only prints the tag report and errors ** -v -- Also prints one line progress messages ** -vv -- Also prints all LLRP messages as XML text ** ** IMPORTANT: ** This example was written before best practices were determined. ** Most of the command messages (including subparameters) are ** composed using local (auto) variables with initializers. ** It has been determined that this approach has drawbacks. ** Best practice is to use the constructor and accessors. ** ** Function deleteAllROSpecs() was recoded to demonstrate ** good technique. Someday we might be able to fix the rest. ** *****************************************************************************/ #include #include "ltkc.h" #include "impinj_ltkc.h" /* * BEGIN forward declarations */ int main ( int ac, char * av[]); void usage ( char * pProgName); int run ( const char * pReaderHostName); int checkConnectionStatus (void); int scrubConfiguration (void); int enableImpinjExtensions (void); int resetConfigurationToFactoryDefaults (void); int deleteAllROSpecs (void); int addROSpec (void); int enableROSpec (void); int startROSpec (void); int awaitAndPrintReport (void); void printTagReportData ( LLRP_tSRO_ACCESS_REPORT * pRO_ACCESS_REPORT); void printOneTagReportData ( LLRP_tSTagReportData * pTagReportData); void handleReaderEventNotification ( LLRP_tSReaderEventNotificationData *pNtfData); void handleAntennaEvent ( LLRP_tSAntennaEvent * pAntennaEvent); void handleReaderExceptionEvent ( LLRP_tSReaderExceptionEvent * pReaderExceptionEvent); int checkLLRPStatus ( LLRP_tSLLRPStatus * pLLRPStatus, char * pWhatStr); LLRP_tSMessage * transact ( LLRP_tSMessage * pSendMsg); LLRP_tSMessage * recvMessage ( int nMaxMS); int sendMessage ( LLRP_tSMessage * pSendMsg); void freeMessage ( LLRP_tSMessage * pMessage); void printXMLMessage ( LLRP_tSMessage * pMessage); /* * END forward declarations */ /* * Global variables */ /** Verbose level, incremented by each -v on command line */ int g_Verbose; /** Connection to the LLRP reader */ LLRP_tSConnection * g_pConnectionToReader; /** ***************************************************************************** ** ** @brief Command main routine ** ** Command synopsis: ** ** dx201 READERHOSTNAME ** ** @exitcode 0 Everything *seemed* to work. ** 1 Bad usage ** 2 Run failed ** *****************************************************************************/ int main ( int ac, char * av[]) { char * pReaderHostName; int rc; /* * Process comand arguments, determine reader name * and verbosity level. */ if(ac == 2) { pReaderHostName = av[1]; } else if(ac == 3) { char * p = av[1]; while(*p) { switch(*p++) { case '-': /* linux conventional option warn char */ case '/': /* Windows/DOS conventional option warn char */ break; case 'v': case 'V': g_Verbose++; break; default: usage(av[0]); /* no return */ break; } } pReaderHostName = av[2]; } else { usage(av[0]); /* no return */ } /* * Run application, capture return value for exit status */ rc = run(pReaderHostName); printf("INFO: Done\n"); /* * Exit with the right status. */ if(0 == rc) { exit(0); } else { exit(2); } /*NOTREACHED*/ } /** ***************************************************************************** ** ** @brief Print usage message and exit ** ** @param[in] nProgName Program name string ** ** @return none, exits ** *****************************************************************************/ void usage ( char * pProgName) { printf("Usage: %s [-v] READERHOSTNAME\n", pProgName); printf("\n"); printf("Each -v increases verbosity level\n"); exit(1); } /** ***************************************************************************** ** ** @brief Run the application ** ** The steps: ** - Instantiate connection ** - Connect to LLRP reader (TCP) ** - Make sure the connection status is good ** - Clear (scrub) the reader configuration ** - Configure for what we want to do ** - Run inventory operation 5 times ** - Again, clear (scrub) the reader configuration ** - Disconnect from reader ** - Destruct connection ** ** @param[in] pReaderHostName String with reader name ** ** @return 0 Everything worked. ** -1 Failed allocation of type registry ** -2 Failed construction of connection ** -3 Could not connect to reader ** 1 Reader connection status bad ** 2 Cleaning reader config failed ** 3 Enable Impinj Extensions failed ** 4 Adding ROSpec failed ** 5 Enabling ROSpec failed ** 6 Something went wrong running the ROSpec ** *****************************************************************************/ int run ( const char * pReaderHostName) { LLRP_tSTypeRegistry * pTypeRegistry; LLRP_tSConnection * pConn; int rc; /* * Allocate the type registry. This is needed * by the connection to decode. */ pTypeRegistry = LLRP_getTheTypeRegistry(); if(NULL == pTypeRegistry) { printf("ERROR: getTheTypeRegistry failed\n"); return -1; } /* * Enroll impinj extension types into the * type registry, in praparation for using * Impinj extension params. */ LLRP_enrollImpinjTypesIntoRegistry(pTypeRegistry); /* * Construct a connection (LLRP_tSConnection). * Using a 32kb max frame size for send/recv. * The connection object is ready for business * but not actually connected to the reader yet. */ pConn = LLRP_Conn_construct(pTypeRegistry, 32u*1024u); if(NULL == pConn) { printf("ERROR: Conn_construct failed\n"); return -2; } /* * Open the connection to the reader */ if(g_Verbose) { printf("INFO: Connecting to %s....\n", pReaderHostName); } rc = LLRP_Conn_openConnectionToReader(pConn, pReaderHostName); if(0 != rc) { printf("ERROR: connect: %s (%d)\n", pConn->pConnectErrorStr, rc); LLRP_Conn_destruct(pConn); return -3; } /* * Record the pointer to the connection object so other * routines can use it. */ g_pConnectionToReader = pConn; if(g_Verbose) { printf("INFO: Connected, checking status....\n"); } /* * Commence the sequence and check for errors as we go. * See comments for each routine for details. * Each routine prints messages per verbose level. */ rc = 1; if(0 == checkConnectionStatus()) { rc = 2; if(0 == scrubConfiguration()) { rc = 3; if(0 == enableImpinjExtensions()) { rc = 4; if(0 == addROSpec()) { rc = 5; if(0 == enableROSpec()) { int i; rc = 6; for(i = 1; i <= 5; i++) { printf("INFO: Starting run %d ================\n", i); if(0 != startROSpec()) { /* already tattled */ break; } if(0 != awaitAndPrintReport()) { /* already tattled */ break; } } if(i == 5) { rc = 0; } } } } /* * After we're done, try to leave the reader * in a clean state for next use. This is best * effort and no checking of the result is done. */ if(g_Verbose) { printf("INFO: Clean up reader configuration...\n"); } scrubConfiguration(); } } if(g_Verbose) { printf("INFO: Finished\n"); } /* * Close the connection and release its resources */ LLRP_Conn_closeConnectionToReader(pConn); LLRP_Conn_destruct(pConn); /* * Done with the registry. */ LLRP_TypeRegistry_destruct(pTypeRegistry); /* * When we get here all allocated memory should have been deallocated. */ return rc; } /** ***************************************************************************** ** ** @brief Await and check the connection status message from the reader ** ** We are expecting a READER_EVENT_NOTIFICATION message that ** tells us the connection is OK. The reader is suppose to ** send the message promptly upon connection. ** ** If there is already another LLRP connection to the ** reader we'll get a bad Status. ** ** The message should be something like: ** ** ** ** ** 1184491439614224 ** ** ** Success ** ** ** ** ** @return ==0 Everything OK ** !=0 Something went wrong ** *****************************************************************************/ int checkConnectionStatus (void) { LLRP_tSMessage * pMessage; LLRP_tSREADER_EVENT_NOTIFICATION *pNtf; LLRP_tSReaderEventNotificationData *pNtfData; LLRP_tSConnectionAttemptEvent *pEvent; /* * Expect the notification within 10 seconds. * It is suppose to be the very first message sent. */ pMessage = recvMessage(10000); /* * recvMessage() returns NULL if something went wrong. */ if(NULL == pMessage) { /* recvMessage() already tattled. */ goto fail; } /* * Check to make sure the message is of the right type. * The type label (pointer) in the message should be * the type descriptor for READER_EVENT_NOTIFICATION. */ if(&LLRP_tdREADER_EVENT_NOTIFICATION != pMessage->elementHdr.pType) { goto fail; } /* * Now that we are sure it is a READER_EVENT_NOTIFICATION, * traverse to the ReaderEventNotificationData parameter. */ pNtf = (LLRP_tSREADER_EVENT_NOTIFICATION *) pMessage; pNtfData = pNtf->pReaderEventNotificationData; if(NULL == pNtfData) { goto fail; } /* * The ConnectionAttemptEvent parameter must be present. */ pEvent = pNtfData->pConnectionAttemptEvent; if(NULL == pEvent) { goto fail; } /* * The status in the ConnectionAttemptEvent parameter * must indicate connection success. */ if(LLRP_ConnectionAttemptStatusType_Success != pEvent->eStatus) { goto fail; } /* * Done with the message */ freeMessage(pMessage); if(g_Verbose) { printf("INFO: Connection status OK\n"); } /* * Victory. */ return 0; fail: /* * Something went wrong. Tattle. Clean up. Return error. */ printf("ERROR: checkConnectionStatus failed\n"); freeMessage(pMessage); return -1; } /** ***************************************************************************** ** ** @brief Scrub the reader configuration ** ** The steps: ** - Try to reset configuration to factory defaults, ** this feature is optional and may not be supported ** by the reader. ** - Delete all ROSpecs ** ** @return ==0 Everything OK ** !=0 Something went wrong ** *****************************************************************************/ int scrubConfiguration (void) { if(0 != resetConfigurationToFactoryDefaults()) { return -1; } if(0 != deleteAllROSpecs()) { return -2; } return 0; } /** ***************************************************************************** ** ** @brief Enable Impinj extensions using IMPINJ_ENABLE_EXTENSIONS message ** ** This routine sends an IMPINJ_ENABLE_EXTENSIONS message. It then waits for ** the IMPINJ_ENABLE_EXTENSIONS_RESPONSE message and checks everything is OK. ** ** @return ==0 Everything OK ** !=0 Something went wrong ** *****************************************************************************/ int enableImpinjExtensions (void) { LLRP_tSIMPINJ_ENABLE_EXTENSIONS Cmd = { .hdr.elementHdr.pType = &LLRP_tdIMPINJ_ENABLE_EXTENSIONS, .hdr.MessageID = 103, }; LLRP_tSMessage * pRspMsg; LLRP_tSIMPINJ_ENABLE_EXTENSIONS_RESPONSE *pRsp; /* * Send the message, expect the response of certain type */ pRspMsg = transact(&Cmd.hdr); if(NULL == pRspMsg) { /* transact already tattled */ return -1; } /* * Cast to a LLRP_tSIMPINJ_ENABLE_EXTENSIONS_RESPONSE message. */ pRsp = (LLRP_tSIMPINJ_ENABLE_EXTENSIONS_RESPONSE *) pRspMsg; /* * Check the LLRPStatus parameter. */ if(0 != checkLLRPStatus(pRsp->pLLRPStatus, "enableImpinjExtensions")) { /* checkLLRPStatus already tattled */ freeMessage(pRspMsg); return -1; } /* * Done with the response message. */ freeMessage(pRspMsg); /* * Tattle progress, maybe */ if(g_Verbose) { printf("INFO: ImpinjExtension enabled\n"); } /* * Victory. */ return 0; } /** ***************************************************************************** ** ** @brief Send a SET_READER_CONFIG message that resets the ** reader to factory defaults. ** ** NB: The ResetToFactoryDefault semantics vary between readers. ** It might have no effect because it is optional. ** ** The message is: ** ** ** 1 ** ** ** @return ==0 Everything OK ** !=0 Something went wrong ** *****************************************************************************/ int resetConfigurationToFactoryDefaults (void) { LLRP_tSSET_READER_CONFIG Cmd = { .hdr.elementHdr.pType = &LLRP_tdSET_READER_CONFIG, .hdr.MessageID = 101, .ResetToFactoryDefault = 1 }; LLRP_tSMessage * pRspMsg; LLRP_tSSET_READER_CONFIG_RESPONSE *pRsp; /* * Send the message, expect the response of certain type */ pRspMsg = transact(&Cmd.hdr); if(NULL == pRspMsg) { /* transact already tattled */ return -1; } /* * Cast to a SET_READER_CONFIG_RESPONSE message. */ pRsp = (LLRP_tSSET_READER_CONFIG_RESPONSE *) pRspMsg; /* * Check the LLRPStatus parameter. */ if(0 != checkLLRPStatus(pRsp->pLLRPStatus, "resetConfigurationToFactoryDefaults")) { /* checkLLRPStatus already tattled */ freeMessage(pRspMsg); return -1; } /* * Done with the response message. */ freeMessage(pRspMsg); /* * Tattle progress, maybe */ if(g_Verbose) { printf("INFO: Configuration reset to factory defaults\n"); } /* * Victory. */ return 0; } /** ***************************************************************************** ** ** @brief Delete all ROSpecs using DELETE_ROSPEC message ** ** Per the spec, the DELETE_ROSPEC message contains an ROSpecID ** of 0 to indicate we want all ROSpecs deleted. ** ** The message is ** ** ** 0 ** ** ** @return ==0 Everything OK ** !=0 Something went wrong ** ** IMPORANT: ** The coding of this function demonstrates best practices. ** Please see IMPORTANT comment at the top of this file. ** *****************************************************************************/ int deleteAllROSpecs (void) { LLRP_tSDELETE_ROSPEC * pCmd; LLRP_tSMessage * pCmdMsg; LLRP_tSMessage * pRspMsg; LLRP_tSDELETE_ROSPEC_RESPONSE *pRsp; /* * Compose the command message */ pCmd = LLRP_DELETE_ROSPEC_construct(); pCmdMsg = &pCmd->hdr; LLRP_Message_setMessageID(pCmdMsg, 102); LLRP_DELETE_ROSPEC_setROSpecID(pCmd, 0); /* All */ /* * Send the message, expect the response of certain type */ pRspMsg = transact(pCmdMsg); /* * Done with the command message */ freeMessage(pCmdMsg); /* * transact() returns NULL if something went wrong. */ if(NULL == pRspMsg) { /* transact already tattled */ return -1; } /* * Cast to a DELETE_ROSPEC_RESPONSE message. */ pRsp = (LLRP_tSDELETE_ROSPEC_RESPONSE *) pRspMsg; /* * Check the LLRPStatus parameter. */ if(0 != checkLLRPStatus(pRsp->pLLRPStatus, "deleteAllROSpecs")) { /* checkLLRPStatus already tattled */ freeMessage(pRspMsg); return -1; } /* * Done with the response message. */ freeMessage(pRspMsg); /* * Tattle progress, maybe */ if(g_Verbose) { printf("INFO: All ROSpecs are deleted\n"); } /* * Victory. */ return 0; } /** ***************************************************************************** ** ** @brief Add our ROSpec using ADD_ROSPEC message ** ** This ROSpec waits for a START_ROSPEC message, ** then takes inventory on all antennas for 5 seconds. ** ** The tag report is generated after the ROSpec is done. ** ** This example is deliberately streamlined. ** Nothing here configures the antennas, RF, or Gen2. ** The current defaults are used. Remember we just reset ** the reader to factory defaults (above). Normally an ** application would be more precise in configuring the ** reader and in its ROSpecs. ** ** Experience suggests that typical ROSpecs are about ** double this in size. ** ** The message is ** ** ** ** 123 ** 0 ** Disabled ** ** ** Null ** ** ** Null ** 0 ** ** ** ** 0 ** ** Duration ** 5000 ** ** ** 1234 ** EPCGlobalClass1Gen2 ** ** ** ** Upon_N_Tags_Or_End_Of_ROSpec ** 0 ** ** 0 ** 0 ** 0 ** 0 ** 0 ** 0 ** 0 ** 0 ** 0 ** 0 ** ** ** ** ** ** @return ==0 Everything OK ** !=0 Something went wrong ** *****************************************************************************/ int addROSpec (void) { LLRP_tSROSpecStartTrigger ROSpecStartTrigger = { .hdr.elementHdr.pType = &LLRP_tdROSpecStartTrigger, .eROSpecStartTriggerType = LLRP_ROSpecStartTriggerType_Null, }; LLRP_tSROSpecStopTrigger ROSpecStopTrigger = { .hdr.elementHdr.pType = &LLRP_tdROSpecStopTrigger, .eROSpecStopTriggerType = LLRP_ROSpecStopTriggerType_Null, .DurationTriggerValue = 0 /* n/a */ }; LLRP_tSROBoundarySpec ROBoundarySpec = { .hdr.elementHdr.pType = &LLRP_tdROBoundarySpec, .pROSpecStartTrigger = &ROSpecStartTrigger, .pROSpecStopTrigger = &ROSpecStopTrigger, }; llrp_u16_t AntennaIDs[1] = { 0 }; /* All */ LLRP_tSAISpecStopTrigger AISpecStopTrigger = { .hdr.elementHdr.pType = &LLRP_tdAISpecStopTrigger, .eAISpecStopTriggerType = LLRP_AISpecStopTriggerType_Duration, .DurationTrigger = 5000, }; LLRP_tSInventoryParameterSpec InventoryParameterSpec = { .hdr.elementHdr.pType = &LLRP_tdInventoryParameterSpec, .InventoryParameterSpecID = 1234, .eProtocolID = LLRP_AirProtocols_EPCGlobalClass1Gen2, }; LLRP_tSAISpec AISpec = { .hdr.elementHdr.pType = &LLRP_tdAISpec, .AntennaIDs = { .nValue = 1, .pValue = AntennaIDs }, .pAISpecStopTrigger = &AISpecStopTrigger, .listInventoryParameterSpec = &InventoryParameterSpec, }; LLRP_tSTagReportContentSelector TagReportContentSelector = { .hdr.elementHdr.pType = &LLRP_tdTagReportContentSelector, .EnableROSpecID = 0, .EnableSpecIndex = 0, .EnableInventoryParameterSpecID = 0, .EnableAntennaID = 0, .EnableChannelIndex = 0, .EnablePeakRSSI = 0, .EnableFirstSeenTimestamp = 0, .EnableLastSeenTimestamp = 0, .EnableTagSeenCount = 0, .EnableAccessSpecID = 0, }; LLRP_tSROReportSpec ROReportSpec = { .hdr.elementHdr.pType = &LLRP_tdROReportSpec, .eROReportTrigger = LLRP_ROReportTriggerType_Upon_N_Tags_Or_End_Of_ROSpec, .N = 0, .pTagReportContentSelector = &TagReportContentSelector, }; LLRP_tSROSpec ROSpec = { .hdr.elementHdr.pType = &LLRP_tdROSpec, .ROSpecID = 123, .Priority = 0, .eCurrentState = LLRP_ROSpecState_Disabled, .pROBoundarySpec = &ROBoundarySpec, .listSpecParameter = &AISpec.hdr, .pROReportSpec = &ROReportSpec, }; LLRP_tSADD_ROSPEC Cmd = { .hdr.elementHdr.pType = &LLRP_tdADD_ROSPEC, .hdr.MessageID = 201, .pROSpec = &ROSpec, }; LLRP_tSMessage * pRspMsg; LLRP_tSADD_ROSPEC_RESPONSE *pRsp; /* * Send the message, expect the response of certain type */ pRspMsg = transact(&Cmd.hdr); if(NULL == pRspMsg) { /* transact already tattled */ return -1; } /* * Cast to a ADD_ROSPEC_RESPONSE message. */ pRsp = (LLRP_tSADD_ROSPEC_RESPONSE *) pRspMsg; /* * Check the LLRPStatus parameter. */ if(0 != checkLLRPStatus(pRsp->pLLRPStatus, "addROSpec")) { /* checkLLRPStatus already tattled */ freeMessage(pRspMsg); return -1; } /* * Done with the response message. */ freeMessage(pRspMsg); /* * Tattle progress, maybe */ if(g_Verbose) { printf("INFO: ROSpec added\n"); } /* * Victory. */ return 0; } /** ***************************************************************************** ** ** @brief Enable our ROSpec using ENABLE_ROSPEC message ** ** Enable the ROSpec that was added above. ** ** The message we send is: ** ** 123 ** ** ** @return ==0 Everything OK ** !=0 Something went wrong ** *****************************************************************************/ int enableROSpec (void) { LLRP_tSENABLE_ROSPEC Cmd = { .hdr.elementHdr.pType = &LLRP_tdENABLE_ROSPEC, .hdr.MessageID = 202, .ROSpecID = 123, }; LLRP_tSMessage * pRspMsg; LLRP_tSENABLE_ROSPEC_RESPONSE *pRsp; /* * Send the message, expect the response of certain type */ pRspMsg = transact(&Cmd.hdr); if(NULL == pRspMsg) { /* transact already tattled */ return -1; } /* * Cast to a ENABLE_ROSPEC_RESPONSE message. */ pRsp = (LLRP_tSENABLE_ROSPEC_RESPONSE *) pRspMsg; /* * Check the LLRPStatus parameter. */ if(0 != checkLLRPStatus(pRsp->pLLRPStatus, "enableROSpec")) { /* checkLLRPStatus already tattled */ freeMessage(pRspMsg); return -1; } /* * Done with the response message. */ freeMessage(pRspMsg); /* * Tattle progress, maybe */ if(g_Verbose) { printf("INFO: ROSpec enabled\n"); } /* * Victory. */ return 0; } /** ***************************************************************************** ** ** @brief Start our ROSpec using START_ROSPEC message ** ** Start the ROSpec that was added above. ** ** The message we send is: ** ** 123 ** ** ** @return ==0 Everything OK ** !=0 Something went wrong ** *****************************************************************************/ int startROSpec (void) { LLRP_tSSTART_ROSPEC Cmd = { .hdr.elementHdr.pType = &LLRP_tdSTART_ROSPEC, .hdr.MessageID = 202, .ROSpecID = 123, }; LLRP_tSMessage * pRspMsg; LLRP_tSSTART_ROSPEC_RESPONSE *pRsp; /* * Send the message, expect the response of certain type */ pRspMsg = transact(&Cmd.hdr); if(NULL == pRspMsg) { /* transact already tattled */ return -1; } /* * Cast to a START_ROSPEC_RESPONSE message. */ pRsp = (LLRP_tSSTART_ROSPEC_RESPONSE *) pRspMsg; /* * Check the LLRPStatus parameter. */ if(0 != checkLLRPStatus(pRsp->pLLRPStatus, "startROSpec")) { /* checkLLRPStatus already tattled */ freeMessage(pRspMsg); return -1; } /* * Done with the response message. */ freeMessage(pRspMsg); /* * Tattle progress, maybe */ if(g_Verbose) { printf("INFO: ROSpec started\n"); } /* * Victory. */ return 0; } /** ***************************************************************************** ** ** @brief Receive and print the RO_ACCESS_REPORT ** ** Receive messages until an RO_ACCESS_REPORT is received. ** Time limit is 7 seconds. We expect a report within 5 seconds. ** ** This shows how to determine the type of a received message. ** ** @return ==0 Everything OK ** !=0 Something went wrong ** *****************************************************************************/ int awaitAndPrintReport (void) { int bDone = 0; int retVal = 0; /* * Keep receiving messages until done or until * something bad happens. */ while(!bDone) { LLRP_tSMessage * pMessage; const LLRP_tSTypeDescriptor *pType; /* * Wait up to 7 seconds for a message. The report * should occur within 5 seconds. */ pMessage = recvMessage(7000); if(NULL == pMessage) { /* * Did not receive a message within a reasonable * amount of time. recvMessage() already tattled */ retVal = -2; bDone = 1; continue; } /* * What happens depends on what kind of message * received. Use the type label (pType) to * discriminate message types. */ pType = pMessage->elementHdr.pType; /* * Is it a tag report? If so, print it out. */ if(&LLRP_tdRO_ACCESS_REPORT == pType) { LLRP_tSRO_ACCESS_REPORT *pNtf; pNtf = (LLRP_tSRO_ACCESS_REPORT *) pMessage; printTagReportData(pNtf); bDone = 1; retVal = 0; } /* * Is it a reader event? This example only recognizes * AntennaEvents. */ else if(&LLRP_tdREADER_EVENT_NOTIFICATION == pType) { LLRP_tSREADER_EVENT_NOTIFICATION *pNtf; LLRP_tSReaderEventNotificationData *pNtfData; pNtf = (LLRP_tSREADER_EVENT_NOTIFICATION *) pMessage; pNtfData = LLRP_READER_EVENT_NOTIFICATION_getReaderEventNotificationData( pNtf); if(NULL != pNtfData) { handleReaderEventNotification(pNtfData); } else { /* * This should never happen. Using continue * to keep indent depth down. */ printf("WARNING: READER_EVENT_NOTIFICATION without data\n"); } } /* * Hmmm. Something unexpected. Just tattle and keep going. */ else { printf("WARNING: Ignored unexpected message during monitor: %s\n", pType->pName); } /* * Done with the received message */ freeMessage(pMessage); } return retVal; } /** ***************************************************************************** ** ** @brief Helper routine to print a tag report ** ** The report is printed in list order, which is arbitrary. ** ** TODO: It would be cool to sort the list by EPC and antenna, ** then print it. ** ** @return void ** *****************************************************************************/ void printTagReportData ( LLRP_tSRO_ACCESS_REPORT * pRO_ACCESS_REPORT) { LLRP_tSTagReportData * pTagReportData; unsigned int nEntry = 0; /* * Loop through and count the number of entries */ for( pTagReportData = pRO_ACCESS_REPORT->listTagReportData; NULL != pTagReportData; pTagReportData = (LLRP_tSTagReportData *) pTagReportData->hdr.pNextSubParameter) { nEntry++; } printf("INFO: %u tag report entries\n", nEntry); /* * Loop through again and print each entry. */ for( pTagReportData = pRO_ACCESS_REPORT->listTagReportData; NULL != pTagReportData; pTagReportData = (LLRP_tSTagReportData *) pTagReportData->hdr.pNextSubParameter) { printOneTagReportData(pTagReportData); } } /** ***************************************************************************** ** ** @brief Helper routine to print one tag report entry on one line ** ** @return void ** *****************************************************************************/ void printOneTagReportData ( LLRP_tSTagReportData * pTagReportData) { const LLRP_tSTypeDescriptor *pType; char aBuf[100*1024]; /* * Print the EPC. It could be an 96-bit EPC_96 parameter * or an variable length EPCData parameter. */ if(NULL != pTagReportData->pEPCParameter) { char * p = aBuf; llrp_u8_t * pValue = NULL; unsigned int n, i; pType = pTagReportData->pEPCParameter->elementHdr.pType; if(&LLRP_tdEPC_96 == pType) { LLRP_tSEPC_96 * pE96; pE96 = (LLRP_tSEPC_96 *) pTagReportData->pEPCParameter; pValue = pE96->EPC.aValue; n = 12u; } else if(&LLRP_tdEPCData == pType) { LLRP_tSEPCData *pEPCData; pEPCData = (LLRP_tSEPCData *) pTagReportData->pEPCParameter; pValue = pEPCData->EPC.pValue; n = (pEPCData->EPC.nBit + 7u) / 8u; } if(NULL != pValue) { for(i = 0; i < n; i++) { if(0 < i && i%2 == 0) { *p++ = '-'; } sprintf(p, "%02X", pValue[i]); while(*p) p++; } } else { strcpy(aBuf, "---unknown-epc-data-type---"); } } else { strcpy(aBuf, "---missing-epc-data---"); } printf("%-32s", aBuf); /* * End of line */ printf("\n"); } /** ***************************************************************************** ** ** @brief Handle a ReaderEventNotification ** ** Handle the payload of a READER_EVENT_NOTIFICATION message. ** This routine simply dispatches to handlers of specific ** event types. ** ** @return void ** *****************************************************************************/ void handleReaderEventNotification ( LLRP_tSReaderEventNotificationData *pNtfData) { LLRP_tSAntennaEvent * pAntennaEvent; LLRP_tSReaderExceptionEvent *pReaderExceptionEvent; int nReported = 0; pAntennaEvent = LLRP_ReaderEventNotificationData_getAntennaEvent(pNtfData); if(NULL != pAntennaEvent) { handleAntennaEvent(pAntennaEvent); nReported++; } pReaderExceptionEvent = LLRP_ReaderEventNotificationData_getReaderExceptionEvent(pNtfData); if(NULL != pReaderExceptionEvent) { handleReaderExceptionEvent(pReaderExceptionEvent); nReported++; } /* * Similarly handle other events here: * HoppingEvent * GPIEvent * ROSpecEvent * ReportBufferLevelWarningEvent * ReportBufferOverflowErrorEvent * RFSurveyEvent * AISpecEvent * ConnectionAttemptEvent * ConnectionCloseEvent * Custom */ if(0 == nReported) { printf("NOTICE: Unexpected (unhandled) ReaderEvent\n"); } } /** ***************************************************************************** ** ** @brief Handle an AntennaEvent ** ** An antenna was disconnected or (re)connected. Tattle. ** ** @return void ** *****************************************************************************/ void handleAntennaEvent ( LLRP_tSAntennaEvent * pAntennaEvent) { LLRP_tEAntennaEventType eEventType; llrp_u16_t AntennaID; char * pStateStr; eEventType = LLRP_AntennaEvent_getEventType(pAntennaEvent); AntennaID = LLRP_AntennaEvent_getAntennaID(pAntennaEvent); switch(eEventType) { case LLRP_AntennaEventType_Antenna_Disconnected: pStateStr = "disconnected"; break; case LLRP_AntennaEventType_Antenna_Connected: pStateStr = "connected"; break; default: pStateStr = "?unknown-event?"; break; } printf("NOTICE: Antenna %d is %s\n", AntennaID, pStateStr); } /** ***************************************************************************** ** ** @brief Handle a ReaderExceptionEvent ** ** Something has gone wrong. There are lots of details but ** all this does is print the message, if one. ** ** @return void ** *****************************************************************************/ void handleReaderExceptionEvent ( LLRP_tSReaderExceptionEvent * pReaderExceptionEvent) { llrp_utf8v_t Message; Message = LLRP_ReaderExceptionEvent_getMessage(pReaderExceptionEvent); if(0 < Message.nValue && NULL != Message.pValue) { printf("NOTICE: ReaderException '%.*s'\n", Message.nValue, Message.pValue); } else { printf("NOTICE: ReaderException but no message\n"); } } /** ***************************************************************************** ** ** @brief Helper routine to check an LLRPStatus parameter ** and tattle on errors ** ** Helper routine to interpret the LLRPStatus subparameter ** that is in all responses. It tattles on an error, if one, ** and tries to safely provide details. ** ** This simplifies the code, above, for common check/tattle ** sequences. ** ** @return ==0 Everything OK ** !=0 Something went wrong, already tattled ** *****************************************************************************/ int checkLLRPStatus ( LLRP_tSLLRPStatus * pLLRPStatus, char * pWhatStr) { /* * The LLRPStatus parameter is mandatory in all responses. * If it is missing there should have been a decode error. * This just makes sure (remember, this program is a * diagnostic and suppose to catch LTKC mistakes). */ if(NULL == pLLRPStatus) { printf("ERROR: %s missing LLRP status\n", pWhatStr); return -1; } /* * Make sure the status is M_Success. * If it isn't, print the error string if one. * This does not try to pretty-print the status * code. To get that, run this program with -vv * and examine the XML output. */ if(LLRP_StatusCode_M_Success != pLLRPStatus->eStatusCode) { if(0 == pLLRPStatus->ErrorDescription.nValue) { printf("ERROR: %s failed, no error description given\n", pWhatStr); } else { printf("ERROR: %s failed, %.*s\n", pWhatStr, pLLRPStatus->ErrorDescription.nValue, pLLRPStatus->ErrorDescription.pValue); } return -2; } /* * Victory. Everything is fine. */ return 0; } /** ***************************************************************************** ** ** @brief Wrapper routine to do an LLRP transaction ** ** Wrapper to transact a request/resposne. ** - Print the outbound message in XML if verbose level is at least 2 ** - Send it using the LLRP_Conn_transact() ** - LLRP_Conn_transact() receives the response or recognizes an error ** - Tattle on errors, if any ** - Print the received message in XML if verbose level is at least 2 ** - If the response is ERROR_MESSAGE, the request was sufficiently ** misunderstood that the reader could not send a proper reply. ** Deem this an error, free the message. ** ** The message returned resides in allocated memory. It is the ** caller's obligtation to free it. ** ** @return ==NULL Something went wrong, already tattled ** !=NULL Pointer to a message ** *****************************************************************************/ LLRP_tSMessage * transact ( LLRP_tSMessage * pSendMsg) { LLRP_tSConnection * pConn = g_pConnectionToReader; LLRP_tSMessage * pRspMsg; /* * Print the XML text for the outbound message if * verbosity is 2 or higher. */ if(1 < g_Verbose) { printf("\n===================================\n"); printf("INFO: Transact sending\n"); printXMLMessage(pSendMsg); } /* * Send the message, expect the response of certain type. * If LLRP_Conn_transact() returns NULL then there was * an error. In that case we try to print the error details. */ pRspMsg = LLRP_Conn_transact(pConn, pSendMsg, 5000); if(NULL == pRspMsg) { const LLRP_tSErrorDetails *pError = LLRP_Conn_getTransactError(pConn); printf("ERROR: %s transact failed, %s\n", pSendMsg->elementHdr.pType->pName, pError->pWhatStr ? pError->pWhatStr : "no reason given"); if(NULL != pError->pRefType) { printf("ERROR: ... reference type %s\n", pError->pRefType->pName); } if(NULL != pError->pRefField) { printf("ERROR: ... reference field %s\n", pError->pRefField->pName); } return NULL; } /* * Print the XML text for the inbound message if * verbosity is 2 or higher. */ if(1 < g_Verbose) { printf("\n- - - - - - - - - - - - - - - - - -\n"); printf("INFO: Transact received response\n"); printXMLMessage(pRspMsg); } /* * If it is an ERROR_MESSAGE (response from reader * when it can't understand the request), tattle * and declare defeat. */ if(&LLRP_tdERROR_MESSAGE == pRspMsg->elementHdr.pType) { const LLRP_tSTypeDescriptor *pResponseType; pResponseType = pSendMsg->elementHdr.pType->pResponseType; printf("ERROR: Received ERROR_MESSAGE instead of %s\n", pResponseType->pName); freeMessage(pRspMsg); pRspMsg = NULL; } return pRspMsg; } /** ***************************************************************************** ** ** @brief Wrapper routine to receive a message ** ** This can receive notifications as well as responses. ** - Recv a message using the LLRP_Conn_recvMessage() ** - Tattle on errors, if any ** - Print the message in XML if verbose level is at least 2 ** ** The message returned resides in allocated memory. It is the ** caller's obligtation to free it. ** ** @param[in] nMaxMS -1 => block indefinitely ** 0 => just peek at input queue and ** socket queue, return immediately ** no matter what ** >0 => ms to await complete frame ** ** @return ==NULL Something went wrong, already tattled ** !=NULL Pointer to a message ** *****************************************************************************/ LLRP_tSMessage * recvMessage ( int nMaxMS) { LLRP_tSConnection * pConn = g_pConnectionToReader; LLRP_tSMessage * pMessage; /* * Receive the message subject to a time limit */ pMessage = LLRP_Conn_recvMessage(pConn, nMaxMS); /* * If LLRP_Conn_recvMessage() returns NULL then there was * an error. In that case we try to print the error details. */ if(NULL == pMessage) { const LLRP_tSErrorDetails *pError = LLRP_Conn_getRecvError(pConn); printf("ERROR: recvMessage failed, %s\n", pError->pWhatStr ? pError->pWhatStr : "no reason given"); if(NULL != pError->pRefType) { printf("ERROR: ... reference type %s\n", pError->pRefType->pName); } if(NULL != pError->pRefField) { printf("ERROR: ... reference field %s\n", pError->pRefField->pName); } return NULL; } /* * Print the XML text for the inbound message if * verbosity is 2 or higher. */ if(1 < g_Verbose) { printf("\n===================================\n"); printf("INFO: Message received\n"); printXMLMessage(pMessage); } return pMessage; } /** ***************************************************************************** ** ** @brief Wrapper routine to send a message ** ** Wrapper to send a message. ** - Print the message in XML if verbose level is at least 2 ** - Send it using the LLRP_Conn_sendMessage() ** - Tattle on errors, if any ** ** @param[in] pSendMsg Pointer to message to send ** ** @return ==0 Everything OK ** !=0 Something went wrong, already tattled ** *****************************************************************************/ int sendMessage ( LLRP_tSMessage * pSendMsg) { LLRP_tSConnection * pConn = g_pConnectionToReader; /* * Print the XML text for the outbound message if * verbosity is 2 or higher. */ if(1 < g_Verbose) { printf("\n===================================\n"); printf("INFO: Sending\n"); printXMLMessage(pSendMsg); } /* * If LLRP_Conn_sendMessage() returns other than LLRP_RC_OK * then there was an error. In that case we try to print * the error details. */ if(LLRP_RC_OK != LLRP_Conn_sendMessage(pConn, pSendMsg)) { const LLRP_tSErrorDetails *pError = LLRP_Conn_getSendError(pConn); printf("ERROR: %s sendMessage failed, %s\n", pSendMsg->elementHdr.pType->pName, pError->pWhatStr ? pError->pWhatStr : "no reason given"); if(NULL != pError->pRefType) { printf("ERROR: ... reference type %s\n", pError->pRefType->pName); } if(NULL != pError->pRefField) { printf("ERROR: ... reference field %s\n", pError->pRefField->pName); } return -1; } /* * Victory */ return 0; } /** ***************************************************************************** ** ** @brief Wrapper to free a message. ** ** All it does is cast pMessage and let ** LLRP_Element_destruct() do the work. ** ** @param[in] pMessage Pointer to message to destruct ** ** @return void ** *****************************************************************************/ void freeMessage ( LLRP_tSMessage * pMessage) { LLRP_Element_destruct(&pMessage->elementHdr); } /** ***************************************************************************** ** ** @brief Helper to print a message as XML text ** ** Print a LLRP message as XML text ** ** @param[in] pMessage Pointer to message to print ** ** @return void ** *****************************************************************************/ void printXMLMessage ( LLRP_tSMessage * pMessage) { char aBuf[100*1024]; /* * Convert the message to an XML string. * This fills the buffer with either the XML string * or an error message. The return value could * be checked. */ LLRP_toXMLString(&pMessage->elementHdr, aBuf, sizeof aBuf); /* * Print the XML Text to the standard output. */ printf("%s", aBuf); }