Example #1
0
// Called when an event occurs in the kernel
void StringListener::OnKernelEvent(int eventIDIn, AgentSML* pAgentSML, void* pCallData)
{
    // There are currently no kernel events corresponding to this SML event.
    // They are all directly generated from SML.  If we later add kernel callbacks
    // for this class of events they would come here.
    
    smlStringEventId eventID = static_cast<smlStringEventId>(eventIDIn);
    StringListenerCallbackData* pCallbackData = static_cast<StringListenerCallbackData*>(pCallData);
    assert(pCallbackData);
    
    memset(pCallbackData->pReturnStringBuffer, 0, pCallbackData->maxLengthReturnStringBuffer);
    
    // Get the first listener for this event (or return if there are none)
    ConnectionListIter connectionIter ;
    if (!EventManager<smlStringEventId>::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) ;
    
    // Build the SML message we're doing to send.
    soarxml::ElementXML* pMsg = pConnection->CreateSMLCommand(sml_Names::kCommand_Event) ;
    pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamEventID, event) ;
    
    if (pCallbackData->pData)
    {
        pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamValue, pCallbackData->pData) ;
    }
    
    // Note: we should be telling the client the maximum length of the result,
    // however, we're planning on changing this so there is no maximum length
    // so we're not implementing this.
    
    // Send the message out
    AnalyzeXML response ;
    SendEvent(pAgentSML, pConnection, pMsg, &response, connectionIter, GetEnd(eventID)) ;
    
    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 allowed we just truncate it.
        // (In practice this shouldn't be a problem--just need to make sure nobody crashes on a super long return string).
        strncpy(pCallbackData->pReturnStringBuffer, pResult, pCallbackData->maxLengthReturnStringBuffer) ;
        pCallbackData->pReturnStringBuffer[ pCallbackData->maxLengthReturnStringBuffer - 1 ] = 0 ;  // Make sure it's NULL terminated
    }
    
    // Clean up
    delete pMsg ;
}
Example #2
0
smlRunState Agent::GetRunState()
{
	AnalyzeXML response ;

	bool ok = GetConnection()->SendAgentCommand(&response, sml_Names::kCommand_GetRunState, GetAgentName(), sml_Names::kParamValue, sml_Names::kParamRunState) ;

	if (!ok)
		return smlRunState(0) ;

	return smlRunState(response.GetResultInt(0)) ;
}
Example #3
0
int Agent::GetDecisionCycleCounter()
{
	AnalyzeXML response ;

	bool ok = GetConnection()->SendAgentCommand(&response, sml_Names::kCommand_GetRunState, GetAgentName(), sml_Names::kParamValue, sml_Names::kParamDecision) ;

	if (!ok)
		return 0 ;

	return response.GetResultInt(0) ;
}
Example #4
0
/*************************************************************
* @brief	Takes an incoming SML message and responds with
*			an appropriate response message.
*
* @param pConnection	The connection this message came in on.
* @param pIncoming		The incoming message
*************************************************************/
soarxml::ElementXML* KernelSML::ProcessIncomingSML(Connection* pConnection, soarxml::ElementXML* pIncomingMsg)
{
	if (!pIncomingMsg || !pConnection)
		return NULL ;

	// Make sure only one thread is executing commands in the kernel at a time.
	// This is really just an insurance policy as I don't think we'll ever execute
	// commands on different threads within kernelSML because we
	// only allow one embedded connection to the kernel, but it's nice to be sure.
	soar_thread::Lock lock(m_pKernelMutex) ;

#ifdef DEBUG
	// For debugging, it's helpful to be able to look at the incoming message as an XML string
	char* pIncomingXML = pIncomingMsg->GenerateXMLString(true) ;
#endif

	soarxml::ElementXML* pResponse = pConnection->CreateSMLResponse(pIncomingMsg) ;

	// Fatal error creating the response
	if (!pResponse)
		return NULL ;

	// Analyze the message and find important tags
	AnalyzeXML msg ;
	msg.Analyze(pIncomingMsg) ;

	// Get the "name" attribute from the <command> tag
	char const* pCommandName = msg.GetCommandName() ;

	if (pCommandName)
	{
		ProcessCommand(pCommandName, pConnection, &msg, pResponse) ;
	}
	else
	{
		// The message wasn't something we recognize.
		if (!msg.GetCommandTag())
			AddErrorMsg(pConnection, pResponse, "Incoming message did not contain a <command> tag") ;
		else
			AddErrorMsg(pConnection, pResponse, "Incoming message did not contain a name attribute in the <command> tag") ;
	}

#ifdef DEBUG
	// For debugging, it's helpful to be able to look at the response as XML
	char* pResponseXML = pResponse->GenerateXMLString(true) ;

	// Set a break point on this next line if you wish to see the incoming
	// and outgoing as XML before they get deleted.
	soarxml::ElementXML::DeleteString(pIncomingXML) ;
	soarxml::ElementXML::DeleteString(pResponseXML) ;
#endif

	return pResponse ;
}
Example #5
0
bool Agent::WasAgentOnRunList()
{
	AnalyzeXML response ;

	bool ok = GetConnection()->SendAgentCommand(&response, sml_Names::kCommand_WasAgentOnRunList, GetAgentName()) ;

	if (!ok)
		return false ;

	bool wasRun = response.GetResultBool(false) ;
	return wasRun ;
}
Example #6
0
// Execute the command line by building up an XML message and submitting it to our regular command processor.
bool RhsListener::ExecuteCommandLine(AgentSML* pAgent, char const* pFunctionName, char const* pArgument, int maxLengthReturnValue, char* pReturnValue)
{
	KernelSML* pKernel = m_pKernelSML ;

	// We'll pretend this came from the local (embedded) connection.
	Connection* pConnection = pKernel->GetEmbeddedConnection() ;

	// Build up a single command line from our functionName + argument combination
	std::stringstream commandLine;
	commandLine << pFunctionName;

	if (pArgument)
	{
		commandLine << " " ;
		commandLine << pArgument ;
	}

	// Build up a message to execute the command line
	bool rawOutput = true ;
	soarxml::ElementXML* pMsg = pConnection->CreateSMLCommand(sml_Names::kCommand_CommandLine, rawOutput) ;
	pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamAgent, pAgent->GetName());
	pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamLine, commandLine.str().c_str()) ;

	AnalyzeXML incoming ;
	incoming.Analyze(pMsg) ;

	// Create a response object which the command line can fill in
	soarxml::ElementXML* pResponse = pConnection->CreateSMLResponse(pMsg) ;

	// Execute the command line
	bool ok = pKernel->ProcessCommand(sml_Names::kCommand_CommandLine, pConnection, &incoming, pResponse) ;

	if (ok)
	{
		// Take the result from executing the command line and fill it in to our "pReturnValue" array.
		AnalyzeXML response ;
		response.Analyze(pResponse) ;

		char const* pResult = response.GetResultString() ;

		if (pResult)
		{
			strncpy(pReturnValue, pResult, maxLengthReturnValue) ;
			pReturnValue[maxLengthReturnValue-1] = 0 ;
		}
	}

	// Clean up
	delete pMsg ;
	delete pResponse ;

	return ok ;
}
Example #7
0
smlPhase Agent::GetCurrentPhase()
{
	AnalyzeXML response ;

	bool ok = GetConnection()->SendAgentCommand(&response, sml_Names::kCommand_GetRunState, GetAgentName(), sml_Names::kParamValue, sml_Names::kParamPhase) ;

	if (!ok)
		return sml_INPUT_PHASE ;

	smlPhase phase = smlPhase(response.GetResultInt(int(sml_INPUT_PHASE))) ;

	return phase ;
}
Example #8
0
smlRunResult Agent::GetResultOfLastRun()
{
	AnalyzeXML response ;

	bool ok = GetConnection()->SendAgentCommand(&response, sml_Names::kCommand_GetResultOfLastRun, GetAgentName()) ;

	if (!ok)
		return sml_RUN_ERROR ;

	smlRunResult result = smlRunResult(response.GetResultInt(int(sml_RUN_ERROR))) ;

	return result ;
}
Example #9
0
bool Agent::IsProductionLoaded(char const* pProductionName)
{
	if (!pProductionName)
		return false ;

	AnalyzeXML response ;

	bool ok = GetConnection()->SendAgentCommand(&response, sml_Names::kCommand_IsProductionLoaded, GetAgentName(), sml_Names::kParamName, pProductionName) ;

	if (!ok)
		return false ;

	return response.GetResultBool(false) ;
}
Example #10
0
char const* Agent::ConvertIdentifier(char const* pClientIdentifier)
{
	// need to keep the result around after the function returns
	// bad
	static std::string kernelIdentifier;

	AnalyzeXML response;

	// Send the command to the kernel
	bool ret = m_Kernel->GetConnection()->SendAgentCommand(&response, sml_Names::kCommand_ConvertIdentifier, GetAgentName(), sml_Names::kParamName, pClientIdentifier);

	if (ret)
	{
		// Get the result as a string
		char const *pResult = response.GetResultString();
		if (pResult && strlen(pResult)) 
		{
			kernelIdentifier.assign(pResult);
			return kernelIdentifier.c_str();
		} 
	}
	return pClientIdentifier;
}
Example #11
0
void InputListener::ProcessPendingInput(AgentSML* pAgentSML, int )
{
	PendingInputList* pPending = pAgentSML->GetPendingInputList() ;

	bool ok = true ;

	for (PendingInputListIter iter = pPending->begin() ; iter != pPending->end() ; iter = pPending->erase(iter)) {
		soarxml::ElementXML* pInputMsg = *iter ;

		// Analyze the message and find important tags
		AnalyzeXML msg ;
		msg.Analyze(pInputMsg) ;

		// Get the "name" attribute from the <command> tag
		char const* pCommandName = msg.GetCommandName() ;

		// Only input commands should be stored in the pending input list
		(void)pCommandName; // silences warning in release mode
		assert(!strcmp(pCommandName, "input")) ;

		soarxml::ElementXML const* pCommand = msg.GetCommandTag() ;

		int nChildren = pCommand->GetNumberChildren() ;

		soarxml::ElementXML wmeXML(NULL) ;
		soarxml::ElementXML* pWmeXML = &wmeXML ;

		if (kDebugInput)
			PrintDebugFormat("--------- %s starting input ----------", pAgentSML->GetName()) ;

		for (int i = 0 ; i < nChildren ; i++)
		{
			pCommand->GetChild(&wmeXML, i) ;

			// Ignore tags that aren't wmes.
			if (!pWmeXML->IsTag(sml_Names::kTagWME))
				continue ;

			// Find out if this is an add or a remove
			char const* pAction = pWmeXML->GetAttribute(sml_Names::kWME_Action) ;

			if (!pAction)
				continue ;

			bool add = IsStringEqual(pAction, sml_Names::kValueAdd) ;
			bool remove = IsStringEqual(pAction, sml_Names::kValueRemove) ;

			if (add)
			{
				char const* pID			= pWmeXML->GetAttribute(sml_Names::kWME_Id) ;	// May be a client side id value (e.g. "o3" not "O3")
				char const* pAttribute  = pWmeXML->GetAttribute(sml_Names::kWME_Attribute) ;
				char const* pValue		= pWmeXML->GetAttribute(sml_Names::kWME_Value) ;
				char const* pType		= pWmeXML->GetAttribute(sml_Names::kWME_ValueType) ;	// Can be NULL (=> string)
				char const* pTimeTag	= pWmeXML->GetAttribute(sml_Names::kWME_TimeTag) ;	// May be a client side time tag (e.g. -3 not +3)

				// Set the default value
				if (!pType)
					pType = sml_Names::kTypeString ;

				// Check we got everything we need
				if (!pID || !pAttribute || !pValue || !pTimeTag)
					continue ;

				if (kDebugInput)
				{
					PrintDebugFormat("%s Add %s ^%s %s (type %s tag %s)", pAgentSML->GetName(), pID, pAttribute, pValue, pType, pTimeTag) ;
				}

				// Add the wme
				ok = pAgentSML->AddInputWME( pID, pAttribute, pValue, pType, pTimeTag) && ok ;
			}
			else if (remove)
			{
				char const* pTimeTag = pWmeXML->GetAttribute(sml_Names::kWME_TimeTag) ;	// May be (will be?) a client side time tag (e.g. -3 not +3)

				if (kDebugInput)
				{
					PrintDebugFormat("%s Remove tag %s", pAgentSML->GetName(), pTimeTag) ;
				}

				// Remove the wme
				ok = pAgentSML->RemoveInputWME(pTimeTag) && ok ;
			}
		}

		delete pInputMsg ;
	}

	std::list<DirectInputDelta>* pBufferedDirect = pAgentSML->GetBufferedDirectList();
	for (std::list<DirectInputDelta>::iterator iter = pBufferedDirect->begin() ; iter != pBufferedDirect->end() ; iter = pBufferedDirect->erase(iter)) 
	{
		DirectInputDelta& delta = *iter;
		switch (delta.type)
		{
		case DirectInputDelta::kRemove:
			pAgentSML->RemoveInputWME(delta.clientTimeTag);
			break;
		case DirectInputDelta::kAddString:
			pAgentSML->AddStringInputWME(delta.id.c_str(), delta.attribute.c_str(), delta.svalue.c_str(), delta.clientTimeTag);
			break;
		case DirectInputDelta::kAddInt:
			pAgentSML->AddIntInputWME(delta.id.c_str(), delta.attribute.c_str(), delta.ivalue, delta.clientTimeTag);
			break;
		case DirectInputDelta::kAddDouble:
			pAgentSML->AddDoubleInputWME(delta.id.c_str(), delta.attribute.c_str(), delta.dvalue, delta.clientTimeTag);
			break;
		case DirectInputDelta::kAddId:
			pAgentSML->AddIdInputWME(delta.id.c_str(), delta.attribute.c_str(), delta.svalue.c_str(), delta.clientTimeTag);
			break;
		default:
			assert(false);
			break;
		}
	}
}
Example #12
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 ;
}
Example #13
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 ;
}