/// <summary> /// Updates the display with a new value for a monitored variable. /// </summary> private void MonitoredItem_Notification(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e) { if (this.InvokeRequired) { this.BeginInvoke(new MonitoredItemNotificationEventHandler(MonitoredItem_Notification), monitoredItem, e); return; } try { EventFieldList notification = e.NotificationValue as EventFieldList; if (notification == null) { return; } // check the type of event. NodeId eventTypeId = ClientUtils.FindEventType(monitoredItem, notification); // ignore unknown events. if (NodeId.IsNull(eventTypeId)) { return; } // construct the audit object. SystemCycleStatusEventState status = ClientUtils.ConstructEvent( m_session, monitoredItem, notification, m_knownEventTypes, m_eventTypeMappings) as SystemCycleStatusEventState; if (e == null) { return; } ListViewItem item = new ListViewItem(String.Empty); item.SubItems.Add(String.Empty); // Source item.SubItems.Add(String.Empty); // Type item.SubItems.Add(String.Empty); // CycleId item.SubItems.Add(String.Empty); // Step item.SubItems.Add(String.Empty); // Time item.SubItems.Add(String.Empty); // Message // look up the condition type metadata in the local cache. INode type = m_session.NodeCache.Find(status.TypeDefinitionId); // Source if (status.SourceName != null) { item.SubItems[0].Text = Utils.Format("{0}", status.SourceName.Value); } else { item.SubItems[0].Text = null; } // Type if (type != null) { item.SubItems[1].Text = Utils.Format("{0}", type); } else { item.SubItems[1].Text = null; } // CycleId if (status.CycleId != null) { item.SubItems[2].Text = Utils.Format("{0}", status.CycleId.Value); } else { item.SubItems[2].Text = null; } // Step if (status.CurrentStep != null && status.CurrentStep.Value != null) { item.SubItems[3].Text = Utils.Format("{0}", status.CurrentStep.Value.Name); } else { item.SubItems[3].Text = null; } // Time if (status.Time != null) { item.SubItems[4].Text = Utils.Format("{0:HH:mm:ss.fff}", status.Time.Value.ToLocalTime()); } else { item.SubItems[4].Text = null; } // Message if (status.Message != null) { item.SubItems[5].Text = Utils.Format("{0}", status.Message.Value); } else { item.SubItems[5].Text = null; } item.Tag = status; EventsLV.Items.Add(item); // adjust the width of the columns. for (int ii = 0; ii < EventsLV.Columns.Count; ii++) { EventsLV.Columns[ii].Width = -2; } } catch (Exception exception) { ClientUtils.HandleException(this.Text, exception); } }
override protected void MonitoredItem_Notification(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e) { try { var tEventTag = monitoredItem.Handle as TheOPCEvent; if (tEventTag == null) { TheBaseAssets.MySYSLOG.WriteToLog(78102, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyOPCServer.GetBaseThing().EngineName, $"Internal error: invalid monitored item handle in Event", eMsgLevel.l1_Error, $"{DisplayName} {GetNodeIdForLogs()} {EventInfo.AggregateRetainedConditions}")); return; } var tEventHost = tEventTag.GetHostThing(); if (tEventHost == null) { TheBaseAssets.MySYSLOG.WriteToLog(78102, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyOPCServer.GetBaseThing().EngineName, $"Event host thing not found", eMsgLevel.l1_Error, $"{DisplayName} {GetNodeIdForLogs()} {EventInfo.AggregateRetainedConditions}")); return; } EventFieldList notification = e.NotificationValue as EventFieldList; if (notification == null) { TheBaseAssets.MySYSLOG.WriteToLog(78102, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyOPCServer.GetBaseThing().EngineName, $"Internal error: notification is not an EventFieldList", eMsgLevel.l1_Error, $"{DisplayName} {GetNodeIdForLogs()} {EventInfo.AggregateRetainedConditions}")); return; } // check the type of event. NodeId eventTypeId = ClientUtils.FindEventType(monitoredItem, notification); if (MyOPCServer.EnableOPCDataLogging) { var logInfo = new Dictionary <string, object> { { "ReceiveTime", DateTimeOffset.Now }, { "TagId", DisplayName }, { "EventTypeId", eventTypeId }, { "Value", notification.EventFields.Aggregate("", (s, ef) => $"{s} [{ef.TypeInfo},{ef.Value}]") }, { "Server", notification.Message.PublishTime }, { "MonitoredItem", monitoredItem?.ClientHandle }, { "SequenceNumber", notification.Message?.SequenceNumber }, }; TheOPCTag.LogOPCData(logInfo, MyOPCServer.GetLogAddress(), $"{DisplayName} {GetNodeIdForLogs()} {EventInfo.AggregateRetainedConditions}"); } // ignore unknown events. if (NodeId.IsNull(eventTypeId)) { TheBaseAssets.MySYSLOG.WriteToLog(78102, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyOPCServer.GetBaseThing().EngineName, $"Unknown eventTypeId", eMsgLevel.l1_Error, $"{DisplayName} {GetNodeIdForLogs()} {EventInfo.AggregateRetainedConditions}: received {eventTypeId}")); return; } EventFilter filter = monitoredItem.Status.Filter as EventFilter; bool isRefreshEvent = false; var eventData = new Dictionary <string, object>(); DateTimeOffset sourceTime = DateTimeOffset.Now; string conditionId = null; bool? bRetain = null; int index = 0; foreach (var field in filter.SelectClauses) { var value = index < notification?.EventFields.Count ? notification?.EventFields[index].Value : null; if (value is ExtensionObject || value is ExtensionObject[]) { value = MyOPCServer.DecodeExtensionObjectToJson(value, out var ignored); } var name = field?.BrowsePath?.Count > 0 ? field.BrowsePath[0].Name : null; if (value is NodeId) { if (name == "EventType") { var eventType = value as NodeId; if (eventType == Opc.Ua.ObjectTypeIds.RefreshStartEventType) { TheBaseAssets.MySYSLOG.WriteToLog(78102, TSM.L(eDEBUG_LEVELS.ESSENTIALS) ? null : new TSM(MyOPCServer.GetBaseThing().EngineName, $"Received Refresh Start event", eMsgLevel.l4_Message, $"{DisplayName} {GetNodeIdForLogs()} {EventInfo.AggregateRetainedConditions}")); _currentConditionsByConditionId.Clear(); if (_bRefreshing) { // Two overlapping refresh starts received TheBaseAssets.MySYSLOG.WriteToLog(78102, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyOPCServer.GetBaseThing().EngineName, $"Received more than one Refresh Start event", eMsgLevel.l2_Warning, $"{DisplayName} {GetNodeIdForLogs()} {EventInfo.AggregateRetainedConditions}")); } _bRefreshing = true; _lastRefreshStartTime = DateTimeOffset.Now; isRefreshEvent = true; } else if (eventType == Opc.Ua.ObjectTypeIds.RefreshEndEventType) { TheBaseAssets.MySYSLOG.WriteToLog(78102, TSM.L(eDEBUG_LEVELS.ESSENTIALS) ? null : new TSM(MyOPCServer.GetBaseThing().EngineName, $"Received Refresh End event", eMsgLevel.l4_Message, $"{DisplayName} {GetNodeIdForLogs()} {EventInfo.AggregateRetainedConditions}")); if (!_bRefreshing) { TheBaseAssets.MySYSLOG.WriteToLog(78102, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyOPCServer.GetBaseThing().EngineName, $"Received Refresh End event without matching start event", eMsgLevel.l2_Warning, $"{DisplayName} {GetNodeIdForLogs()} {EventInfo.AggregateRetainedConditions}")); // refresh end received without refresh start } _bRefreshing = false; isRefreshEvent = true; if (EventInfo.AggregateRetainedConditions) { WriteAggregatedConditionsToProperty(tEventHost, TheCommonUtils.CDate(notification.Message.PublishTime)); } } } value = value.ToString(); } if (name == null) { if (field.TypeDefinitionId == Opc.Ua.ObjectTypeIds.BaseEventType) { name = "ConditionId"; } } if (name != null) { if (aliasMap.TryGetValue(index, out var alias)) { eventData[alias] = value; } else { eventData[name] = value; } switch (name) { case "ConditionId": conditionId = value?.ToString(); break; case "Retain": bRetain = TheCommonUtils.CBool(value); break; case "Time": sourceTime = TheCommonUtils.CDate(value); break; } } index++; } if (!isRefreshEvent) { var eventInfoProperties = EventInfo.GetPropertyNames(); var filteredEventProps = EventInfo.Properties?.Count > 0 ? eventData.Where(pk => !requiredEventFields.Contains(pk.Key) || eventInfoProperties.Contains(pk.Key)) //.Select(kv => //{ // if (aliasMap.TryGetValue(kv.Key, out var alias)) // { // return new KeyValuePair<string, object>(alias, kv.Value); // } // return kv; //}) .ToDictionary(kv => kv.Key, kv => kv.Value) : eventData; //if (_bRefreshing) //{ // filteredEventProps["Refresh"] = true; //} if (!EventInfo.AggregateRetainedConditions) { // Raw events // TODO avoid resending events due to a refresh? var eventAsJson = TheCommonUtils.SerializeObjectToJSONString(filteredEventProps); TheBaseAssets.MySYSLOG.WriteToLog(78102, TSM.L(eDEBUG_LEVELS.VERBOSE) ? null : new TSM(MyOPCServer.GetBaseThing().EngineName, $"Received raw event {eventAsJson}", eMsgLevel.l6_Debug, $"{DisplayName} {GetNodeIdForLogs()} {EventInfo.AggregateRetainedConditions}")); tEventHost.GetBaseThing().SetProperty(DisplayName, eventAsJson, TheCommonUtils.CDate(notification.Message.PublishTime)); } else { // Aggregated Condition State if (conditionId != null) { if (bRetain == false) { TheBaseAssets.MySYSLOG.WriteToLog(78102, TSM.L(eDEBUG_LEVELS.ESSENTIALS) ? null : new TSM(MyOPCServer.GetBaseThing().EngineName, $"Removed current event {conditionId}", eMsgLevel.l6_Debug, $"{DisplayName} {GetNodeIdForLogs()} {EventInfo.AggregateRetainedConditions}")); _currentConditionsByConditionId.RemoveNoCare(conditionId); } else { TheBaseAssets.MySYSLOG.WriteToLog(78102, TSM.L(eDEBUG_LEVELS.ESSENTIALS) ? null : new TSM(MyOPCServer.GetBaseThing().EngineName, $"Added current event {conditionId}", eMsgLevel.l6_Debug, $"{DisplayName} {GetNodeIdForLogs()} {EventInfo.AggregateRetainedConditions}")); _currentConditionsByConditionId[conditionId] = filteredEventProps; } if (!_bRefreshing) { WriteAggregatedConditionsToProperty(tEventHost, TheCommonUtils.CDate(notification.Message.PublishTime)); } } } // Legacy format: do we still need to support this? Existing event support was not really usable... //foreach (var eventField in eventData) //{ // string propertyName; // if (tEventHost is TheOPCUATagThing) // TODO Create a TheOPCUAEventThing // { // // If this is a dedicated event thing, use the original value name // // TODO Is this really what we want to do or do we also want to use the full browsepath for dedicated event things? // throw new NotImplementedException("Should never get here"); // //propertyName = field.BrowsePath[0].Name; // } // else // { // // This is an external, multi-event thing: use the browsepath to avoid collisions with properties from multiple events // propertyName = DisplayName + "." + eventField.Key; // } // SetPropertyFromVariant(tEventHost, propertyName, eventField.Value, sourceTime); //} } } catch (Exception ex) { TheBaseAssets.MySYSLOG.WriteToLog(78102, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyOPCServer.GetBaseThing().EngineName, $"Internal error processing event notification", eMsgLevel.l1_Error, $"{DisplayName} {GetNodeIdForLogs()} {EventInfo.AggregateRetainedConditions}: {ex.ToString()}")); } }