package org.fosstrak.ale.server.llrp;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.rmi.RemoteException;
import java.util.List;
import kr.ac.kaist.resl.fosstrak.ale.Adaptor;
import kr.ac.kaist.resl.fosstrak.ale.AdaptorManagement;
import org.apache.log4j.Logger;
import org.fosstrak.ale.server.ALEApplicationContext;
import org.fosstrak.ale.server.readers.llrp.LLRPManager;
/*
import org.fosstrak.llrp.adaptor.Adaptor;
import org.fosstrak.llrp.adaptor.AdaptorManagement;
*/
import org.fosstrak.llrp.adaptor.exception.LLRPRuntimeException;
import org.fosstrak.llrp.client.LLRPExceptionHandler;
import org.fosstrak.llrp.client.LLRPExceptionHandlerTypeMap;
import org.fosstrak.llrp.client.LLRPMessageItem;
import org.fosstrak.llrp.client.MessageHandler;
import org.llrp.ltk.exceptions.InvalidLLRPMessageException;
import kr.ac.kaist.resl.ltk.generated.enumerations.ConnectionAttemptStatusType;
import kr.ac.kaist.resl.ltk.generated.enumerations.StatusCode;
import kr.ac.kaist.resl.ltk.generated.messages.READER_EVENT_NOTIFICATION;
import kr.ac.kaist.resl.ltk.generated.parameters.ConnectionAttemptEvent;
import kr.ac.kaist.resl.ltk.generated.parameters.LLRPStatus;
import kr.ac.kaist.resl.ltk.generated.parameters.ROSpec;
import kr.ac.kaist.resl.ltk.generated.parameters.AccessSpec;
import org.llrp.ltk.types.LLRPMessage;
/**
* ORANGE: This class initializes the llrp context and manage the LLRP messages.
*
* @author wafa.soubra@orange.com
*/
public class AdaptorMgmt {
/** logger. */
private static final Logger LOG = Logger.getLogger(AdaptorMgmt.class.getName());
/** flag whether the adaptor management has been initialized or not. */
private static boolean adaptorMgmtInitialized = false;
/** the unique instance of the LLRP Adaptor Management. */
private static AdaptorManagement llrpAdaptorMgmt = null;
/** the remote llrp adaptor. */
private static Adaptor llrpRemoteAdaptor = null;
/** enable or disable the redefine function to avoid adding several times the ROSpec. */
private static boolean redefineStatus = false;
/** a boolean to indicate if it is the first time we add an rospec. */
private static boolean addFirstTime = true;
/**
* Initialize the LLRP Context.
* Initialize & set the unique instance of the Adaptor Management.
* Initialize & set the Remote Adaptor.
*/
public static void initializeLLRPContext () {
LOG.debug("InitializeLLRPContext ...");
String readConfig = null;
String writeConfig = null;
boolean commitChanges = false;
if (!getAdaptorMgmtInitialized()) {
llrpAdaptorMgmt = AdaptorManagement.getInstance();
if (!llrpAdaptorMgmt.isInitialized()) {
//musn't happen when we launch the fc-server in fosstrak
try {
llrpAdaptorMgmt.initialize(readConfig, writeConfig, commitChanges, null, null);
} catch (LLRPRuntimeException e) {
LOG.error("Error when trying to initialize LLRP Adaptor Management", e);
}
}
setAdaptorMgmtInitialized(true);
//llrpRemoteAdaptor = llrpAdaptorMgmt.getAdaptorNames().get(0);
llrpRemoteAdaptor = ALEApplicationContext.getBean(LLRPManager.class).getAdaptor();
// Register the handlers, so the adaptor management will
// distribute the LLRP messages automatically and asynchronously.
llrpAdaptorMgmt.registerFullHandler(defineMessageHandler());
llrpAdaptorMgmt.setExceptionHandler(defineExceptionHandler());
}
LOG.debug("End initializeLLRPContext.");
}
/**
* Send asynchronously the LLRP messages
* @param readerName the name of the reader
* @param msg the LLRP message to send to the reader
*/
public static void sendLLRPMessage(String readerName, LLRPMessage msg) {
try {
LOG.debug("Asynchrone sending of LLRP message " + msg.getName() + " to the reader " + readerName);
llrpAdaptorMgmt.enqueueLLRPMessage(llrpRemoteAdaptor.getAdaptorName(), readerName, msg);
} catch (LLRPRuntimeException e) {
LOG.error("Couldn't send command " + msg.getName(), e);
} catch (RemoteException e) {
LOG.error("Error occurs during LLRP transmission " + msg.getName(), e);
}
}
/**
* Send synchronously the LLRP message.
* @param readerName the name of the reader
* @param msg the LLRP message to send to the reader
*/
public static void sendSynchroneLLRPMessage(String readerName, LLRPMessage msg) {
try {
byte[] binaryEncodedMessage = msg.encodeBinary();
LOG.debug("Synchrone sending of LLRP message " + msg.getName() + " to the reader " + readerName);
llrpRemoteAdaptor.sendLLRPMessage(readerName, binaryEncodedMessage);
} catch (InvalidLLRPMessageException e) {
LOG.error("Invalid message ", e);
} catch (LLRPRuntimeException e) {
LOG.error("Couldn't send command " + msg.getName(), e);
} catch (RemoteException e) {
LOG.error("Error occurs during LLRP transmission " + msg.getName(), e);
}
}
/**
* Disconnect all readers & shutdown the Adaptor Management
* We must call that function when we exit the platform.
*/
public static void shutdownLLRPContext() {
LOG.debug("ShutdownLLRPContext and disconnecting all readers ...");
if (adaptorMgmtInitialized) {
llrpAdaptorMgmt.disconnectReaders();
llrpAdaptorMgmt.shutdown();
setAdaptorMgmtInitialized(false);
}
LOG.debug("End shutdownLLRPContext.");
}
/**
* Test if the adaptor contains a physical reader
* @param readerName the name of the reader
* @return boolean true if the adaptor contains the reader.
*/
public static boolean containsReader(String readerName) {
boolean readerExist = false;
try {
readerExist = llrpRemoteAdaptor.containsReader(readerName);
} catch (RemoteException e) {
LOG.error("Error when testing if the remote adaptor contains the reader " + readerName, e);
}
return readerExist;
}
/**
* Sets the value of the redefineStatus
* @param status if true enable the redefine function.
*/
public static void setRedefineStatus (boolean status) {
redefineStatus= status;
}
/**
* @return boolean true if the adaptor management is initialized.
*/
private static synchronized boolean getAdaptorMgmtInitialized() {
return adaptorMgmtInitialized;
}
/**
* @param status if true means the adaptor management is initialized
* else shutdown the adaptor management.
*/
private static synchronized void setAdaptorMgmtInitialized(boolean status) {
adaptorMgmtInitialized = status;
}
/**
* Create a message handler to let the client receive all the incoming LLRP messages
* @return the message handler.
*/
private static MessageHandler defineMessageHandler () {
MessageHandler msgHandler = new MessageHandler() {
@SuppressWarnings("unchecked")
public void handle(String adaptorName, String readerName, LLRPMessage msg) {
LOG.debug (String.format("Received LLRP message msg name: %s from adapter: %s and reader: %s",
msg.getName(), adaptorName, readerName));
StatusCode statusCode = null;
String statusStr ="";
try {
Method getLLRPStatusMethod = msg.getClass().getMethod("getLLRPStatus", new Class[0]);
LLRPStatus status = (LLRPStatus) getLLRPStatusMethod.invoke(msg, new Object[0]);
statusCode = status.getStatusCode();
statusStr = statusCode.toString();
} catch (Exception e) {
//do Nothing
}
// store the item in the repository which is the LOG file
LLRPMessageItem item = new LLRPMessageItem();
item.setAdapter(adaptorName);
item.setReader(readerName);
String msgName = msg.getName();
item.setMessageType(msgName);
item.setStatusCode(statusStr);
try {
item.setContent(msg.toXMLString());
LOG.trace(item.prettyPrint());
LOG.trace(item.getContent());
} catch (InvalidLLRPMessageException e) {
LOG.error("error when converting a LLRP msg into XML", e);
}
// connection between the client and the reader is established
if (item.getMessageType().equals("READER_EVENT_NOTIFICATION")) {
READER_EVENT_NOTIFICATION msgEvent = (READER_EVENT_NOTIFICATION)msg;
ConnectionAttemptEvent connectionEvent = msgEvent.getReaderEventNotificationData().getConnectionAttemptEvent();
if (connectionEvent != null){
ConnectionAttemptStatusType connectionStatus = connectionEvent.getStatus();
String connectionStatusStr = connectionStatus.toString();
if (connectionStatus.getValue(connectionStatusStr) == ConnectionAttemptStatusType.Success) {
LOG.debug("Connection is established");
ALEApplicationContext.getBean(LLRPControllerManager.class).setReaderConnected(readerName, true);
}
}
}
// enable the Redefine Status when the add_rospec is done
if (item.getMessageType().equals("ADD_ROSPEC_RESPONSE") && (!"".equals(statusStr)) && (statusCode.getValue(statusStr) == StatusCode.M_Success)) {
LOG.debug("Redefine is enabled");
addFirstTime=false;
setRedefineStatus(true);
}
// added to test sending commands
if (item.getMessageType().equals("ADD_ACCESSSPEC_RESPONSE") && (!"".equals(statusStr))) {
LOG.debug("Status of AccesSpec Response = " + statusCode.getValue(statusStr));
}
if (item.getMessageType().equals("ENABLE_ACCESSSPEC_RESPONSE") && (!"".equals(statusStr))) {
LOG.debug("Status of Enable AccesSpec Response = " + statusCode.getValue(statusStr));
}
if (item.getMessageType().equals("ERROR_MESSAGE") && (!"".equals(statusStr))) {
LOG.debug("Status of Error Message = " + statusCode.getValue(statusStr));
}
try {
if (item.getMessageType().equals("GET_ACCESSSPECS_RESPONSE")) {
Method getAccessSpecList = msg.getClass().getMethod("getAccessSpecList", new Class[0]);
List<AccessSpec> listAccessSpec = (List<AccessSpec>) getAccessSpecList.invoke(msg, new Object[0]);
for (AccessSpec access : listAccessSpec) {
LOG.debug ("length of acccesspecList = " + listAccessSpec.size());
LOG.debug ("access id is = " + access.getAccessSpecID());
}
}
} catch (NoSuchMethodException e) {
LOG.error ("NoSuchMethodException in GET_ACCESSSPECS_RESPONSE " + e);
} catch (InvocationTargetException e) {
LOG.error ("InvocationTargetException in GET_ACCESSSPECS_RESPONSE " + e);
} catch (IllegalAccessException e) {
LOG.error ("IllegalAccessException in GET_ACCESSSPECS_RESPONSE " + e);
}
// if reader was deconnected, redefine the ROSpec and disable the Redefine Status
try {
if (item.getMessageType().equals("GET_ROSPECS_RESPONSE")) {
Method getROSpecList = msg.getClass().getMethod("getROSpecList", new Class[0]);
List<ROSpec> listRoSpec = (List<ROSpec>) getROSpecList.invoke(msg, new Object[0]);
if (listRoSpec == null || listRoSpec.isEmpty()) {
if (redefineStatus) {
ALEApplicationContext.getBean(LLRPControllerManager.class).redefine (readerName);
setRedefineStatus (false);
} else {
// This is the case where we are in the mode waitForConnection = false.
// So we send several GET_ROSPECS messages. We do the ADD_ROSPEC one time and just the first time.
if (addFirstTime) {
ALEApplicationContext.getBean(LLRPControllerManager.class).redefine (readerName);
addFirstTime = false;
} else {
// This is the case where :
// - we never get the "add_rospec_response" or we are still waiting for the
// "add_rospec_response", so the redefineSatus is false
// - and the reader has been disconnected meanwhile, so the rospec list is empty.
// If we never get the response, the redefine must do it manually.
LOG.warn ("Wait to redefine the ROSpec automatically ... If not, do it manually.");
}
}
}
}
} catch (NoSuchMethodException e) {
LOG.error ("NoSuchMethodException in GET_ROSPECS_RESPONSE " + e);
} catch (InvocationTargetException e) {
LOG.error ("InvocationTargetException in GET_ROSPECS_RESPONSE " + e);
} catch (IllegalAccessException e) {
LOG.error ("IllegalAccessException in GET_ROSPECS_RESPONSE " + e);
}
// TODO : 1- Check if an ROSpec is already defined and disabled on the reader
// TODO: 2- Check if an ROSpec is already defined and enabled on the reader
// when launching the platform, we receive RO_ACCESS_REPORT
// if (item.getMessageType().equals("RO_ACCESS_REPORT")) {
// LOG.debug(" ROSpec already defined and running in the reader.");
// }
}
};
return msgHandler;
}
/**
* Create an exception handler to receive asynchronous exceptions from the adaptors and the readers.
* @return the exception handler.
*/
private static LLRPExceptionHandler defineExceptionHandler () {
LLRPExceptionHandler exceptionHandler = new LLRPExceptionHandler() {
public void postExceptionToGUI (LLRPExceptionHandlerTypeMap exceptionType, LLRPRuntimeException e,
String adaptorName, String readerName) {
LOG.warn(String.format("Received LLRP Exception %s from adapter: %s and reader: %s",
exceptionType.toString(), adaptorName, readerName));
}
};
return exceptionHandler;
}
}