/// <summary> /// Called during incoming event processing and as a result of a client-initiated Refresh. /// Forwards to each server instance for further processing. /// </summary> /// <param name="Source"></param> /// <param name="Condition"></param> /// <param name="cond"></param> public void NotifyClients(string Source, string Condition, OPCCondition cond) { OnEventClass OEClass = new OnEventClass(Source, Condition, cond); foreach (ComAeProxy s in m_EvServerSet) { s.ProcessNewEvent(OEClass); } }
/// <summary> /// Force a refresh for all active conditions and inactive, unacknowledged conditions whose event notifications match the filter of the event subscription. /// </summary> /// <param name="dwConnection">The OLE Connection number returned from IConnectionPoint::Advise. This is passed to help the server determine which OPC event sink to call when the request completes.</param> public void Refresh(int dwConnection) { try { if (m_RefreshID != 0 || m_RefreshQ.Count != 0) { throw ComUtils.CreateComException("Refresh", ResultIds.E_BUSY); } m_RefreshID = dwConnection; // Foe each source walk through all associated conditions. If the condition is "refreshable", i.e. Active or // inactive/unacknowledged, then create an event and push it on to the subscription's refresh queue SourceMap sourceMap = SourceMap.TheSourceMap; foreach (KeyValuePair <string, ConditionMap> kvp in sourceMap) { string sourceName = kvp.Key; ConditionMap conditionMap = kvp.Value; foreach (KeyValuePair <string, OPCCondition> kvpCond in conditionMap) { string conditionName = kvpCond.Key; OPCCondition cond = kvpCond.Value; if (cond.IsEnabled() && (cond.IsActive() || !cond.IsAcked())) { OnEventClass OEClass = new OnEventClass(sourceName, conditionName, cond); if (MatchesFilter(OEClass)) { m_RefreshQ.Enqueue(OEClass); } } } } if (m_RefreshQ.Count > 0) { ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadWork), null); } else { CancelRefresh(dwConnection); } } catch (Exception e) { Utils.Trace(e, "Unexpected error in Refresh"); throw ComUtils.CreateComException(e); } }
/// <summary> /// If the event type is CONDITION_EVENT then update any existing record of the condition and adjust /// state and change mask. /// </summary> /// <param name="EventNotification"></param> /// <param name="areas"></param> public void ProcessEventNotificationList(EventNotification EventNotification, string[] areas) { SourceMap sourceMap = SourceMap.TheSourceMap; try { lock (sourceMap) { OPCCondition cond; if (EventNotification.EventType == OpcRcw.Ae.Constants.CONDITION_EVENT) { ConditionMap conditionMap; if (sourceMap.TryGetValue(EventNotification.SourceID, out conditionMap) == false) { conditionMap = new ConditionMap(); sourceMap.Add(EventNotification.SourceID, conditionMap); } if (conditionMap.TryGetValue(EventNotification.ConditionName, out cond) == false) { cond = new OPCCondition(); cond.EventType = EventNotification.EventType; conditionMap.Add(EventNotification.ConditionName, cond); } ProcessCondition(EventNotification, areas, cond); // When the condition has transitioned to Acked (if ack required) and inactive or disabled // then remove it from the condition source/condition database if ((!cond.IsActive() || !cond.IsEnabled()) && (cond.IsAcked() || !cond.AckRequired)) { conditionMap.Remove(EventNotification.ConditionName); } } else // a tracking or simple event { cond = new OPCCondition(); cond.EventType = EventNotification.EventType; ProcessCondition(EventNotification, areas, cond); } } } catch (Exception e) { Utils.Trace(e, "Unexpected error in ProcessEventNotificationList"); } }
/// <summary> /// Constructor -- initializes all members /// </summary> /// <param name="wszSource"></param> /// <param name="wszCondition"></param> /// <param name="cond"></param> public OnEventClass(string wszSource, string wszCondition, OPCCondition cond) { m_oes.pEventAttributes = IntPtr.Zero; m_oes.dwNumEventAttrs = 0; m_oes.wChangeMask = cond.ChangeMask; m_oes.wNewState = cond.NewState; m_oes.szSource = wszSource; m_oes.ftTime = ComUtils.GetFILETIME(cond.Time); m_oes.szMessage = cond.Message; m_oes.dwEventType = cond.EventType; m_oes.dwEventCategory = cond.EventCategory; m_oes.dwSeverity = cond.Severity; m_oes.szConditionName = wszCondition; m_oes.szSubconditionName = cond.SubconditionName; m_oes.wQuality = cond.Quality; m_oes.bAckRequired = cond.AckRequired ?1:0; m_oes.ftActiveTime = ComUtils.GetFILETIME(cond.SubconditionTime); m_oes.dwCookie = cond.Cookie; m_oes.szActorID = cond.ActorID; m_EventAttributes = (Dictionary <int, object>)DeepCopy(cond.EventAttributes); }
/// <summary> /// Values of existing CONDITION events are updated the forwarded to TheGlobal for further processing /// per subscription per server instance /// </summary> /// <param name="EventNotification"></param> /// <param name="areas"></param> /// <param name="newCond"></param> private void ProcessCondition(EventNotification EventNotification, string[] areas, OPCCondition newCond) { try { newCond.ClearChangeMask(); newCond.SetMessage(EventNotification.Message); newCond.SetSeverity(EventNotification.Severity); newCond.SetSubconditionName(EventNotification.SubConditionName); newCond.SetActive((EventNotification.NewState & OpcRcw.Ae.Constants.CONDITION_ACTIVE) != 0); // The UA server generates unique identifiers in the form of guids. how to map these to COM-style cookies? // For now, just increment a static DWORD counter from the OPCCondition constructor and assign //newCond.Cookie = EventNotification.Cookie; newCond.SetTime(EventNotification.ActiveTime); newCond.EventId = EventNotification.EventId; newCond.EventCategory = EventNotification.EventCategory; newCond.SetQuality((short)EventNotification.Quality); newCond.ActorID = EventNotification.EventType == OpcRcw.Ae.Constants.TRACKING_EVENT ? EventNotification.ActorID : ""; newCond.SetEnable((EventNotification.NewState & OpcRcw.Ae.Constants.CONDITION_ENABLED) != 0); newCond.SetIsAcked((EventNotification.NewState & OpcRcw.Ae.Constants.CONDITION_ACKED) != 0); newCond.SetAckRequired(EventNotification.AckRequired); newCond.ConditionId = EventNotification.ConditionId; newCond.AcknowledgeMethod = EventNotification.AcknowledgeMethod; OPCSubcondition subCond = new OPCSubcondition(newCond.SubconditionName, newCond.Message, "", newCond.Severity); newCond.push_back_subcondition(subCond); for (int i = 0; i < areas.Length; i++) { newCond.push_back_area(areas[i]); } // Insert standard attribute "AckComment" newCond.push_back_attrval(Global.TheGlobal.StdAttrIds[0], newCond.AckComment); // Insert standard attribute "Areas" newCond.push_back_attrval(Global.TheGlobal.StdAttrIds[1], areas); foreach (KeyValuePair <int, object> kvp in EventNotification.EventAttributes) { newCond.push_back_attrval(kvp.Key, kvp.Value); } if (newCond.IsEnabled()) { lock (Global.TheGlobal) { Global.TheGlobal.NotifyClients(EventNotification.SourceID, EventNotification.ConditionName, newCond); } } } catch (Exception e) { Utils.Trace(e, "Unexpected error in ProcessCondition"); } // Utils.Trace("ProcessCondition - END Source: {0}, condition: {1}, conditionstate: {2}", // EventNotification.SourceID, EventNotification.ConditionName, newCond.NewState); }