Example #1
0
// Called when an event occurs in the kernel
void RunListener::OnKernelEvent(int eventID, AgentSML* pAgentSML, void* pCallData)
{
    // Get the first listener for this event (or return if there are none)
    ConnectionListIter connectionIter ;
    if (!EventManager<smlRunEventId>::GetBegin(smlRunEventId(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) ;
    
    // Convert phase to a string
    int phase = static_cast<int>(reinterpret_cast<intptr_t>(pCallData)) ;
    
    // Build the SML message we're doing to send.
    soarxml::ElementXML* pMsg = pConnection->CreateSMLCommand(sml_Names::kCommand_Event) ;
    pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamAgent, pAgentSML->GetName()) ;
    pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamEventID, event) ;
    char buf[TO_C_STRING_BUFSIZE];
    pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamPhase, to_c_string(phase, buf)) ;
    
    // Send the message out
    AnalyzeXML response ;
    SendEvent(pAgentSML, pConnection, pMsg, &response, connectionIter, GetEnd(smlRunEventId(eventID))) ;
    
    // Clean up
    delete pMsg ;
}
Example #2
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 #3
0
// Echo the list of wmes received back to any listeners
void XMLListener::FireInputReceivedEvent(soarxml::ElementXML const* pCommands)
{
    smlXMLEventId eventID = smlEVENT_XML_INPUT_RECEIVED ;
    
    // Get the first listener for this event (or return if there are none)
    ConnectionListIter connectionIter ;
    if (!EventManager<smlXMLEventId>::GetBegin(eventID, &connectionIter))
    {
        return ;
    }
    
    // Make a copy of pCommands and send it out.
    
    // 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 going to send.
    soarxml::ElementXML* pMsg = pConnection->CreateSMLCommand(sml_Names::kCommand_Event);
    pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamEventID, event);
    
    // Add the agent parameter and as a side-effect, get a pointer to the <command> tag.  This is an optimization.
    ElementXML_Handle hCommand = pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamAgent, m_pCallbackAgentSML->GetName()) ;
    soarxml::ElementXML command(hCommand) ;
    
    // Copy the list of wmes from the input message over
    int nWmes = pCommands->GetNumberChildren() ;
    for (int i = 0 ; i < nWmes ; i++)
    {
        soarxml::ElementXML wme ;
        pCommands->GetChild(&wme, i) ;
        
        if (wme.IsTag(sml_Names::kTagWME))
        {
            soarxml::ElementXML* pCopy = wme.MakeCopy() ;
            command.AddChild(pCopy) ;
        }
    }
    
    // This is important.  We are working with a subpart of pMsg.
    // If we retain ownership of the handle and delete the object
    // it will release the handle...deleting part of our message.
    command.Detach() ;
    
    // Send the message out
    AnalyzeXML response ;
    SendEvent(m_pCallbackAgentSML, pConnection, pMsg, &response, connectionIter, GetEnd(eventID)) ;
    
    // Clean up
    delete pMsg ;
}
Example #4
0
void XMLListener::OnKernelEvent(int eventIDIn, AgentSML* pAgentSML, void* pCallDataIn)
{
    // If the print callbacks have been disabled, then don't forward this message
    // on to the clients.  This allows us to use the print callback within the kernel to
    // retrieve information without it appearing in the trace.  (One day we won't need to do this enable/disable game).
    if (!m_EnablePrintCallback)
    {
        return ;
    }
    
    ElementXML* pXMLTrace = static_cast< ElementXML* >(pCallDataIn);
    smlXMLEventId eventID = static_cast< smlXMLEventId >(eventIDIn);
    
    // Nothing waiting to be sent, so we're done.
    if (pXMLTrace->GetNumberChildren() == 0)
    {
        return ;
    }
    
    // Get the first listener for this event (or return if there are none)
    ConnectionListIter connectionIter ;
    if (!EventManager<smlXMLEventId>::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 going to send.
    soarxml::ElementXML* pMsg = pConnection->CreateSMLCommand(sml_Names::kCommand_Event);
    
    // NOTE: For this trace message we require that the agent name be the first param here (so we can look it up quickly)
    // and any other changes to the structure of this message should be carefully checked to make sure they don't
    // break the client side code as this path is more brittle and faster than the rest of the message passing.
    pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamAgent, m_pCallbackAgentSML->GetName());
    pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamEventID, event);
    
    // Add it as a child of this message.  This is just moving a few pointers around, nothing is getting copied.
    // The structure of the message is <sml><command></command><trace></trace></sml>
    pMsg->AddChild(pXMLTrace) ;
    
    // Send the message out
    AnalyzeXML response ;
    SendEvent(pAgentSML, pConnection, pMsg, &response, connectionIter, GetEnd(eventID)) ;
    
    // Clean up
    delete pMsg ;
}
Example #5
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 ;
}
void ProductionListener::OnKernelEvent(int eventID, AgentSML* pAgentSML, void* pCallData)
{
	// TODO: all event handlers should be doing this:
	assert(IsProductionEventID(eventID)) ;

	smlProductionEventId smlEventID = smlProductionEventId(eventID) ;	

	std::string productionName ;

	// We're either passed a production* or an instantiation* depending on the type of event
	production* p = 0;
	if (smlEventID == smlEVENT_AFTER_PRODUCTION_ADDED || smlEventID == smlEVENT_BEFORE_PRODUCTION_REMOVED)
	{
		p = static_cast<production*>(pCallData) ;
	}
	else
	{
		instantiation* inst = static_cast<instantiation*>(pCallData) ;
		assert(inst) ;
		p = inst->prod ;
	}

	assert(p) ;
	assert(p->name->sc.name) ;
	productionName = p->name->sc.name ;

	// Get the first listener for this event (or return if there are none)
	ConnectionListIter connectionIter ;
	if (!EventManager<smlProductionEventId>::GetBegin(smlProductionEventId(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::kParamAgent, pAgentSML->GetName()) ;
	pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamEventID, event) ;
	pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamName, productionName.c_str()) ;

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

	// Clean up
	delete pMsg ;
}
Example #7
0
void OutputListener::SendOutputInitEvent()
{
    if (m_pCallbackAgentSML)
    {
        // Tell output link listeners about the reinitialize so they can clear out their output
        // link identifier, as it may change due to existing smem lti (long term identifiers)
        smlWorkingMemoryEventId outputEventId = smlEVENT_OUTPUT_PHASE_CALLBACK ;
        
        // Get the first listener for this event (or return if there are none)
        ConnectionListIter connectionIter ;
        if (!EventManager<smlWorkingMemoryEventId>::GetBegin(outputEventId, &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 ;
        
        // Build the SML message we're doing to send.
        soarxml::ElementXML* pMsg = pConnection->CreateSMLCommand(sml_Names::kCommand_OutputInit) ;
        
        // Add the agent parameter and as a side-effect, get a pointer to the <command> tag.  This is an optimization.
        ElementXML_Handle hCommand = pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamAgent, m_pCallbackAgentSML->GetName()) ;
        soarxml::ElementXML command(hCommand) ;
        
        // This is important.  We are working with a subpart of pMsg.
        // If we retain ownership of the handle and delete the object
        // it will release the handle...deleting part of our message.
        command.Detach() ;
        
#ifdef _DEBUG
        // Convert the XML to a string so we can look at it in the debugger
        char* pStr = pMsg->GenerateXMLString(true) ;
#endif
        
        // Send the message out
        AnalyzeXML response ;
        SendEvent(m_pCallbackAgentSML, pConnection, pMsg, &response, connectionIter, GetEnd(outputEventId)) ;
        
#ifdef _DEBUG
        pMsg->DeleteString(pStr) ;
#endif
        
        // Clean up
        delete pMsg ;
    }
}
Example #8
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() );
}
Example #9
0
// Called when an event occurs in the kernel
void SystemListener::OnKernelEvent(int eventIDIn, AgentSML* pAgentSML, void* /*pCallData*/)
{
	// All system events are currently implemented directly in kernel SML so there's
	// no underlying kernel callbacks to connect to.
	// If we ever change that, this is where the callbacks would come in.

	smlSystemEventId eventID = static_cast<smlSystemEventId>(eventIDIn);

	// The system start event can be suppressed by a client.
	// This allows us to run a Soar agent without running the associated simulation
	// (which should be listening for system-start/system-stop events).

	// DJP May 2007: This was an earlier idea about how we'd control systems through SML.  I'm
	// not sure this model (and these events) are relevant anymore.
	if (eventID == smlEVENT_SYSTEM_START)
	{
		bool suppress = m_pKernelSML->IsSystemStartSuppressed() ;

		// The flag is reset forcing the client to repeatedly suppress the system
		// start event each time they wish to run Soar and not generate this event.
		m_pKernelSML->SetSuppressSystemStart(false) ;

		if (suppress)
			return ;
	}

	// Similarly, system stop can be suppressed.
	if (eventID == smlEVENT_SYSTEM_STOP)
	{
		bool suppress = m_pKernelSML->IsSystemStopSuppressed() ;

		// Clear our flags that control this event
		m_pKernelSML->RequireSystemStop(false) ;
		m_pKernelSML->SetSuppressSystemStop(false) ;

		if (suppress)
			return ;
	}

	// Get the first listener for this event (or return if there are none)
	ConnectionListIter connectionIter ;
	if (!EventManager<smlSystemEventId>::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* eventString = 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, eventString) ;

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

	// Clean up
	delete pMsg ;
}
Example #10
0
void OutputListener::SendOutput(smlWorkingMemoryEventId eventId, AgentSML* pAgentSML, int /*outputMode*/, io_wme* io_wmelist)
{
    if (eventId != smlEVENT_OUTPUT_PHASE_CALLBACK)
    {
        return ;
    }
    
    // Get the first listener for this event (or return if there are none)
    ConnectionListIter connectionIter ;
    if (!EventManager<smlWorkingMemoryEventId>::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 ;
    
    // Build the SML message we're doing to send.
    soarxml::ElementXML* pMsg = pConnection->CreateSMLCommand(sml_Names::kCommand_Output) ;
    
    // Add the agent parameter and as a side-effect, get a pointer to the <command> tag.  This is an optimization.
    ElementXML_Handle hCommand = pConnection->AddParameterToSMLCommand(pMsg, sml_Names::kParamAgent, pAgentSML->GetName()) ;
    soarxml::ElementXML command(hCommand) ;
    
    // We are passed a list of all wmes in the transitive closure (TC) of the output link.
    // We need to decide which of these we've already seen before, so we can just send the
    // changes over to the client (rather than sending the entire TC each time).
    
    // Reset everything in the current list of tags to "not in use".  After we've processed all wmes,
    // any still in this state have been removed.
    for (OutputTimeTagIter iter = m_TimeTags.begin() ; iter != m_TimeTags.end() ; iter++)
    {
        iter->second = false ;
    }
    
    // Start with the output link itself
    // The kernel seems to only output this itself during link initialization
    // and we might be connecting up after that.  Including it twice will not hurt on the client side.
    output_link* ol = pAgentSML->GetSoarAgent()->existing_output_links ;    // This is technically a list but we only support one output link
    TagWme* pOutputLinkWme = OutputListener::CreateTagWme(pAgentSML, ol->link_wme) ;
    command.AddChild(pOutputLinkWme) ;
    
    for (io_wme* wme = io_wmelist ; wme != NIL ; wme = wme->next)
    {
        // Build the list of WME changes
        uint64_t timeTag = wme->timetag ;
        
        // See if we've already sent this wme to the client
        OutputTimeTagIter iter = m_TimeTags.find(timeTag) ;
        
        if (iter != m_TimeTags.end())
        {
            // This is a time tag we've already sent over, so mark it as still being in use
            iter->second = true ;
            continue ;
        }
        
        // If we reach here we need to send the wme to the client and add it to the list
        // of tags currently in use.
        m_TimeTags[timeTag] = true ;
        
        // Create the wme tag
        TagWme* pTag = CreateTagIOWme(pAgentSML, wme) ;
        
        // Add it as a child of the command tag
        command.AddChild(pTag) ;
    }
    
    // At this point we check the list of time tags and any which are not marked as "in use" must
    // have been deleted, so we need to send them over to the client as deletions.
    for (OutputTimeTagIter iter = m_TimeTags.begin() ; iter != m_TimeTags.end() ;)
    {
        // Ignore time tags that are still in use.
        if (iter->second == true)
        {
            // We have to do manual iteration because we're deleting elements
            // as we go and that invalidates iterators if we're not careful.
            iter++ ;
            continue ;
        }
        
        uint64_t timeTag = iter->first ;
        
        // Create the wme tag
        TagWme* pTag = new TagWme() ;
        
        // For deletions we just send the time tag
        pTag->SetTimeTag(static_cast<int64_t>(timeTag)) ;
        pTag->SetActionRemove() ;
        
        // Add it as a child of the command tag
        command.AddChild(pTag) ;
        
        // Delete the entry from the time tag map
        m_TimeTags.erase(iter++);
    }
    
    // This is important.  We are working with a subpart of pMsg.
    // If we retain ownership of the handle and delete the object
    // it will release the handle...deleting part of our message.
    command.Detach() ;
    
    smlWorkingMemoryEventId eventID = smlEVENT_OUTPUT_PHASE_CALLBACK ;
    
#ifdef _DEBUG
    // Convert the XML to a string so we can look at it in the debugger
    char* pStr = pMsg->GenerateXMLString(true) ;
#endif
    
    // Send the message out
    AnalyzeXML response ;
    SendEvent(pAgentSML, pConnection, pMsg, &response, connectionIter, GetEnd(eventID)) ;
    
#ifdef _DEBUG
    pMsg->DeleteString(pStr) ;
#endif
    
    // Clean up
    delete pMsg ;
}
Example #11
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 #12
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 ;
}