public DxpItemByteArray() { Result = new DaValue(); Result.Subscribe(value => { if (value != null) { var t = value.GetType(); if (t.IsArray) { var arr = (Array)Result.Value; if (t.GetElementType() == typeof(bool)) { } } } if (Node != null) { switch (Node.Device.DeviceType) { case DxpDeviceType.Unknown: break; case DxpDeviceType.Bit: break; case DxpDeviceType.Byte: if (Node.Size > 1) { Buffer = (byte[])Result.Value; } else { Buffer = new[] { (byte)Result.Value }; } break; case DxpDeviceType.Word: if (Node.Size > 1) { Buffer = ((Int16[])Result.Value).SelectMany(_ => BitConverter.GetBytes(_)).ToArray(); } else { Buffer = BitConverter.GetBytes((Int16)Result.Value); } break; default: break; } } }); }
/// <summary> /// Reads the current values for the specified attributes. /// </summary> /// <param name="session">The session.</param> /// <param name="itemHandle">The item handle.</param> /// <param name="attributeIds">The attribute ids.</param> /// <returns></returns> public DaValue[] ReadCurrentValues(Session session, HdaItemHandle itemHandle, uint[] attributeIds) { DaValue[] results = new DaValue[attributeIds.Length]; // check handle. InternalHandle handle = itemHandle as InternalHandle; if (handle == null) { for (int ii = 0; ii < results.Length; ii++) { results[ii] = new DaValue(); results[ii].Error = ResultIds.E_INVALIDHANDLE; } return results; } // look up the supported attributes for an item. ReadValueIdCollection supportedAttributes = handle.Item.SupportedAttributes; if (supportedAttributes == null) { handle.Item.SupportedAttributes = supportedAttributes = GetAvailableAttributes(session, handle.NodeId); } // build list of values to read. ReadValueIdCollection valuesToRead = new ReadValueIdCollection(); List<int> indexes = new List<int>(); for (int ii = 0; ii < attributeIds.Length; ii++) { ReadValueId valueToRead = GetReadValueId(supportedAttributes, attributeIds[ii]); if (valueToRead == null) { results[ii] = new DaValue(); results[ii].Error = ResultIds.E_INVALIDATTRID; continue; } valuesToRead.Add(valueToRead); indexes.Add(ii); // need to fetch the value rank as well. if (attributeIds[ii] == Constants.OPCHDA_DATA_TYPE) { valuesToRead.Add(GetReadValueId(supportedAttributes, ComHdaProxy.INTERNAL_ATTRIBUTE_VALUE_RANK)); indexes.Add(-1); } } // nothing to do. if (valuesToRead.Count == 0) { return results; } // read values from the UA server. DataValueCollection values = null; DiagnosticInfoCollection diagnosticInfos = null; session.Read( null, 0, TimestampsToReturn.Neither, valuesToRead, out values, out diagnosticInfos); // validate response from the UA server. ClientBase.ValidateResponse(values, valuesToRead); ClientBase.ValidateDiagnosticInfos(diagnosticInfos, valuesToRead); // assign a local handle to all valid items. for (int ii = 0; ii < valuesToRead.Count; ii++) { int index = indexes[ii]; uint attributeId = (uint)valuesToRead[ii].Handle; // check for values which are combined with other values to create the value (e.g. ValueRank). if (index == -1) { continue; } results[index] = GetAttributeValue(session, attributeId, values, ii); // only support current value for now. if (results[index].Error == ResultIds.S_OK) { results[index].Error = ResultIds.S_CURRENTVALUE; } } return results; }
/// <summary> /// Gets the remote data value. /// </summary> /// <param name="localValue">The local value.</param> /// <param name="remoteType">The remote data type.</param> /// <returns>The remote data value.</returns> /// <exception cref="COMException">Thrown if a conversion error occurs.</exception> public DataValue GetRemoteDataValue(DaValue localValue, TypeInfo remoteType) { DataValue remoteValue = new DataValue(); remoteValue.SourceTimestamp = localValue.Timestamp; if (localValue.Error < 0) { throw ComUtils.CreateComException(localValue.Error); } remoteValue.StatusCode = ComUtils.GetHdaQualityCode(localValue.HdaQuality); try { remoteValue.WrappedValue = GetRemoteValue(new Variant(localValue.Value), remoteType); } catch (Exception e) { throw ComUtils.CreateComException(e, ResultIds.E_BADTYPE); } return remoteValue; }
/// <summary> /// Reads the values of the properties from the server. /// </summary> /// <param name="itemId">The item id.</param> /// <param name="propertyIds">The list of property ids to read. All properties are read if null.</param> /// <returns>True if the element has properies.</returns> private DaValue[] ReadPropertyValues(string itemId, params int[] propertyIds) { string methodName = "IOPCItemProperties.GetItemProperties"; // check for trivial case. if (propertyIds == null) { return null; } int count = propertyIds.Length; DaValue[] results = new DaValue[count]; // check for empty list. if (count == 0) { return results; } // get the values from the server. IntPtr pValues = IntPtr.Zero; IntPtr pErrors = IntPtr.Zero; try { IOPCItemProperties server = BeginComCall<IOPCItemProperties>(methodName, true); server.GetItemProperties( itemId, count, propertyIds, out pValues, out pErrors); } catch (Exception e) { if (ComUtils.IsUnknownError(e, ResultIds.E_FAIL, ResultIds.E_UNKNOWNITEMID, ResultIds.E_INVALIDITEMID)) { ComUtils.TraceComError(e, methodName); return null; } for (int ii = 0; ii < count; ii++) { DaValue result = results[ii] = new DaValue(); result.Value = null; result.Quality = OpcRcw.Da.Qualities.OPC_QUALITY_GOOD; result.Timestamp = DateTime.UtcNow; result.Error = Marshal.GetHRForException(e); } return results; } finally { EndComCall(methodName); } // unmarshal results. object[] values = ComUtils.GetVARIANTs(ref pValues, count, true); int[] errors = ComUtils.GetInt32s(ref pErrors, count, true); for (int ii = 0; ii < count; ii++) { DaValue result = results[ii] = new DaValue(); result.Value = ComUtils.ProcessComValue(values[ii]); result.Quality = OpcRcw.Da.Qualities.OPC_QUALITY_GOOD; result.Timestamp = DateTime.UtcNow; result.Error = errors[ii]; } return results; }
/// <summary> /// Called when a asynchronous read completes. /// </summary> /// <param name="requestId">The request id.</param> /// <param name="clientHandles">The client handles.</param> /// <param name="values">The values.</param> internal void OnReadComplete(int requestId, int[] clientHandles, DaValue[] values) { }
/// <summary> /// Creates an array of item value result objects from the callback data. /// </summary> internal static DaValue[] GetItemValues( int dwCount, object[] pvValues, short[] pwQualities, System.Runtime.InteropServices.ComTypes.FILETIME[] pftTimeStamps, int[] pErrors) { // contruct the item value results. DaValue[] values = new DaValue[dwCount]; for (int ii = 0; ii < dwCount; ii++) { DaValue value = values[ii] = new DaValue(); value.Error = pErrors[ii]; if (pErrors[ii] >= 0) { value.Value = ComUtils.ProcessComValue(pvValues[ii]); value.Quality = pwQualities[ii]; value.Timestamp = ComUtils.GetDateTime(pftTimeStamps[ii]); } } // return results return values; }
/// <summary> /// Creates the update requests. /// </summary> private List<HdaUpdateRequest> CreateUpdateRequests( Session session, PerformUpdateType updateType, int[] serverHandles, DaValue[] values) { List<HdaUpdateRequest> requests = new List<HdaUpdateRequest>(); for (int ii = 0; ii < serverHandles.Length; ii++) { HdaUpdateRequest request = new HdaUpdateRequest(); requests.Add(request); // find handle. request.Handle = m_itemManager.LookupHandle(serverHandles[ii]); if (request.Handle == null) { request.Error = ResultIds.E_INVALIDHANDLE; continue; } request.ClientHandle = request.Handle.ClientHandle; // check if nothing to do. DaValue value = values[ii]; if (value == null) { request.Error = ResultIds.E_FAIL; continue; } // specify the parameters for the request. UpdateDataDetails details = new UpdateDataDetails(); details.NodeId = request.Handle.NodeId; details.PerformInsertReplace = updateType; DataValue value2 = m_mapper.GetRemoteDataValue(value, m_itemManager.GetRemoteDataType(request.Handle)); value2.SourceTimestamp = value.Timestamp; value2.StatusCode = ComUtils.GetHdaQualityCode(value.HdaQuality); details.UpdateValues.Add(value2); request.Details = new ExtensionObject(details); } return requests; }
/// <summary> /// Updates the history. /// </summary> public int[] UpdateRaw( PerformUpdateType updateType, int[] serverHandles, DaValue[] values) { Session session = ThrowIfNotConnected(); // create the update requests. List<HdaUpdateRequest> requests = CreateUpdateRequests( session, updateType, serverHandles, values); // update the server. return UpdateHistory(session, requests, true); }
/// <summary> /// Updates the cache. /// </summary> /// <param name="item">The item.</param> /// <param name="value">The value.</param> /// <param name="isInitialRefresh">if set to <c>true</c> the change flag is set to false because the values are sent in the refresh.</param> private void UpdateCache(ComDaGroupItem item, DaValue value, bool isInitialRefresh) { lock (m_lock) { // get sampling rate. long now = HiResClock.UtcNow.Ticks; int samplingRate = item.ActualSamplingRate; if (item.SamplingRate == -1) { samplingRate = m_actualUpdateRate; } // check existing cache contents. DaValue oldValue = null; DaCacheValue entry = null; if (item.CacheEntry != null) { // do not update cache if a newer value exists. if (item.CacheEntry.Timestamp >= value.Timestamp) { /* TraceState( "UpdateCache OLD VALUE RECEIVED", this.m_serverHandle, item.ServerHandle, new Variant(item.CacheEntry.Value), item.CacheEntry.Timestamp.ToString("HH:mm:ss.fff"), new Variant(value.Value), value.Timestamp.ToString("HH:mm:ss.fff")); */ return; } oldValue = item.CacheEntry; // replace the newest value if sampling interval has not elasped. if (!item.BufferEnabled || item.NextUpdateTime > now) { // TraceState("UpdateCache ENTRY REPLACED", this.m_serverHandle, item.ServerHandle, samplingRate); entry = item.CacheEntry; } } // create a new cache entry. if (entry == null) { entry = new DaCacheValue(); entry.CacheTimestamp = DateTime.UtcNow; if (item.BufferEnabled) { entry.NextEntry = item.CacheEntry; item.NextUpdateTime += samplingRate*TimeSpan.TicksPerMillisecond; if (entry.NextEntry != null) { // TraceState("UpdateCache ENTRY BUFFERED", this.m_serverHandle, item.ServerHandle, samplingRate); } } item.CacheEntry = entry; } // check if the value has changed. bool changed = !isInitialRefresh; if (oldValue != null) { if (oldValue.Error == value.Error) { if (oldValue.Quality == value.Quality) { if (Utils.IsEqual(oldValue.Value, value.Value)) { changed = false; } } } } // save values. item.CacheEntry.Value = value.Value; item.CacheEntry.Quality = value.Quality; item.CacheEntry.Timestamp = value.Timestamp; item.CacheEntry.Error = value.Error; item.CacheEntry.Changed = changed; TraceState( "UpdateCache COMPLETE", this.m_serverHandle, item.ServerHandle, item.ClientHandle, new Variant(value.Value), value.Timestamp.ToString("HH:mm:ss.fff"), item.CacheEntry.Changed); } }
/// <summary> /// Updates the read result by converting the value to the requested data type. /// </summary> /// <param name="item">The item.</param> /// <param name="value">The value.</param> private void UpdateReadResult(ComDaGroupItem item, DaValue value) { if (value.Value == null || item.RequestedDataType == (short)VarEnum.VT_EMPTY) { return; } object convertedValue = null; int error = ComUtils.ChangeTypeForCOM(value.Value, (VarEnum)item.RequestedDataType, out convertedValue); if (error < 0) { value.Value = null; value.Error = error; return; } value.Value = convertedValue; }
/// <summary> /// Performs n synchronous read operation. /// </summary> /// <param name="maxAge">The max age.</param> /// <param name="serverHandles">The server handles.</param> /// <param name="isInitialRefresh">if set to <c>true</c> the read is being done as part of the initial refresh for active items.</param> /// <param name="clientHandles">The client handles (must be allocated by the caller).</param> /// <returns>Any errors.</returns> public DaValue[] SyncRead(uint maxAge, int[] serverHandles, bool isInitialRefresh, int[] clientHandles) { TraceState("SyncRead", maxAge, serverHandles.Length); ThrowIfDisposed(); DaValue[] results = new DaValue[serverHandles.Length]; List<ComDaGroupItem> items = new List<ComDaGroupItem>(serverHandles.Length); ReadValueIdCollection valuesToRead = new ReadValueIdCollection(); lock (m_lock) { // validate items. for (int ii = 0; ii < serverHandles.Length; ii++) { results[ii] = new DaValue(); ComDaGroupItem item = null; if (!m_itemsByHandle.TryGetValue(serverHandles[ii], out item)) { results[ii].Error = ResultIds.E_INVALIDHANDLE; continue; } if (clientHandles != null) { clientHandles[ii] = item.ClientHandle; } // check for cache read. if (maxAge == UInt32.MaxValue) { // inactive items cannot be read from cache. if (!item.Active || !m_active) { results[ii].Quality = OpcRcw.Da.Qualities.OPC_QUALITY_OUT_OF_SERVICE; results[ii].Timestamp = DateTime.UtcNow; continue; } // check if waiting for initial data. if (item.CacheEntry == null) { results[ii].Quality = OpcRcw.Da.Qualities.OPC_QUALITY_WAITING_FOR_INITIAL_DATA; results[ii].Timestamp = DateTime.UtcNow; continue; } // get the latest value from the cache. item.CacheEntry.GetLatest(results[ii]); UpdateReadResult(item, results[ii]); continue; } // apply max age. if (maxAge > 0) { if (item.CacheEntry != null) { if (item.CacheEntry.CacheTimestamp.AddMilliseconds(maxAge) > DateTime.UtcNow) { item.CacheEntry.GetLatest(results[ii]); UpdateReadResult(item, results[ii]); continue; } } } // schedule read from device. ReadValueId valueToRead = new ReadValueId(); valueToRead.NodeId = item.NodeId; valueToRead.AttributeId = Attributes.Value; valueToRead.Handle = ii; valuesToRead.Add(valueToRead); items.Add(item); } } if (valuesToRead.Count == 0) { return results; } // read the values from the server. DaValue[] remoteResults = m_manager.Read(valuesToRead); // copy results. for (int ii = 0; ii < valuesToRead.Count; ii++) { int index = (int)valuesToRead[ii].Handle; if (isInitialRefresh) { UpdateCache(items[ii], remoteResults[ii], isInitialRefresh); } UpdateReadResult(items[ii], remoteResults[ii]); results[(int)valuesToRead[ii].Handle] = remoteResults[ii]; } return results; }
/// <summary> /// Gets the property values. /// </summary> /// <param name="itemId">The item id.</param> /// <param name="propertyIds">The property ids.</param> /// <returns>The property values.</returns> public DaValue[] GetPropertyValues(string itemId, int[] propertyIds) { DaValue[] results = new DaValue[propertyIds.Length]; for (int ii = 0; ii < results.Length; ii++) { results[ii] = new DaValue(); results[ii].Error = ResultIds.E_UNKNOWNITEMID; } return results; }
/// <summary> /// Writes the values for the specified item ids. /// </summary> /// <param name="itemIds">The item ids.</param> /// <param name="values">The values.</param> /// <returns>The results.</returns> public int[] Write(string[] itemIds, DaValue[] values) { int[] results = new int[itemIds.Length]; for (int ii = 0; ii < results.Length; ii++) { results[ii] = ResultIds.E_UNKNOWNITEMID; } return results; }
/// <summary> /// Reads the values for the specified item ids. /// </summary> /// <param name="itemIds">The item ids.</param> /// <returns>The values.</returns> public DaValue[] Read(string[] itemIds) { DaValue[] values = new DaValue[itemIds.Length]; for (int ii = 0; ii < values.Length; ii++) { values[ii] = new DaValue(); values[ii].Error = ResultIds.E_UNKNOWNITEMID; } return values; }
/// <summary> /// Converts a UA value to an HDA attribute value. /// </summary> /// <param name="session">The session.</param> /// <param name="attributeId">The attribute id.</param> /// <param name="values">The values.</param> /// <param name="index">The index.</param> /// <returns></returns> private DaValue GetAttributeValue(Session session, uint attributeId, DataValueCollection values, int index) { switch (attributeId) { case Constants.OPCHDA_DATA_TYPE: { DaValue result = new DaValue(); DataValue value = values[index]; // check for valid node. if (StatusCode.IsBad(value.StatusCode)) { result.Error = ResultIds.E_UNKNOWNITEMID; return result; } // covert to var type. NodeId dataTypeId = value.GetValue<NodeId>(DataTypeIds.BaseDataType); int valueRank = values[index+1].GetValue<int>(ValueRanks.Scalar); BuiltInType builtInType = DataTypes.GetBuiltInType(dataTypeId, session.TypeTree); TypeInfo typeInfo = new TypeInfo(builtInType, valueRank); short varType = (short)ComUtils.GetVarType(typeInfo); result.Value = varType; result.Quality = ComUtils.GetQualityCode(value.StatusCode); result.Timestamp = value.ServerTimestamp; result.Error = ResultIds.S_OK; return result; } case Constants.OPCHDA_DESCRIPTION: { DataValue value = values[index]; if (value.StatusCode == StatusCodes.BadAttributeIdInvalid) { DaValue result = new DaValue(); result.Error = ResultIds.E_INVALIDATTRID; return result; } return m_mapper.GetLocalDataValue(value); } default: { return ComHdaProxy.GetAttributeValue(session, m_mapper, attributeId, values[index]); } } }
/// <summary> /// Reads the attribute values from the server. /// </summary> /// <param name="valuesToRead">The values to read.</param> /// <returns> /// The values read. /// </returns> public DaValue[] Read(ReadValueIdCollection valuesToRead) { TraceState("Read", valuesToRead.Count); // check for valid session. Session session = m_session; if (session == null) { throw ComUtils.CreateComException(ResultIds.E_FAIL); } int masterError = ResultIds.E_FAIL; DaValue[] results = new DaValue[valuesToRead.Count]; if (session != null) { try { // read the values. DataValueCollection values = null; DiagnosticInfoCollection diagnosticInfos = null; session.Read( null, 0, TimestampsToReturn.Both, valuesToRead, out values, out diagnosticInfos); ClientBase.ValidateResponse(values, valuesToRead); ClientBase.ValidateDiagnosticInfos(diagnosticInfos, valuesToRead); // convert the response. for (int ii = 0; ii < values.Count; ii++) { results[ii] = m_mapper.GetLocalDataValue(values[ii]); } // return the results. return results; } catch (Exception e) { masterError = ComUtils.GetErrorCode(e, ResultIds.E_FAIL); } } // report any unexpected errors. for (int ii = 0; ii < results.Length; ii++) { DaValue result = results[ii] = new DaValue(); result.Error = masterError; } return results; }
/// <summary> /// Performs n synchronous write operation. /// </summary> /// <param name="serverHandles">The server handles.</param> /// <param name="values">The values.</param> /// <returns>Any errors.</returns> public int[] SyncWrite(int[] serverHandles, DaValue[] values) { TraceState("SyncWrite", serverHandles.Length); ThrowIfDisposed(); int[] results = new int[serverHandles.Length]; WriteValueCollection valuesToWrite = new WriteValueCollection(); lock (m_lock) { // validate items. DaValue convertedValue = new DaValue(); for (int ii = 0; ii < serverHandles.Length; ii++) { ComDaGroupItem item = null; if (!m_itemsByHandle.TryGetValue(serverHandles[ii], out item)) { results[ii] = ResultIds.E_INVALIDHANDLE; continue; } // apply the COM type conversion. DaValue requestedValue = values[ii]; if (requestedValue.Value == null) { results[ii] = ResultIds.E_BADTYPE; continue; } if (item.CanonicalDataType != (short)VarEnum.VT_EMPTY) { object value = null; int error = ComUtils.ChangeTypeForCOM(requestedValue.Value, (VarEnum)item.CanonicalDataType, out value); if (error < 0) { results[ii] = error; continue; } // could happen if there is a problem reading the datatype from the server. if (requestedValue.Value == null) { results[ii] = ResultIds.E_BADTYPE; continue; } // copy all of the attributes into the converted value. convertedValue.Value = value; convertedValue.Quality = requestedValue.Quality; convertedValue.Timestamp = requestedValue.Timestamp; convertedValue.Error = requestedValue.Error; requestedValue = convertedValue; } WriteValue valueToWrite = new WriteValue(); valueToWrite.NodeId = item.NodeId; valueToWrite.AttributeId = Attributes.Value; valueToWrite.Handle = ii; // convert value to UA data type. try { valueToWrite.Value = m_manager.Mapper.GetRemoteDataValue(requestedValue, item.RemoteDataType); } catch (Exception e) { results[ii] = ComUtils.GetErrorCode(e, ResultIds.E_BADTYPE); continue; } valuesToWrite.Add(valueToWrite); } } // check if nothing to do. if (valuesToWrite.Count == 0) { return results; } // write the values to the server. int[] remoteResults = m_manager.Write(valuesToWrite); // copy results. for (int ii = 0; ii < valuesToWrite.Count; ii++) { results[(int)valuesToWrite[ii].Handle] = remoteResults[ii]; } return results; }
/// <summary> /// Updates the history. /// </summary> public int[] UpdateRaw( int transactionId, PerformUpdateType updateType, int[] serverHandles, DaValue[] values, out int cancelId) { Session session = ThrowIfNotConnected(); // create the update requests. List<HdaUpdateRequest> requests = CreateUpdateRequests( session, updateType, serverHandles, values); // queue the transaction. int[] errors = CreateTransaction( TransationType.Update, transactionId, requests, out cancelId); // return the initial results. return errors; }
/// <summary> /// Starts an asynchronous write operation. /// </summary> /// <param name="transactionId">The transaction id.</param> /// <param name="serverHandles">The server handles.</param> /// <param name="values">The values.</param> /// <param name="cancelId">The cancel id.</param> /// <returns>Any errors.</returns> public int[] AsyncWrite(int transactionId, int[] serverHandles, DaValue[] values, out int cancelId) { TraceState("AsyncWrite", transactionId, serverHandles.Length); ThrowIfDisposed(); cancelId = 0; lock (m_lock) { int[] results = new int[serverHandles.Length]; List<ComDaGroupItem> items = new List<ComDaGroupItem>(serverHandles.Length); List<DaValue> valuesToWrite = new List<DaValue>(serverHandles.Length); for (int ii = 0; ii < serverHandles.Length; ii++) { ComDaGroupItem item = null; if (!m_itemsByHandle.TryGetValue(serverHandles[ii], out item)) { results[ii] = ResultIds.E_INVALIDHANDLE; continue; } items.Add(item); valuesToWrite.Add(values[ii]); } if (items.Count > 0) { ComDaAsnycWriteRequest request = new ComDaAsnycWriteRequest(); request.CancelId = ++m_transactionCounter; request.TransactionId = transactionId; request.ServerHandles = new int[items.Count]; request.ClientHandles = new int[items.Count]; request.Values = valuesToWrite.ToArray(); for (int ii = 0; ii < items.Count; ii++) { request.ServerHandles[ii] = items[ii].ServerHandle; request.ClientHandles[ii] = items[ii].ClientHandle; } m_requests.Add(request); cancelId = request.CancelId; TraceState("AsyncWrite Queued", transactionId, request.CancelId); // create a thread to process the request. Thread thread = new Thread(OnAsyncWrite); thread.IsBackground = true; thread.Start(request); } return results; } }
/// <summary> /// Converts a UA value to an HDA attribute value. /// </summary> /// <param name="session">The session.</param> /// <param name="mapper">The mapper.</param> /// <param name="attributeId">The attribute id.</param> /// <param name="value">The value.</param> /// <returns></returns> internal static DaValue GetAttributeValue(Session session, ComNamespaceMapper mapper, uint attributeId, DataValue value) { switch (attributeId) { case INTERNAL_ATTRIBUTE_ANNOTATION: { DaValue result = new DaValue(); Annotation annotation = value.GetValue<Annotation>(null); if (annotation == null) { result.Error = ResultIds.E_BADTYPE; return result; } result.Value = annotation; result.HdaQuality = ComUtils.GetHdaQualityCode(value.StatusCode); result.Timestamp = value.SourceTimestamp; result.Error = ResultIds.S_OK; return result; } case Constants.OPCHDA_ENG_UNITS: { DaValue result = new DaValue(); EUInformation engineeringUnits = value.GetValue<EUInformation>(null); if (engineeringUnits == null) { result.Error = ResultIds.E_INVALIDATTRID; return result; } if (engineeringUnits.DisplayName != null) { result.Value = engineeringUnits.DisplayName.Text; } result.HdaQuality = ComUtils.GetHdaQualityCode(value.StatusCode); result.Timestamp = value.SourceTimestamp; result.Error = ResultIds.S_OK; return result; } case Constants.OPCHDA_HIGH_ENTRY_LIMIT: case Constants.OPCHDA_NORMAL_MAXIMUM: { DaValue result = new DaValue(); Range range = value.GetValue<Range>(null); if (range == null) { result.Error = ResultIds.E_INVALIDATTRID; return result; } result.Value = range.High; result.HdaQuality = ComUtils.GetHdaQualityCode(value.StatusCode); result.Timestamp = value.SourceTimestamp; result.Error = ResultIds.S_OK; return result; } case Constants.OPCHDA_LOW_ENTRY_LIMIT: case Constants.OPCHDA_NORMAL_MINIMUM: { DaValue result = new DaValue(); Range range = value.GetValue<Range>(null); if (range == null) { result.Error = ResultIds.E_INVALIDATTRID; return result; } result.Value = range.Low; result.HdaQuality = ComUtils.GetHdaQualityCode(value.StatusCode); result.Timestamp = value.SourceTimestamp; result.Error = ResultIds.S_OK; return result; } case Constants.OPCHDA_MAX_TIME_INT: case Constants.OPCHDA_MIN_TIME_INT: { DaValue result = new DaValue(); int error = ComHdaProxy.MapReadStatusToErrorCode(value.StatusCode); if (error < 0) { result.Error = error; return result; } // need to support the VT_CY type. result.Value = (decimal)value.GetValue<double>(0); result.HdaQuality = ComUtils.GetHdaQualityCode(value.StatusCode); result.Timestamp = value.SourceTimestamp; result.Error = ResultIds.S_OK; return result; } default: case Constants.OPCHDA_ITEMID: case Constants.OPCHDA_ARCHIVING: case Constants.OPCHDA_STEPPED: case Constants.OPCHDA_EXCEPTION_DEV: case Constants.OPCHDA_EXCEPTION_DEV_TYPE: case Constants.OPCHDA_DERIVE_EQUATION: { return mapper.GetLocalDataValue(value); } } }
/// <summary> /// Updates the cache with quality. /// </summary> /// <param name="quality">The quality.</param> public void UpdateCacheWithQuality(short quality) { lock (m_lock) { DateTime now = DateTime.UtcNow; foreach (ComDaGroupItem item in m_itemsByHandle.Values) { if (null == item.CacheEntry) continue; DaValue value = new DaValue(); item.CacheEntry.GetLatest(value); value.Quality = quality; value.Timestamp = now; UpdateCache(item, value, false); } } }
/// <summary> /// Called when a data change event arrives. /// </summary> /// <param name="clientHandles">The client handles.</param> /// <param name="values">The values.</param> internal void OnDataChange(int[] clientHandles, DaValue[] values) { // check if callbacks are enabled. if (m_monitoredItems == null) { return; } // lookup client handle a report change directly to monitored item. lock (m_monitoredItems) { for (int ii = 0; ii < clientHandles.Length; ii++) { DataChangeInfo info = null; if (!m_monitoredItems.TryGetValue(clientHandles[ii], out info)) { continue; } MonitoredItem[] monitoredItems = info.MonitoredItems; // convert the value to a UA value. info.LastValue = new DataValue(); info.LastError = ReadRequest.GetItemValue(values[ii], info.LastValue, DiagnosticsMasks.All); info.LastValue.ServerTimestamp = DateTime.UtcNow; // queue the values. for (int jj = 0; jj < monitoredItems.Length; jj++) { if (info.LastValue.Value.GetType().IsArray && monitoredItems[jj].IndexRange.Count != info.LastValue.Value.GetType().GetArrayRank() && StatusCode.IsBad(info.LastValue.StatusCode)) { info.LastValue.StatusCode = StatusCodes.BadIndexRangeNoData; } monitoredItems[jj].QueueValue(info.LastValue, info.LastError); } } } }
/// <summary> /// Called when it is time to send an update. /// </summary> /// <param name="state">The state.</param> private void OnUpdate(object state) { try { IComDaGroupCallback callback = null; List<int> clientHandles = null; List<DaValue> values = null; long now = HiResClock.UtcNow.Ticks; lock (m_lock) { // check if updates are required. if (!AreUpdatesRequired || m_updateInProgress) { TraceState("OnUpdate Skipped"); return; } // check if enough time has elapsed. if (m_nextUpdateTime == -1 || m_nextUpdateTime > (now + 50*TimeSpan.TicksPerMillisecond)) { return; } // Utils.Trace("NextUpdateTime={0:mm:ss.fff}, CurrentTime={1:mm:ss.fff}", new DateTime(m_nextUpdateTime), new DateTime(now)); // collect values to report. for (int ii = 0; ii < m_items.Count; ii++) { ComDaGroupItem item = m_items[ii]; if (!item.Active) { continue; } if (item.CacheEntry == null || !item.CacheEntry.Changed) { continue; } // write buffered values first. DaValue value = null; if (item.CacheEntry.NextEntry != null) { Stack<DaCacheValue> stack = new Stack<DaCacheValue>(); for (DaCacheValue entry = item.CacheEntry; entry != null; entry = entry.NextEntry) { stack.Push(entry); } while (stack.Count > 1) { DaCacheValue entry = stack.Pop(); value = new DaValue(); entry.GetLatest(value); UpdateReadResult(item, value); if (item.LastSentValue != null) { if (value.Quality == item.LastSentValue.Quality) { if (Utils.IsEqual(item.LastSentValue.Value, value.Value)) { continue; } } } if (clientHandles == null) { clientHandles = new List<int>(); values = new List<DaValue>(); } clientHandles.Add(item.ClientHandle); values.Add(value); item.LastSentValue = value; /* TraceState( "OnUpdate BUFFERED VALUE", this.m_serverHandle, item.ServerHandle, item.ClientHandle, new Variant(value.Value), value.Timestamp.ToString("HH:mm:ss.fff"), item.CacheEntry.Changed); */ } // clear cache. item.CacheEntry.NextEntry = null; } // check if enough time has elapsed for this item (used if the sampling rate > update rate). if (item.NextUpdateTime != -1 && item.NextUpdateTime > now) { continue; } // add latest values. value = new DaValue(); item.CacheEntry.GetLatest(value); UpdateReadResult(item, value); if (item.LastSentValue != null) { if (value.Quality == item.LastSentValue.Quality) { if (Utils.IsEqual(item.LastSentValue.Value, value.Value)) { item.CacheEntry.Changed = false; continue; } } } if (clientHandles == null) { clientHandles = new List<int>(); values = new List<DaValue>(); } clientHandles.Add(item.ClientHandle); values.Add(value); item.LastSentValue = value; /* TraceState( "OnUpdate LATEST VALUE", this.m_serverHandle, item.ServerHandle, item.ClientHandle, new Variant(value.Value), value.Timestamp.ToString("HH:mm:ss.fff"), item.CacheEntry.Changed); */ // clear change flag. item.CacheEntry.Changed = false; } // nothing to report unless the keep alive expired. if (clientHandles == null || clientHandles.Count == 0) { if (m_keepAliveTime == 0 || m_lastUpdateTime + m_keepAliveTime*TimeSpan.TicksPerMillisecond > now) { ScheduleNextUpdate(); return; } } callback = m_callback; m_updateInProgress = true; m_lastUpdateTime = now; // schedule next update. ScheduleNextUpdate(); } // send callback. try { callback.ReadCompleted( this.m_clientHandle, false, 0, 0, (clientHandles != null) ? clientHandles.ToArray() : new int[0], (values != null) ? values.ToArray() : new DaValue[0]); if (clientHandles.Count > 0) { m_manager.SetLastUpdateTime(); } /* TraceState( "OnUpdate Completed", this.m_serverHandle, (values != null && values.Count > 0)?values[0].Value:"null", (m_nextUpdateTime-now)/TimeSpan.TicksPerMillisecond); */ } finally { lock (m_lock) { m_updateInProgress = false; } } } catch (Exception e) { Utils.Trace("Unexpected error during GroupUpdate. {0}", e.Message); } }
/// <summary> /// Unmarshals and deallocates a OPCITEMSTATE structures. /// </summary> internal static DaValue[] GetItemValues(ref IntPtr pInput, int count, bool deallocate) { DaValue[] output = null; if (pInput != IntPtr.Zero && count > 0) { output = new DaValue[count]; IntPtr pos = pInput; for (int ii = 0; ii < count; ii++) { OpcRcw.Da.OPCITEMSTATE result = (OpcRcw.Da.OPCITEMSTATE)Marshal.PtrToStructure(pos, typeof(OpcRcw.Da.OPCITEMSTATE)); DaValue value = new DaValue(); value.Value = ComUtils.ProcessComValue(result.vDataValue); value.Quality = result.wQuality; value.Timestamp = ComUtils.GetDateTime(result.ftTimeStamp); output[ii] = value; if (deallocate) { Marshal.DestroyStructure(pos, typeof(OpcRcw.Da.OPCITEMSTATE)); } pos = (IntPtr)(pos.ToInt32() + Marshal.SizeOf(typeof(OpcRcw.Da.OPCITEMSTATE))); } if (deallocate) { Marshal.FreeCoTaskMem(pInput); pInput = IntPtr.Zero; } } return output; }
/// <summary> /// Gets the property value from the browse element. /// </summary> /// <param name="element">The element.</param> /// <param name="propertyId">The property id.</param> /// <returns>The value containing the property value.</returns> private DaValue GetPropertyValue(BrowseElement element, int propertyId) { DaValue value = new DaValue(); value.Quality = OpcRcw.Da.Qualities.OPC_QUALITY_GOOD; value.Timestamp = element.CacheTimestamp; // check for objects - they only support the description property. if (element.NodeClass == NodeClass.Object) { switch (propertyId) { case PropertyIds.Description: { value.Value = element.BrowseName; break; } case PropertyIds.UaBrowseName: { value.Value = element.UaBrowseName; break; } case PropertyIds.UaDescription: { value.Value = element.UaDescription; break; } default: { value.Error = ResultIds.E_INVALID_PID; return value; } } return value; } // handle variable properties. switch (propertyId) { case PropertyIds.Description: { value.Value = element.BrowseName; break; } case PropertyIds.DataType: { value.Value = element.CanonicalDataType; break; } case PropertyIds.ScanRate: { value.Value = element.ScanRate; break; } case PropertyIds.AccessRights: { value.Value = element.AccessRights; break; } case PropertyIds.EuType: { value.Value = element.EuType; break; } case PropertyIds.EuInfo: { value.Value = element.EuInfo; break; } case PropertyIds.UaBuiltInType: { value.Value = (int)element.BuiltInType; break; } case PropertyIds.UaDataTypeId: { value.Value = element.DataTypeId; break; } case PropertyIds.UaValueRank: { value.Value = element.ValueRank; break; } case PropertyIds.UaBrowseName: { value.Value = element.UaBrowseName; break; } case PropertyIds.UaDescription: { value.Value = element.UaDescription; break; } case PropertyIds.EngineeringUnits: { if (element.EngineeringUnits == null) { value.Error = ResultIds.E_INVALID_PID; break; } value.Value = element.EngineeringUnits; value.Value = element.EngineeringUnits; break; } case PropertyIds.HighEU: { if (element.HighEU == Double.MaxValue) { value.Error = ResultIds.E_INVALID_PID; break; } value.Value = element.HighEU; break; } case PropertyIds.LowEU: { if (element.LowEU == Double.MaxValue) { value.Error = ResultIds.E_INVALID_PID; break; } value.Value = element.LowEU; break; } case PropertyIds.HighIR: { if (element.HighIR == Double.MaxValue) { value.Error = ResultIds.E_INVALID_PID; break; } value.Value = element.HighIR; break; } case PropertyIds.LowIR: { if (element.LowIR == Double.MaxValue) { value.Error = ResultIds.E_INVALID_PID; break; } value.Value = element.LowIR; break; } case PropertyIds.CloseLabel: { if (element.CloseLabel == null) { value.Error = ResultIds.E_INVALID_PID; break; } value.Value = element.CloseLabel; break; } case PropertyIds.OpenLabel: { if (element.OpenLabel == null) { value.Error = ResultIds.E_INVALID_PID; break; } value.Value = element.OpenLabel; break; } case PropertyIds.TimeZone: { if (element.TimeZone == Int32.MaxValue) { value.Error = ResultIds.E_INVALID_PID; break; } value.Value = element.TimeZone; break; } case PropertyIds.Value: { if (element.LastValue == null) { value.Error = ResultIds.E_INVALID_PID; break; } value.Value = element.LastValue.Value; break; } case PropertyIds.Quality: { if (element.LastValue == null) { value.Error = ResultIds.E_INVALID_PID; break; } value.Value = element.LastValue.Quality; break; } case PropertyIds.Timestamp: { if (element.LastValue == null) { value.Error = ResultIds.E_INVALID_PID; break; } value.Value = element.LastValue.Timestamp; break; } default: { value.Error = ResultIds.E_INVALID_PID; break; } } return value; }
/// <summary> /// Writes the values for the specified item ids. /// </summary> /// <param name="itemIds">The item ids.</param> /// <param name="values">The values.</param> /// <returns>The results.</returns> public int[] Write(string[] itemIds, DaValue[] values) { int[] results = new int[itemIds.Length]; WriteValueCollection valuesToWrite = new WriteValueCollection(); ComDaReadPropertiesRequest[] requests = new ComDaReadPropertiesRequest[values.Length]; // prepare request. for (int ii = 0; ii < itemIds.Length; ii++) { ComDaReadPropertiesRequest request = requests[ii] = new ComDaReadPropertiesRequest(); request.ItemId = itemIds[ii]; } // need to get the data type of the remote node. m_browseManager.GetPropertyValues(Session, requests, PropertyIds.UaBuiltInType, PropertyIds.UaValueRank); // validate items. for (int ii = 0; ii < requests.Length; ii++) { ComDaReadPropertiesRequest request = requests[ii]; if (request.Error < 0) { results[ii] = request.Error; continue; } int? builtInType = request.Values[0].Value as int?; int? valueRank = request.Values[1].Value as int?; if (builtInType == null || valueRank == null) { results[ii] = ResultIds.E_UNKNOWNITEMID; continue; } // convert value to UA data type. WriteValue valueToWrite = new WriteValue(); valueToWrite.NodeId = m_mapper.GetRemoteNodeId(itemIds[ii]); valueToWrite.AttributeId = Attributes.Value; valueToWrite.Handle = ii; // convert value to UA data type. try { TypeInfo remoteType = new TypeInfo((BuiltInType)builtInType.Value, valueRank.Value); valueToWrite.Value = m_mapper.GetRemoteDataValue(values[ii], remoteType); } catch (Exception e) { results[ii] = ComUtils.GetErrorCode(e, ResultIds.E_BADTYPE); continue; } valuesToWrite.Add(valueToWrite); } // check if nothing to do. if (valuesToWrite.Count == 0) { return results; } // write the values to the server. int[] remoteResults = m_groupManager.Write(valuesToWrite); // copy results. for (int ii = 0; ii < valuesToWrite.Count; ii++) { results[(int)valuesToWrite[ii].Handle] = remoteResults[ii]; } return results; }
/// <summary> /// Gets the local data value. /// </summary> /// <param name="remoteValue">The remote value.</param> /// <returns>The local data value.</returns> public DaValue GetLocalDataValue(DataValue remoteValue) { DaValue localValue = new DaValue(); localValue.Error = ComDaProxy.MapReadStatusToErrorCode(remoteValue.StatusCode); if (localValue.Error >= 0) { localValue.HdaQuality = ComUtils.GetHdaQualityCode(remoteValue.StatusCode); localValue.Timestamp = remoteValue.SourceTimestamp; localValue.Error = ResultIds.S_OK; if (localValue.Timestamp == DateTime.MinValue) { localValue.Timestamp = remoteValue.ServerTimestamp; } try { localValue.Value = GetLocalValue(remoteValue.WrappedValue); } catch { localValue.Error = ResultIds.E_BADTYPE; } } return localValue; }