public TheOPCUATagThing(TheThing pThing, TheOPCTag pTag) : base(pThing) { MyBaseThing.DeviceType = eOPCDeviceTypes.OPCLiveTag; Reset(); MyBaseThing.RegisterEvent("OnInitialized", sinkInit); MyBaseThing.SetIThingObject(this); }
void SetPropertyFromVariant(ICDEThing thing, string name, Variant variant, DateTimeOffset sourceTime) { var prop = thing.GetProperty(name, true); var dataValue = new DataValue(variant); ePropertyTypes?tType; var cdeValue = TheOPCTag.ChangeType(dataValue, dataValue, out tType); prop.cdeT = (int)(tType ?? ePropertyTypes.TString); prop.cdeCTIM = sourceTime; prop.Value = cdeValue; }
public override void HandleMessage(ICDEThing sender, object pIncoming) { TheProcessMessage pMsg = pIncoming as TheProcessMessage; if (pMsg == null || pMsg.Message == null) { return; } var cmd = pMsg.Message.TXT.Split(':'); switch (cmd[0]) { case "CALL_METHOD": case nameof(MsgOPCUAMethodCall): string error = "Unexpected"; string exceptionText = ""; MsgOPCUAMethodCall callInfo = null; byte[] largeOutput = null; string outParamsAsJson = null; IList <object> outputArguments = null; if (m_Method == null) { error = "Method meta data not initialized"; } else if (m_Method.MyOPCServer == null) { error = "Method not inititialized"; } else if (m_Method.MyOPCServer.m_session == null) { error = "OPC UA session not created"; } else { try { if (TheCommonUtils.cdeIsLocked(m_Method)) { TheBaseAssets.MySYSLOG.WriteToLog(78401, TSM.L(eDEBUG_LEVELS.FULLVERBOSE) ? null : new TSM(MyBaseThing.EngineName, String.Format("[{0}] Method called concurrently", m_Method.MyOPCServer.GetLogAddress()), eMsgLevel.l4_Message, String.Format("{0}", MyBaseThing.Address))); } lock (m_Method) { if (m_Method.Args == null) { var browseError = m_Method.MyOPCServer.MethodBrowser(m_Method.TagRef, m_Method.DisplayName, m_Method); if (!string.IsNullOrEmpty(browseError)) { error = "Unable to retrieve method metadata from server: " + browseError; } } if (m_Method.Args == null) { error = "Unable to retrieve method metadata from server"; } else { if (!string.IsNullOrEmpty(pMsg.Message.PLS)) { if (cmd[0] == nameof(MsgOPCUAMethodCall)) { callInfo = TheCommRequestResponse.ParseRequestMessageJSON <MsgOPCUAMethodCall>(pMsg.Message); foreach (var argument in callInfo.Arguments) { TheThing.SetSafeProperty(this, argument.Key, argument.Value, ePropertyTypes.NOCHANGE); } } else { var tLst = TheCommonUtils.cdeSplit(pMsg.Message.PLS, ":;:", true, true).ToList(); foreach (string t in tLst) { TheThing.SetPropertyFromBagItem(this, t); } } } object[] tArgs = new object[InputArgCnt]; for (int i = 0; i < InputArgCnt; i++) { tArgs[i] = TheOPCTag.GetOPCValueFromCDEValue(InputArgs[i].cdeProperty == null ? null : InputArgs[i].cdeProperty.Value, InputArgs[i].OPCType); } #if OLD_UA outputArguments = m_Method.MyOPCServer.m_session.CallWithTimeout(m_Method.ParentId, m_Method.TagRef, MethodCallTimeout, tArgs); #else outputArguments = m_Method.MyOPCServer.m_session.Call(m_Method.ParentId, m_Method.TagRef, tArgs); //CM: C-labs extension: .CallWithTimeout(m_Method.ParentId, m_Method.TagRef, MethodCallTimeout, tArgs); #endif if (cmd[0] != nameof(MsgOPCUAMethodCall)) { if (TheThing.GetSafePropertyBool(this, "ReturnOutputAsJson")) { outParamsAsJson = TheCommonUtils.SerializeObjectToJSONString(outputArguments); //TheThing.SetSafePropertyString(this, "OutputAsJson", outParamsAsJson); } else { if (outputArguments != null && outputArguments.Count > 0) { for (int i = 0; i < outputArguments.Count; i++) { if (i < OutputArgs.Count) { object value; if (outputArguments[i] is byte[] && (outputArguments[i] as byte[]).Length > 4096 && largeOutput == null) { largeOutput = outputArguments[i] as byte[]; value = ""; } else { value = outputArguments[i]; } cdeP tP = OutputArgs[i].cdeProperty; if (tP != null) { //TheOPCTag.UpdateValueProperty(outputArguments[i] as DataValue, tP, outputArguments[i] as DataValue); tP.Value = value; // tP.SetValue(outputArguments[i], pMsg.Message.GetOriginator().ToString()); // CODE REVIEW: Why did we set the originator here? It's only really needed for remote things to break update cycles... } } else { TheBaseAssets.MySYSLOG.WriteToLog(78402, TSM.L(eDEBUG_LEVELS.FULLVERBOSE) ? null : new TSM(MyBaseThing.EngineName, String.Format("[{0}] Error processing method response for OPC Server", m_Method.MyOPCServer.GetLogAddress()), eMsgLevel.l2_Warning, String.Format("{0}: too many out parameters in method", MyBaseThing.Address))); } } } } MyBaseThing.LastUpdate = DateTimeOffset.Now; LastMessage = string.Format("Success at {0}", MyBaseThing.LastUpdate); } error = ""; } } } catch (Exception e) { error = "Method Call failed: " + e.Message; exceptionText = e.ToString(); LastMessage = error; TheBaseAssets.MySYSLOG.WriteToLog(78403, TSM.L(eDEBUG_LEVELS.VERBOSE) ? null : new TSM(MyBaseThing.EngineName, String.Format("[{0}] Method Call failed", m_Method.MyOPCServer.GetLogAddress()), eMsgLevel.l1_Error, String.Format("{0}:{1}", MyBaseThing.Address, e.ToString()))); } } if (cmd[0] == nameof(MsgOPCUAMethodCall)) { if (callInfo?.ReturnRawJSON == true) { TheCommRequestResponse.PublishResponseMessageJson(pMsg.Message, new MsgOPCUAMethodCallResponse { OutputArguments = new List <object> { TheCommonUtils.SerializeObjectToJSONString(outputArguments) }, Error = error }); } else { TheCommRequestResponse.PublishResponseMessageJson(pMsg.Message, new MsgOPCUAMethodCallResponse { OutputArguments = (List <object>)outputArguments, Error = error }); } } else { TSM tTSN = new TSM(MyBaseThing.EngineName, string.Format(String.IsNullOrEmpty(error) ? "CALL_METHOD_RESPONSE:{0}:{1}" : "CALL_METHOD_RESPONSE:{0}:{1}:{2}:{3}", MyBaseThing.ID, cmd[1], error.Replace(":", " "), exceptionText.Replace(":", " "))); if (largeOutput != null && String.IsNullOrEmpty(error)) { tTSN.PLB = largeOutput; } if (outParamsAsJson != null && String.IsNullOrEmpty(error)) { tTSN.PLS = outParamsAsJson; } if (pMsg.LocalCallback != null) { pMsg.LocalCallback(tTSN); } else { TheCommCore.PublishToOriginator(pMsg.Message, tTSN); } } break; } base.HandleMessage(this, pMsg); }
public void Setup(TheOPCTag pTag) { m_Tag = pTag; IsActive = false; sinkInit(this, null); }
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()}")); } }