Пример #1
0
bool RhsListener::ExecuteRhsCommand(AgentSML* pAgentSML, smlRhsEventId eventID, std::string const& functionName, std::string const& arguments, std::string* pResultStr)
{
	bool result = false ;

	// Get the list of connections (clients) who have registered to implement this right hand side (RHS) function.
	ConnectionList* pList = GetRhsListeners(functionName.c_str()) ;

	// If nobody is listening we're done (not a bug as we register for all rhs functions and only forward specific ones that the client has registered)
	if (!pList || pList->size() == 0)
		return result ;

	ConnectionListIter connectionIter = pList->begin() ;

	// We need the first connection for when we're building the message.  Perhaps this is a sign that
	// we shouldn't have rolled these methods into Connection.
	Connection* pConnection = *connectionIter ;

	// Convert eventID to a string
	char const* event = m_pKernelSML->ConvertEventToString(eventID) ;

	// Build the SML message we're doing to send.
	// Pass the agent in the "name" parameter not the "agent" parameter as this is a kernel
	// level event, not an agent level one (because you need to register with the kernel to get "agent created").
	soarxml::ElementXML* pMsg = pConnection->CreateSMLCommand(sml_Names::kCommand_Event) ;
	if (pAgentSML) pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamName, pAgentSML->GetName()) ;
	pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamEventID, event) ;
	pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamFunction, functionName.c_str()) ;
	pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamValue, arguments.c_str()) ;

#ifdef _DEBUG
	// Generate a text form of the XML so we can look at it in the debugger.
	char* pStr = pMsg->GenerateXMLString(true) ;
#endif

	AnalyzeXML response ;

	// We want to call embedded connections first, so that we get the best performance
	// for these functions.  I don't want to sort the list or otherwise change it so
	// instead we'll just use a rather clumsy outer loop to do this.
	for (int phase = 0 ; phase < 2 && !result ; phase++)
	{
		// Only call to embedded connections
		bool embeddedPhase = (phase == 0) ;

		// Reset the iterator to the beginning of the list
		connectionIter = pList->begin();

		// Keep looping until we get a result
		while (connectionIter != pList->end() && !result)
		{
			pConnection = *connectionIter ;

			// We call all embedded connections (same process) first before
			// trying any remote methods.  This ensures that if multiple folks register
			// for the same function we execute the fastest one (w/o going over a socket for the result).
			if (pConnection->IsRemoteConnection() && embeddedPhase)
			{
				connectionIter++ ;
				continue ;
			}

			// It would be faster to just send a message here without waiting for a response
			// but that could produce incorrect behavior if the client expects to act *during*
			// the event that we're notifying them about (e.g. notification that we're in the input phase).
			bool ok = pConnection->SendMessageGetResponse(&response, pMsg) ;

			if (ok)
			{
				char const* pResult = response.GetResultString() ;

				if (pResult != NULL)
				{
					(*pResultStr) = pResult ;
					result = true ;
				}
			}

			connectionIter++ ;
		}
	}

#ifdef _DEBUG
	// Release the string form we generated for the debugger
	pMsg->DeleteString(pStr) ;
#endif

	// Clean up
	delete pMsg ;

	return result ;
}
Пример #2
0
void PrintListener::FlushOutput(Connection* pSourceConnection, smlPrintEventId eventID) 
{
	int buffer = eventID - smlEVENT_FIRST_PRINT_EVENT ;

	// Nothing waiting to be sent, so we're done.
	if (!m_BufferedPrintOutput[buffer].str().size())
		return ;

	// Get the first listener for this event (or return if there are none)
	ConnectionListIter connectionIter ;
	if (!EventManager<smlPrintEventId>::GetBegin(eventID, &connectionIter))
		return ;

	// We need the first connection for when we're building the message.  Perhaps this is a sign that
	// we shouldn't have rolled these methods into Connection.
	Connection* pConnection = *connectionIter;

	// Convert eventID to a string
	char const* event = m_pKernelSML->ConvertEventToString(eventID) ;

	// Send the message out
	AnalyzeXML response ;

	// For non-echo events, just send it normally
	if (eventID != smlEVENT_ECHO)
	{
		// Build the SML message we're going to send.
		soarxml::ElementXML* pMsg = pConnection->CreateSMLCommand(sml_Names::kCommand_Event);
		pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamAgent, m_pCallbackAgentSML->GetName());
		pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamEventID, event);
		pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamMessage, m_BufferedPrintOutput[buffer].str().c_str());

		// Send the message out
		AnalyzeXML response ;
		SendEvent(0, pConnection, pMsg, &response, connectionIter, GetEnd(eventID)) ;

		// Clean up
		delete pMsg ;
	}
	else
	{
		// For echo events build up the message on a connection by connection basis.
		// This allows us to tell a sender if they were the cause of a given message so a client
		// can filter out echoes from commands they originated.
		while (connectionIter != GetEnd(eventID))
		{
			pConnection = *connectionIter ;
			connectionIter++ ;

			// Build the SML message we're going to send.
			soarxml::ElementXML* pMsg = pConnection->CreateSMLCommand(sml_Names::kCommand_Event);
			pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamAgent, m_pCallbackAgentSML->GetName());
			pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamEventID, event);
			pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamMessage, m_BufferedPrintOutput[buffer].str().c_str());
			pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamSelf, (pSourceConnection == pConnection) ? sml_Names::kTrue : sml_Names::kFalse) ;

			#ifdef _DEBUG
				// Generate a text form of the XML so we can look at it in the debugger.
				char* pStr = pMsg->GenerateXMLString(true) ;
			#endif

			// It would be faster to just send a message here without waiting for a response
			// but that could produce incorrect behavior if the client expects to act *during*
			// the event that we're notifying them about (e.g. notification that we're in the input phase).
			pConnection->SendMessageGetResponse(&response, pMsg) ;

			#ifdef _DEBUG
				// Clean up the string form of the message.
				pMsg->DeleteString(pStr) ;
			#endif

			// Clean up
			delete pMsg ;
		}
	}

	// Clear the buffer now that it's been sent
	m_BufferedPrintOutput[buffer].str( std::string() );
}
Пример #3
0
bool RhsListener::HandleFilterEvent(smlRhsEventId eventID, AgentSML* pAgent, char const* pArgument,
						    std::string &pReturnValue)
{
	// Currently only supporting one event here, but that could change in time.
	assert(eventID == smlEVENT_FILTER) ;

	// Filters are handled as a RHS function call internally, using a special reserved name.
	char const* pFunctionName = sml_Names::kFilterName ;

	// Get the list of connections (clients) who have registered to implement this right hand side (RHS) function.
	ConnectionList* pList = GetRhsListeners(pFunctionName) ;

	bool result = false ;

	// If nobody is listening we're done (not a bug as we register for all rhs functions and only forward specific ones that the client has registered)
	if (!pList || pList->size() == 0)
		return result ;

	ConnectionListIter connectionIter = pList->begin() ;

	// We need the first connection for when we're building the message.  Perhaps this is a sign that
	// we shouldn't have rolled these methods into Connection.
	Connection* pConnection = *connectionIter ;

	// Convert eventID to a string
	char const* event = m_pKernelSML->ConvertEventToString(eventID) ;

	// Copy the initial command line into the return buffer and send that over.
	// This will be sequentially replaced by each filter in turn and whatever
	// is left in here at the end is the result of the filtering.
	// This allows multiple filters to act on the data, each modifying it as it goes.
	// A side effect of this is that the order the filters work on the data is significant.
	// Anyone in the chain can set the string to be the empty string, which brings the
	// whole process to a halt (they have eaten the command line at that point).
    pReturnValue.reserve(strlen(pArgument) + 1); //adding 1 because strlen does not count the terminating null character
	pReturnValue.assign(pArgument);	

	bool stop = false ;

	while (connectionIter != pList->end() && !stop)
	{
		// Build the SML message we're doing to send.
		// Pass the agent in the "name" parameter not the "agent" parameter as this is a kernel
		// level event, not an agent level one (because you need to register with the kernel to get "agent created").
		soarxml::ElementXML* pMsg = pConnection->CreateSMLCommand(sml_Names::kCommand_Event) ;
		pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamName, pAgent ? pAgent->GetName() : "") ;
		pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamEventID, event) ;
		pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamFunction, sml_Names::kFilterName) ;
		pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamValue, pReturnValue.c_str()) ;	// We send the current command line over

#ifdef _DEBUG
		// Generate a text form of the XML so we can look at it in the debugger.
		char* pStr = pMsg->GenerateXMLString(true) ;
#endif

		AnalyzeXML response ;

		pConnection = *connectionIter ;

		bool ok = pConnection->SendMessageGetResponse(&response, pMsg) ;

		if (ok)
		{
			char const* pResult = response.GetResultString() ;

			if (pResult != NULL)
			{
				// If the listener returns a result then take that
				// value and return it in "pReturnValue" to the caller.
				// If the client returns a longer string than the caller, pReturnValue is resized to fit it.
                pReturnValue.reserve(strlen(pResult) + 1); //adding 1 because strlen does not count the terminating null character
                pReturnValue.assign(pResult);	
				result = true ;
			}
			else
			{
				// If one of the filters returns an empty string, stop the process at that point
				// because the command has been "eaten".  Make the result the empty string.
				pReturnValue[0] = 0 ;
				result = true ;
				stop = true ;
			}
		}

		connectionIter++ ;

	#ifdef _DEBUG
		// Release the string form we generated for the debugger
		pMsg->DeleteString(pStr) ;
	#endif

		// Clean up
		delete pMsg ;
	}

	return result ;
}