}// HeartBeat() // // ******************************************************************* // **** ConnectData() **** // ******************************************************************* /// <summary> /// This is called for each cell with an RTD link. It's called only the first time they /// are entered into the cell (or at startup). /// </summary> /// <param name="TopicID">Excel's id# for this RTD link.</param> /// <param name="strings">Arguments in RTD command passed by excel</param> /// <param name="GetNewValues"></param> /// <returns>Object to be displayed in cell now.</returns> object IRtdServer.ConnectData(int TopicID, ref Array strings, ref bool GetNewValues) { object returnObject = string.Empty; if (strings.Length > 0) { string[] arguments = new string[strings.Length]; // Read arguments provided by Excel user: strings.CopyTo(arguments, 0); TopicBase topic = new TopicBase(TopicID, arguments); m_Topics.TryAdd(topic.TopicID, topic); string response = OnRtdConnectData(ref topic); // create topic, add to list. double x; if (Double.TryParse(response, out x)) { returnObject = x; } else { returnObject = response; } GetNewValues = true; // tells Excel it will have to call back to get new values. } else { GetNewValues = false; // tells Excel it will have to call back to get new values. } if ((!m_Topics.IsEmpty) && (!m_Timer.Enabled)) // Make sure timer for message throttleing is running. { m_Timer.Interval = ThrottleTime; m_Timer.Start(); } return(returnObject); }// ConnectData()
} // OnRtdServerTerminate() // // /// <summary> /// When a new topic is created (in the base class), we separate these topics into /// "UserTopics" (intended for client on other end of the socket) and those we keep as private "ServerTopics." /// The private "ServerTopics" allow the Excel user to control the behavior of this object and its services. /// The base class created a new TopicBase object to hold info requested from excel. /// This new TopicBase is analyzed and stored here as a "UserTopic" or "ServerTopic". /// ServerTopics are forwarded to ProcessNewServerTopic(), UserTopics are serialized and sent to socket client. /// </summary> /// <returns>a value immediately displayed by excel.</returns> protected override string OnRtdConnectData(ref TopicBase newTopic) { string responseString = string.Empty; // default string topicName = newTopic.Arguments[0].Trim().ToLower(); // first element must always exist! if (topicName.StartsWith(TopicKey_Base)) { // this is a server topic. Store it. if (!m_ServerTopics.ContainsKey(topicName)) // if this topicName is the first, create place for them. { m_ServerTopics.TryAdd(topicName, new ConcurrentDictionary <int, TopicBase>()); } ConcurrentDictionary <int, TopicBase> topicList; if (m_ServerTopics.TryGetValue(topicName, out topicList)) // get all topics with this same name. { topicList.TryAdd(newTopic.TopicID, newTopic); } responseString = ProcessNewServerTopic(topicName, ref newTopic);// initial processing of this server topic. } else { m_UserTopics.TryAdd(newTopic.TopicID, newTopic); // This is a user topic. if (m_Socket.IsConversing) { m_Socket.Send(newTopic.Serialize()); // Send new topic to socket. } } // Exit return(responseString); // what appears in cell immediately. }// OnRtdConnectData()
}// ConnectData() // // // ******************************************************************* // **** DisconnectData() **** // ******************************************************************* /// <summary> /// Excel user no longer wants to follow this topic. /// </summary> /// <param name="TopicID">id of topic to drop.</param> void IRtdServer.DisconnectData(int TopicID) { TopicBase topicToRemove = null; if (m_Topics.TryRemove(TopicID, out topicToRemove)) { OnRtdDisconnect(topicToRemove); } if (m_Topics.IsEmpty && m_Timer != null && m_Timer.Enabled) { m_Timer.Stop(); // last RTD link on sheet is removed. Shut off timer. } }// DisconnectData()
}// OnRtdConnectData() // protected override void OnRtdDisconnect(TopicBase topicToRemove) { // See if topic is a ServerTopic TopicBase topic = null; foreach (string topicName in m_ServerTopics.Keys) { ConcurrentDictionary <int, TopicBase> topicList; if (m_ServerTopics.TryGetValue(topicName, out topicList) && topicList.ContainsKey(topicToRemove.TopicID)) { topicList.TryRemove(topicToRemove.TopicID, out topic); break; } } if (topic == null) // if topic is not null, it was a server topic. { // We did not find this topic in ServerTopics. Check UserTopics. if (m_UserTopics.ContainsKey(topicToRemove.TopicID)) { m_UserTopics.TryRemove(topicToRemove.TopicID, out topic); m_Socket.Send(topicToRemove.SerializeCurrent(MessageType.TopicRemoved)); } } }
/// <summary> /// Called when a excel rtd link is deleted thereby removing the need to /// continue updating this topic. /// </summary> /// <param name="topicToRemove"></param> protected virtual void OnRtdDisconnect(TopicBase topicToRemove) { }
/// <summary> /// After startup, when excel detects a new RTD link in a cell, this event is fired /// by excel thread. /// This method must create a new topic and add it to the list. /// </summary> /// <param name="arguments"></param> /// <returns>value to be displayed immediately in excel cell.</returns> protected virtual string OnRtdConnectData(ref TopicBase newTopic) { return(string.Empty); // what appears in cell immediately. }
// // #endregion//Constructors #region Private Methods // ***************************************************************** // **** Private Methods **** // ***************************************************************** // // /// <summary> /// This is called whenever a new topic arrives from the RtdServerBase that's /// determined to be a "server topic". Its then passed to this method by the excel thread. /// This is only called once (upon new topic creation), so is useful for initialization. /// Threading: Called by excel thread. /// </summary> /// <returns>immediate string response</returns> private string ProcessNewServerTopic(string topicName, ref TopicBase newTopic) { string responseString = string.Empty; switch (topicName) { case TopicKey_Status: // Parameters are passed in key/value pairs: RTD("bre.talker","","bret_connect" string ipAddress = string.Empty; int portID = 6012; int throttleTime = base.ThrottleTime; // msec default throttle time. for (int i = 1; i < newTopic.Arguments.Length; ++i) { string[] parts = newTopic.Arguments[i].Split(DelimiterKeyValues); // keeping empty args here. if (parts.Length == 2) { switch (parts[0].ToLower()) { case "port": int.TryParse(parts[1], out portID); break; case "throttle": int.TryParse(parts[1], out throttleTime); throttleTime = Math.Max(200, throttleTime); if (Convert.ToInt32(Math.Round(m_Timer.Interval)) != throttleTime) { base.ThrottleTime = throttleTime; m_Timer.Stop(); m_Timer.Interval = base.ThrottleTime; m_Timer.Start(); } break; case "ipaddress": ipAddress = parts[1]; break; default: break; } //switch } } if (m_Socket.IsServing) { // After stopping, we can restart server to listen on another port, etc. m_Socket.StopServer(); // Disconnect any conversations. Stop listener, and restart it on another port. } try { if (!string.IsNullOrEmpty(ipAddress)) // Start server { m_Socket.StartServer(portID, ipAddress); } else { m_Socket.StartServer(portID); } if (m_Socket.IsServing) { responseString = "Listening"; // report that server is successfully listening now... } else { responseString = "Not listening"; // report that server failed to start listener. ReportWarning("ProcessNewServerTopic code=1: Failed to start listener."); } } catch (Exception) { responseString = "Listen Exception"; } break; case TopicKey_ConnectionTime: responseString = "0"; break; case TopicKey_UpdateCounter: responseString = "0"; break; case TopicKey_LastWarning: responseString = "None"; break; case TopicKey_LastWarningTime: responseString = DateTime.Now.ToShortTimeString(); break; default: break; } //switch return(responseString); } // ProcessNewServerTopic()