/// <summary> /// Send a message to the engine /// </summary> /// <param name="message"></param> public void sendMessage(TMsgHeader message) { TNativeMsgHeader msgPtr = new TNativeMsgHeader(); uint nBytes = message.nDataBytes; try { msgPtr.version = message.version; msgPtr.msgType = message.msgType; msgPtr.from = message.from; msgPtr.to = message.to; msgPtr.msgID = message.msgID; msgPtr.toAck = message.toAck; msgPtr.nDataBytes = nBytes; if (nBytes == 0) { msgPtr.dataPtr = IntPtr.Zero; } else { msgPtr.dataPtr = Marshal.AllocHGlobal((int)nBytes); Marshal.Copy(message.dataPtr, 0, msgPtr.dataPtr, (int)nBytes); } _callengine(ref componentID, ref msgPtr); } finally { if (nBytes > 0) { Marshal.FreeHGlobal(msgPtr.dataPtr); } } }
// ----------------------------------------------------------------------- /// <summary> /// Handler for all QuerySetValue messages. /// </summary> /// <param name="message"></param> public void onQuerySetValue(TMsgHeader message) { int iPropertyID; uint iReplyTo; uint msgID; uint dataSize; string DDML; interpreter.loadMessage(message); //take ownership of this msg iReplyTo = message.from; msgID = message.msgID; iPropertyID = interpreter.getIntField(Msgs.MSG_QUERYSET_ID); // Parse the request message byte[] dataPtr = new byte[1]; dataSize = interpreter.getValueField(Msgs.MSG_QUERYSET_VALUE, ref dataPtr); DDML = interpreter.getTextField(Msgs.MSG_QUERYSET_TYPE); //find the property by ID in the regNames list String propKey = IDToPropertyName(iPropertyID); if (propKey.Length > 0) { Reg r = regNames[propKey]; r.data.setData(dataPtr, dataSize, 0); // change the value of the variable. sendReplySetValueSuccess(msgID, iReplyTo, true); } else { sendReplySetValueSuccess(msgID, iReplyTo, false); } }
/// <summary> /// Handler for all QueryValue messages. /// </summary> /// <param name="message"></param> public void onQueryValue(TMsgHeader message) { String sDDML = ""; uint iReplyTo = message.from; //the router interpreter.loadMessage(message); //expect the wrapper to trigger a returnValue message int iPropertyID = interpreter.getIntField(Msgs.MSG_QUERYVALUE_ID); int requestedByID = interpreter.getIntField(Msgs.MSG_QUERYVALUE_REQBY); //source of the request //find the property by ID in the regNames list String propKey = IDToPropertyName(iPropertyID); if (propKey.Length > 0) { Reg r = regNames[propKey]; byte[] data = new byte[r.data.sizeBytes()]; r.data.getData(ref data); //get the value sDDML = r.ddml; sendReplyValue(message.msgID, (uint)requestedByID, sDDML, data, data.Length); } else { string errorMsg = string.Format("onQueryValue(): Cannot find property {0}", iPropertyID); error(errorMsg, true); } }
// ----------------------------------------------------------------------- /// <summary> /// Handler for all Event messages. /// </summary> /// <param name="message"></param> // ----------------------------------------------------------------------- public void onEvent(TMsgHeader message) { interpreter.loadMessage(message); //take ownership of this msg int id = interpreter.getIntField(Msgs.MSG_EVENT_ID); byte[] dataPtr = new byte[1]; uint dataSize = interpreter.getValueField(Msgs.MSG_EVENT_PARAMS, ref dataPtr); uint publBy = (uint)interpreter.getIntField(Msgs.MSG_EVENT_PUBLISHEDBY); string DDML = interpreter.getTextField(Msgs.MSG_EVENT_TYPE); /* TODO implement event handling * Reg* reg = (Reg*) cmpevent.ID; * Packable& data = *(reg->data); * * if (cmpevent.ID == tickID) * { * unpack(messageData, tick); * messageData.reset(); * unpack(messageData, cmpevent); * haveWrittenToStdOutToday = false; * } * // unpack the data - this will unpack and then call the function. * data.unpack(messageData, cmpevent.ddml); */ }
//============================================================================ /// <summary> /// Override the init1 so that the Apsim component can have access to the SDML script. /// </summary> /// <param name="msg"></param> /// <param name="SDML"></param> /// <param name="FQN"></param> //============================================================================ protected override void doInit1(TMsgHeader msg, string SDML, string FQN) { Comp.ComponentID = msg.to; //ensure this is set Comp.Name = FQN; Comp.setScript(SDML); base.doInit1(msg, SDML, FQN); }
/* bool readFromSection(XMLNode::iterator initData, * XMLNode::iterator sectionData, * const std::string& parName, * Convertable* value); * void readAndDemangleScripts(std::map<std::string, std::string> &scripts, XMLNode::iterator &data); * void replaceManagerMacros(std::string& contents, XMLNode ui); */ /// <summary> /// Terminate the simulation. /// </summary> private void terminate() { //create and send the msg to the parent interpreter.createMessage(Msgs.MSG_TERMINATE, parentID); TMsgHeader newMsg = interpreter.getMsg(); sendMessage(newMsg); }
//============================================================================ /// <summary> /// Send an acknowledgement message /// </summary> /// <param name="msgTo">Component ID to which complete is sent.</param> /// <param name="msgID">ID of the message being acknowledged.</param> //============================================================================ public void sendComplete(uint msgTo, uint msgID) { interpreter.setField(Msgs.MSG_COMPLETE_ACKID, msgID); interpreter.createMessage(Msgs.MSG_COMPLETE, msgTo); //send back to the sender TMsgHeader ackMsg = interpreter.getMsg(); sendMessage(ackMsg); }
/// <summary> /// Handler for Init2 message. /// </summary> /// <param name="message"></param> public void onInit2(TMsgHeader message) { if ((tickID = nameToRegistrationID("tick", RegistrationKind.respondToEventReg)) == 0) { // We need a tick to determine when to write the "day = ..." heading to a summary file. tickID = RegisterWithPM("tick", "", "", RegistrationKind.respondToEventReg, "<type/>"); } }
//============================================================================== /// <summary> /// Send a message up to the owning system so it can be routed throughout /// the simulation. Overridden to allow the message data to be converted /// to a native pointer. /// </summary> /// <param name="msg">Message that will be sent to the engine.</param> //============================================================================== protected override void sendMessageToEngine(TMsgHeader msg) { // Copy from TMsgHeader to TNativeMsgHeader. // The difference is in the nature of the data pointer. try { TNativeMsgHeader msgPtr = new TNativeMsgHeader(); uint nBytes = msg.nDataBytes; try { msgPtr.version = msg.version; msgPtr.msgType = msg.msgType; msgPtr.from = msg.from; msgPtr.to = msg.to; msgPtr.msgID = msg.msgID; msgPtr.toAck = msg.toAck; msgPtr.nDataBytes = nBytes; if (nBytes == 0) { msgPtr.dataPtr = IntPtr.Zero; } else { /* * if (nBytes > memAllocSize) * { * * if (memAllocSize > 0) * Marshal.FreeHGlobal(nativeMem); * nativeMem = Marshal.AllocHGlobal((int)nBytes); * memAllocSize = nBytes; * } * msgPtr.dataPtr = nativeMem; */ msgPtr.dataPtr = Marshal.AllocHGlobal((int)nBytes); Marshal.Copy(msg.dataPtr, 0, msgPtr.dataPtr, (int)nBytes); } IntPtr dummy = (IntPtr)0; msgNativeDestFunction(ref dummy, ref msgPtr); //will take a copy } finally { if (nBytes > 0) { Marshal.FreeHGlobal(msgPtr.dataPtr); } } } catch (SEHException) { // This is the most likely exception to be caught here, but normally it will } // be handled elsewhere on the native side, and "External component has thrown an exception" is not a very helpful message catch (Exception e) { StringBuilder exmsg = new StringBuilder("sendMessageToEngine() failed "); exmsg.Append(e.Message); Console.WriteLine(exmsg); } }
//private uint memAllocSize = 0; //private IntPtr nativeMem; //============================================================================== /// <summary> /// Send a message up to the owning system so it can be routed throughout /// the simulation. Overridden to allow the message data to be converted /// to a native pointer. This enables calling from embedded Microsoft Frameworks or Mono. /// The message itself then needs to be converted from a TNativeMsgHeader to a TMsgHeader, /// which includes taking a copy of the data referenced by the data pointer /// </summary> /// <param name="inVal">Message that will be sent to the engine. /// The value passed in, although described as a "ulong", is actually /// a pointer to a native TMsgHeader /// </param> //============================================================================== public void handleMessage(ulong inVal) { // We need to copy the "native" message point into a managed object. TNativeMsgHeader src = (TNativeMsgHeader)Marshal.PtrToStructure((IntPtr)inVal, typeof(TNativeMsgHeader)); TMsgHeader msgPtr = TMessageInterpreter.NativeMsgToManagedMsg(ref src); handleMessage(msgPtr); //calls the base class function }
protected void sendReplyValue(uint queryMsgID, uint replyTo, string sParamType, byte[] aParams, int paramsSize) { interpreter.setField(Msgs.MSG_REPLYVALUE_QUERYID, queryMsgID); //ID of queryValue interpreter.setField(Msgs.MSG_REPLYVALUE_TYPE, sParamType); //DDML type interpreter.setField(Msgs.MSG_REPLYVALUE_VALUE, aParams, (uint)paramsSize); TMsgHeader msg = interpreter.createMessage(Msgs.MSG_REPLYVALUE, replyTo); sendMessage(msg); }
//============================================================================== /// <summary> /// Send a message up to the owning system so it can be routed throughout /// the simulation. Overridden to allow the message data to be converted /// to a native pointer. /// </summary> /// <param name="msg">Message that will be sent to the engine.</param> //============================================================================== protected override void sendMessageToEngine(TMsgHeader msg) { // Copy from TMsgHeader to TNativeMsgHeader. // The difference is in the nature of the data pointer. try { TNativeMsgHeader msgPtr = new TNativeMsgHeader(); uint nBytes = msg.nDataBytes; try { msgPtr.version = msg.version; msgPtr.msgType = msg.msgType; msgPtr.from = msg.from; msgPtr.to = msg.to; msgPtr.msgID = msg.msgID; msgPtr.toAck = msg.toAck; msgPtr.nDataBytes = nBytes; if (nBytes == 0) { msgPtr.dataPtr = IntPtr.Zero; } else { /* * if (nBytes > memAllocSize) * { * * if (memAllocSize > 0) * Marshal.FreeHGlobal(nativeMem); * nativeMem = Marshal.AllocHGlobal((int)nBytes); * memAllocSize = nBytes; * } * msgPtr.dataPtr = nativeMem; */ msgPtr.dataPtr = Marshal.AllocHGlobal((int)nBytes); Marshal.Copy(msg.dataPtr, 0, msgPtr.dataPtr, (int)nBytes); } uint dummy = 0; msgNativeDestFunction(ref dummy, ref msgPtr); //will take a copy } finally { if (nBytes > 0) { Marshal.FreeHGlobal(msgPtr.dataPtr); } } } catch (Exception e) { StringBuilder exmsg = new StringBuilder("sendMessageToEngine() failed "); exmsg.Append(e.Message); Console.WriteLine(exmsg); } }
// internal stuff. // ----------------------------------------------------------------------- /// <summary> /// Called for all incoming messages. /// </summary> /// <param name="message"></param> // ----------------------------------------------------------------------- private void messageToLogic(TMsgHeader message) { // We need to keep track of bits of the message because the FARMWI$E infrastructure // doesn't guarantee that a message is still valid at the end of this method. // eg. it deletes the Init1 message before we get to test the ack flag at the bottom. bool ack = message.toAck == 1; uint msgID = message.msgID; uint msgFrom = message.from; interpreter.loadMessage(message); //take ownership of this msg try { switch (message.msgType) { case Msgs.MSG_EVENT: onEvent(message); break; case Msgs.MSG_INIT1: { String SDML = interpreter.getTextField(Msgs.MSG_INIT1_SDML); String FQN = interpreter.getTextField(Msgs.MSG_INIT1_FQN); onInit1(SDML, FQN); //do init1 processing } break; case Msgs.MSG_INIT2: onInit2(message); break; case Msgs.MSG_QUERYVALUE: onQueryValue(message); break; case Msgs.MSG_QUERYSET: onQuerySetValue(message); break; case Msgs.MSG_RETURNVALUE: case Msgs.MSG_RETURNINFO: case Msgs.MSG_NOTIFYSET: case Msgs.MSG_REPLYSET: { TMsgHeader copyMsg = new TMsgHeader(); copyMsg = message; messages.Add(copyMsg); } break; } // if acknowledgement is required, then give it. if (ack) { sendComplete(msgFrom, msgID); } } catch (Exception err) { error(err.Message, true); } }
// ----------------------------------------------------------------------- /// <summary> /// Set the value of a variable in another component. /// </summary> /// <param name="name"></param> /// <param name="units"></param> /// <param name="data"></param> // ----------------------------------------------------------------------- public void set(String name, String units, ref TDDMLValue data) { clearMessages(); int id = nameToRegistrationID(name, RegistrationKind.setReg); bool alreadyRegistered = (id != 0); if (!alreadyRegistered) { id = RegisterWithPM(name, units, "", RegistrationKind.setReg, data.asDDML()); } TMsgHeader msg = buildRequestSetMsg(id, data); if (msg.msgID != 0) { sendMessage(msg); } }
public void error(String errorMessage, bool isFatal) { String sMsg = errorMessage + "\n"; sMsg += "Component name: " + name + "\n"; //use the new error msg interpreter.setField(Msgs.MSG_ERROR_FATAL, isFatal); interpreter.setField(Msgs.MSG_ERROR_MESSAGE, sMsg); //its ID TMsgHeader msg = interpreter.createMessage(Msgs.MSG_ERROR, parentID); sendMessage(msg); if (isFatal) { terminate(); //may not be necessary ??? errorHasOccurred = true; } }
//============================================================================== /// <summary> /// Build a requestSetvalue message. /// </summary> /// <param name="iLocalSetPropID">Local identifier for the property.</param> /// <param name="Value">Value to which to set the property.</param> /// <returns>The new message. If this function fails then the returned message.msgID==0</returns> //============================================================================== protected TMsgHeader buildRequestSetMsg(int iLocalSetPropID, TTypedValue Value) { uint valSize; TSetterProperty localProp = null; TMsgHeader newMsg = new TMsgHeader(); newMsg.msgID = 0; //default to fault valSize = Value.sizeBytes(); byte[] valPtr = new byte[valSize]; Value.getData(ref valPtr); //destination is the owning system interpreter.setField(Msgs.MSG_REQUESTSET_ID, iLocalSetPropID); //local reg property ID interpreter.setField(Msgs.MSG_REQUESTSET_TYPE, localProp.sDDML); interpreter.setField(Msgs.MSG_REQUESTSET_VALUE, valPtr, valSize); newMsg = interpreter.createMessage(Msgs.MSG_REQUESTSET, parentID); return(newMsg); }
// ----------------------------------------------------------------------- /// <summary> /// Get the value of a variable from another component. /// </summary> /// <param name="name"></param> /// <param name="units"></param> /// <param name="optional"></param> /// <param name="data"></param> /// <returns></returns> // ----------------------------------------------------------------------- public Boolean get(String name, String units, Boolean optional, ref TDDMLValue data) { clearMessages(); // see if we have an array specifier. //// TODO: fix the ability to use array specifiers //// ArraySpecifier* arraySpecifier = ArraySpecifier::create(name); String nameWithoutArraySpec = name; //// if (arraySpecifier != NULL) //// nameWithoutArraySpec = arraySpecifier->variableName(); int id = nameToRegistrationID(nameWithoutArraySpec, RegistrationKind.getReg); Boolean alreadyRegistered = (id != 0); if (!alreadyRegistered) { id = RegisterWithPM(nameWithoutArraySpec, units, "", RegistrationKind.getReg, data.asDDML()); } interpreter.setField(Msgs.MSG_GETVALUE_ID, id); //the driver ID code TMsgHeader msg = interpreter.createMessage(Msgs.MSG_GETVALUE, parentID); msg.toAck = 1; //always sendMessage(msg); String errorMsg = ""; if (messages.Count == 0) { errorMsg = "No component responded to a 'get' for variable: " + name; } else if (messages.Count == 1) { interpreter.loadMessage(messages[0]); //take ownership of this msg int iCompID = interpreter.getIntField(Msgs.MSG_RETURNVALUE_COMPID); int iPropertyID = interpreter.getIntField(Msgs.MSG_RETURNVALUE_ID); string DDML = interpreter.getTextField(Msgs.MSG_RETURNVALUE_TYPE); byte[] dataPtr = new byte[1]; uint dataSize = interpreter.getValueField(Msgs.MSG_RETURNVALUE_VALUE, ref dataPtr); data.setData(dataPtr, dataSize, 0); ////if (arraySpecifier != NULL) ////arraySpecifier->summariseData(returnMessageData, returnValue.ddml); } else if (messages.Count > 1) { errorMsg = "Too many components responded to a 'get' for variable: " + name; } if (errorMsg != "") { if (!optional) { throw (new ApplicationException(errorMsg)); } return(false); } else { return(true); } }