// 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 ; }
// 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 ; }
// 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 ; }
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 ; }
// 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 ; }
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 ; } }
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() ); }
// 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 ; }
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 ; }
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 ; }
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 ; }