1922 lines
50 KiB
C
Executable File
1922 lines
50 KiB
C
Executable File
|
|
/*
|
|
*****************************************************************************
|
|
* *
|
|
* 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 <stdio.h>
|
|
|
|
#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:
|
|
**
|
|
** <READER_EVENT_NOTIFICATION MessageID='0'>
|
|
** <ReaderEventNotificationData>
|
|
** <UTCTimestamp>
|
|
** <Microseconds>1184491439614224</Microseconds>
|
|
** </UTCTimestamp>
|
|
** <ConnectionAttemptEvent>
|
|
** <Status>Success</Status>
|
|
** </ConnectionAttemptEvent>
|
|
** </ReaderEventNotificationData>
|
|
** </READER_EVENT_NOTIFICATION>
|
|
**
|
|
** @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:
|
|
**
|
|
** <SET_READER_CONFIG MessageID='101'>
|
|
** <ResetToFactoryDefault>1</ResetToFactoryDefault>
|
|
** </SET_READER_CONFIG>
|
|
**
|
|
** @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
|
|
**
|
|
** <DELETE_ROSPEC MessageID='102'>
|
|
** <ROSpecID>0</ROSpecID>
|
|
** </DELETE_ROSPEC>
|
|
**
|
|
** @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
|
|
**
|
|
** <ADD_ROSPEC MessageID='201'>
|
|
** <ROSpec>
|
|
** <ROSpecID>123</ROSpecID>
|
|
** <Priority>0</Priority>
|
|
** <CurrentState>Disabled</CurrentState>
|
|
** <ROBoundarySpec>
|
|
** <ROSpecStartTrigger>
|
|
** <ROSpecStartTriggerType>Null</ROSpecStartTriggerType>
|
|
** </ROSpecStartTrigger>
|
|
** <ROSpecStopTrigger>
|
|
** <ROSpecStopTriggerType>Null</ROSpecStopTriggerType>
|
|
** <DurationTriggerValue>0</DurationTriggerValue>
|
|
** </ROSpecStopTrigger>
|
|
** </ROBoundarySpec>
|
|
** <AISpec>
|
|
** <AntennaIDs>0</AntennaIDs>
|
|
** <AISpecStopTrigger>
|
|
** <AISpecStopTriggerType>Duration</AISpecStopTriggerType>
|
|
** <DurationTrigger>5000</DurationTrigger>
|
|
** </AISpecStopTrigger>
|
|
** <InventoryParameterSpec>
|
|
** <InventoryParameterSpecID>1234</InventoryParameterSpecID>
|
|
** <ProtocolID>EPCGlobalClass1Gen2</ProtocolID>
|
|
** </InventoryParameterSpec>
|
|
** </AISpec>
|
|
** <ROReportSpec>
|
|
** <ROReportTrigger>Upon_N_Tags_Or_End_Of_ROSpec</ROReportTrigger>
|
|
** <N>0</N>
|
|
** <TagReportContentSelector>
|
|
** <EnableROSpecID>0</EnableROSpecID>
|
|
** <EnableSpecIndex>0</EnableSpecIndex>
|
|
** <EnableInventoryParameterSpecID>0</EnableInventoryParameterSpecID>
|
|
** <EnableAntennaID>0</EnableAntennaID>
|
|
** <EnableChannelIndex>0</EnableChannelIndex>
|
|
** <EnablePeakRSSI>0</EnablePeakRSSI>
|
|
** <EnableFirstSeenTimestamp>0</EnableFirstSeenTimestamp>
|
|
** <EnableLastSeenTimestamp>0</EnableLastSeenTimestamp>
|
|
** <EnableTagSeenCount>0</EnableTagSeenCount>
|
|
** <EnableAccessSpecID>0</EnableAccessSpecID>
|
|
** </TagReportContentSelector>
|
|
** </ROReportSpec>
|
|
** </ROSpec>
|
|
** </ADD_ROSPEC>
|
|
**
|
|
** @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:
|
|
** <ENABLE_ROSPEC MessageID='202'>
|
|
** <ROSpecID>123</ROSpecID>
|
|
** </ENABLE_ROSPEC>
|
|
**
|
|
** @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:
|
|
** <START_ROSPEC MessageID='202'>
|
|
** <ROSpecID>123</ROSpecID>
|
|
** </START_ROSPEC>
|
|
**
|
|
** @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);
|
|
}
|