/// <summary> /// This method is used to write data of the specified list to the server. It is called /// by the ClientBase after the client application has prepared and committed the data /// values. /// </summary> /// <param name="serverListId"> The server identifier of the list containing the data objects to write. </param> /// <param name="writeValueArrays"> The data values to write. </param> /// <returns> /// The list server aliases and result codes for the data objects whose write failed. Returns null if all writes /// succeeded or null if this is a keep-alive. /// </returns> public List <AliasResult>?WriteData(uint serverListId, DataValueArraysWithAlias writeValueArrays) { if (_disposed) { throw new ObjectDisposedException("Cannot access a disposed XiContext."); } if (_writeEndpoint is null) { throw new Exception("No Write Endpoint"); } if (_writeEndpoint.Disposed) { return(null); } IWrite iWrite = _writeEndpoint.Proxy; string contextId = ContextId; _writeEndpoint.LastCallUtc = DateTime.UtcNow; List <AliasResult>?listAliasResult = null; try { listAliasResult = iWrite.WriteVST(contextId, serverListId, writeValueArrays); } catch (Exception ex) { ProcessRemoteMethodCallException(ex); } return(listAliasResult); }
/// <summary> /// <para> This callback method is implemented by the client to receive data changes. </para> /// <para> /// Servers send data changes to the client that have not been reported to the client via this method. Changes /// consists of: /// </para> /// <para> 1) values for data objects that were added to the list, </para> /// <para> /// 2) values for data objects whose current values have changed since the last time they were reported to the /// client via this interface. If a deadband filter has been defined for the list, floating point values are not /// considered to have changed unless they have changed by the deadband amount. /// </para> /// <para> 3) historical values that meet the list filter criteria, including the deadband. </para> /// <para> /// In addition, the server may insert a special value that indicates the server or one of its wrapped servers /// are shutting down. /// </para> /// <para> /// This value is inserted as the first value in the list of values in the callback. Its ListId and ClientId are /// both 0 and its data type is ServerStatus. /// </para> /// </summary> /// <param name="contextId"> The context identifier. </param> /// <param name="clientListId"> The client identifier of the list for which data changes are being reported. </param> /// <param name="updatedValues"> The values being reported. </param> void ICallback.InformationReport(string contextId, uint clientListId, DataValueArraysWithAlias updatedValues) { XiContext?context = XiContext.LookUpContext(contextId); if (context is not null) { context.NotifyCallbackRecieved(); _xiCallbackDoer.BeginInvoke(ct => context.ElementValuesCallback(clientListId, updatedValues)); } }
/// <summary> /// <para> No throws.</para> /// <para> This callback method is implemented by the client to receive data changes. </para> /// <para> /// Servers send data changes to the client that have not been reported to the client via this method. Changes /// consists of: /// </para> /// <para> 1) values for data objects that were added to the list, </para> /// <para> /// 2) values for data objects whose current values have changed since the last time they were reported to the /// client via this interface. If a deadband filter has been defined for the list, floating point values are not /// considered to have changed unless they have changed by the deadband amount. /// </para> /// <para> 3) historical values that meet the list filter criteria, including the deadband. </para> /// <para> /// In addition, the server may insert a special value that indicates the server or one of its wrapped servers /// are shutting down. /// </para> /// <para> /// This value is inserted as the first value in the list of values in the callback. Its ListId and ClientId are /// both 0 and its data type is ServerStatus. /// </para> /// </summary> /// <param name="clientListId"> The client identifier of the list for which data changes are being reported. </param> /// <param name="updatedValues"> The values being reported. </param> public void ElementValuesCallback(uint clientListId, DataValueArraysWithAlias updatedValues) { if (_disposed) { return; } if (_callbackEndpoint is not null) { _callbackEndpoint.LastCallUtc = DateTime.UtcNow; } XiDataList?datalist = GetDataList(clientListId); if (datalist is null) { return; } ElementValuesCallbackInternal(datalist, updatedValues); }
/// <summary> /// <para> /// Writing data object values to the server is a two step process composed of preparing a list of data objects /// to be written, followed by writing that list to the server. /// </para> /// <para> /// This method is used in the first step to individually mark each data object in the Data List as ready for /// writing. It examines all data objects in the Data List that are ready for writing and writes them to the server /// . /// </para> /// </summary> /// <returns> /// The list of data objects whose write failed. Results are not returned data object whose writes succeeded. If /// all writes succeeded, null is returned. /// </returns> public IEnumerable <IXiDataListItem>?CommitWriteDataListItems() { if (Disposed) { throw new ObjectDisposedException("Cannot access a disposed XiDataList."); } DataValueArraysWithAlias?writeValueArrays = null; var writeValueDictionary = new Dictionary <uint, XiDataListItem>(); int uintCount = 0; int dblCount = 0; int objCount = 0; foreach (XiDataListItem item in ListItemsManager) { if (item.PendingWriteValueStatusTimestamp is not null && item.PendingWriteValueStatusTimestamp.Value.ValueStatusCode != ValueStatusCode.Unknown) { switch (item.PendingWriteValueStatusTimestamp.Value.Value.ValueStorageType) { case Any.StorageType.Double: dblCount += 1; break; case Any.StorageType.UInt32: uintCount += 1; break; case Any.StorageType.Object: objCount += 1; break; } writeValueDictionary.Add(item.ClientAlias, item); } } if (0 < dblCount || 0 < uintCount || 0 < objCount) { writeValueArrays = new DataValueArraysWithAlias(dblCount, uintCount, objCount); int intIdx = 0; int dblIdx = 0; int objIdx = 0; foreach (var kvp in writeValueDictionary) { XiDataListItem item = kvp.Value; if (item.PendingWriteValueStatusTimestamp is not null && item.PendingWriteValueStatusTimestamp.Value.ValueStatusCode != ValueStatusCode.Unknown) { var statusCode = XiStatusCode.MakeStatusCode( XiStatusCode.MakeStatusByte((byte)XiStatusCodeStatusBits.GoodNonSpecific, 0), XiStatusCode.MakeFlagsByte((byte)XiStatusCodeHistoricalValueType.NotUsed, false, false, XiStatusCodeAdditionalDetailType.NotUsed), 0); switch (item.PendingWriteValueStatusTimestamp.Value.Value.ValueStorageType) { case Any.StorageType.Double: writeValueArrays.SetDouble(dblIdx++, item.ServerAlias, statusCode, item.PendingWriteValueStatusTimestamp.Value.TimestampUtc, item.PendingWriteValueStatusTimestamp.Value.Value.StorageDouble); break; case Any.StorageType.UInt32: writeValueArrays.SetUint(intIdx++, item.ServerAlias, statusCode, item.PendingWriteValueStatusTimestamp.Value.TimestampUtc, item.PendingWriteValueStatusTimestamp.Value.Value.StorageUInt32); break; case Any.StorageType.Object: writeValueArrays.SetObject(objIdx++, item.ServerAlias, statusCode, item.PendingWriteValueStatusTimestamp.Value.TimestampUtc, item.PendingWriteValueStatusTimestamp.Value.Value.StorageObject); break; } } item.HasWritten(XiFaultCodes.S_OK); } } var listXiValues = new List <XiDataListItem>(); if (writeValueArrays is not null) { List <AliasResult>?listAliasResult = Context.WriteData(ServerListId, writeValueArrays); if (listAliasResult is not null) { foreach (AliasResult aliasResult in listAliasResult) { XiDataListItem?item = null; if (writeValueDictionary.TryGetValue(aliasResult.ClientAlias, out item)) { item.HasWritten(aliasResult.Result); listXiValues.Add(item); } } } } writeValueDictionary.Clear(); return((0 != listXiValues.Count) ? listXiValues : null); }