//====================================================================== // GetStatus /// <summary> /// Returns the current server status. /// </summary> /// <returns>The current server status.</returns> public ServerStatus GetStatus() { lock (this) { if (m_proxy == null) { throw new NotConnectedException(); } OpcXml.Da10.RequestOptions options = OpcXml.Da10.Request.GetRequestOptions(m_options.Locale, m_options.Filters); OpcXml.Da10.ServerStatus remoteStatus = null; OpcXml.Da10.ReplyBase reply = m_proxy.GetStatus( options.LocaleID, options.ClientRequestHandle, out remoteStatus); CacheResponse(m_options.Locale, reply, null); // fill in the last update time. ServerStatus status = OpcXml.Da10.Request.GetServerStatus(reply, remoteStatus); status.LastUpdateTime = m_lastUpdateTime; return(status); } }
/// <summary> /// Returns the locales supported by the server /// </summary> /// <remarks>The first element in the array must be the default locale for the server.</remarks> /// <returns>An array of locales with the format "[languagecode]-[country/regioncode]".</returns> public string[] GetSupportedLocales() { lock (this) { if (m_proxy == null) { throw new NotConnectedException(); } OpcXml.Da10.ServerStatus status = null; OpcXml.Da10.ReplyBase reply = m_proxy.GetStatus( m_options.Locale, null, out status); if (status != null && status.SupportedLocaleIDs != null) { ArrayList locales = new ArrayList(); foreach (string locale in status.SupportedLocaleIDs) { if (locale != null) { locales.Add(locale); } } return((string[])locales.ToArray(typeof(string))); } return(null); } }
//====================================================================== // Write /// <summary> /// Writes the value, quality and timestamp for a set of items. /// </summary> /// <param name="items">The set of item values to write.</param> /// <returns>The results of the write operation for each item.</returns> public IdentifiedResult[] Write(ItemValue[] items) { if (items == null) { throw new ArgumentNullException("items"); } if (items.Length == 0) { return(new Opc.IdentifiedResult[0]); } lock (this) { if (m_proxy == null) { throw new NotConnectedException(); } ItemValueList list = new ItemValueList(); list.AddRange(items); OpcXml.Da10.RequestOptions options = OpcXml.Da10.Request.GetRequestOptions(m_options.Locale, m_options.Filters); OpcXml.Da10.WriteRequestItemList requestList = OpcXml.Da10.Request.GetItemValueList(list); OpcXml.Da10.ReplyItemList replyList = null; OpcXml.Da10.OPCError[] errors = null; OpcXml.Da10.ReplyBase reply = m_proxy.Write( options, requestList, false, out replyList, out errors); CacheResponse(m_options.Locale, reply, errors); ItemValueResultList valueList = OpcXml.Da10.Request.GetResultList(replyList); if (valueList == null) { throw new InvalidResponseException(); } IdentifiedResult[] results = new IdentifiedResult[valueList.Count]; for (int ii = 0; ii < valueList.Count; ii++) { ItemValueResult valueResult = valueList[ii]; results[ii] = new IdentifiedResult(valueResult); results[ii].ResultID = valueResult.ResultID; results[ii].DiagnosticInfo = valueResult.DiagnosticInfo; } return(results); } }
/// <summary> /// Called when a subscription receives a polled refresh response. /// </summary> internal void PollCompleted( string localeID, OpcXml.Da10.ReplyBase reply, OpcXml.Da10.OPCError[] errors) { lock (this) { CacheResponse(localeID, reply, errors); m_lastUpdateTime = reply.ReplyTime; } }
//====================================================================== // BrowseNext /// <summary> /// Continues a browse operation with previously specified search criteria. /// </summary> /// <param name="position">An object containing the browse operation state information.</param> /// <returns>The set of elements found.</returns> public BrowseElement[] BrowseNext(ref Opc.Da.BrowsePosition position) { if (position == null) { throw new ArgumentNullException("position"); } if (m_proxy == null) { throw new NotConnectedException(); } lock (this) { BrowsePosition pos = (BrowsePosition)position; if (pos.ContinuationPoint == null || pos.ContinuationPoint == "") { throw new BrowseCannotContinueException(); } OpcXml.Da10.BrowseElement[] elements = null; OpcXml.Da10.OPCError[] errors = null; OpcXml.Da10.ReplyBase reply = m_proxy.Browse( OpcXml.Da10.Request.GetPropertyNames(pos.Filters.PropertyIDs), pos.Locale, "", (pos.ItemID != null)?pos.ItemID.ItemPath:null, (pos.ItemID != null)?pos.ItemID.ItemName:null, ref pos.ContinuationPoint, pos.Filters.MaxElementsReturned, OpcXml.Da10.Request.GetBrowseFilter(pos.Filters.BrowseFilter), pos.Filters.ElementNameFilter, pos.Filters.VendorFilter, pos.Filters.ReturnAllProperties, pos.Filters.ReturnPropertyValues, pos.ReturnErrorText, out elements, out errors, out pos.MoreElements); CacheResponse(pos.Locale, reply, errors); position = null; if (pos.MoreElements || (pos.ContinuationPoint != null && pos.ContinuationPoint != "")) { position = pos; } return(OpcXml.Da10.Request.GetBrowseElements(elements)); } }
//====================================================================== // Browse /// <summary> /// Fetches the children of a branch that meet the filter criteria. /// </summary> /// <param name="itemID">The identifier of branch which is the target of the search.</param> /// <param name="filters">The filters to use to limit the set of child elements returned.</param> /// <param name="position">An object used to continue a browse that could not be completed.</param> /// <returns>The set of elements found.</returns> public BrowseElement[] Browse( ItemIdentifier itemID, BrowseFilters filters, out Opc.Da.BrowsePosition position) { position = null; lock (this) { if (m_proxy == null) { throw new NotConnectedException(); } // use default filters if none specified. if (filters == null) { filters = new BrowseFilters(); } BrowsePosition pos = new BrowsePosition(itemID, filters, m_options.Locale, ((m_options.Filters & (int)ResultFilter.ErrorText) != 0)); OpcXml.Da10.BrowseElement[] elements = null; OpcXml.Da10.OPCError[] errors = null; OpcXml.Da10.ReplyBase reply = m_proxy.Browse( OpcXml.Da10.Request.GetPropertyNames(filters.PropertyIDs), pos.Locale, "", (itemID != null)?itemID.ItemPath:null, (itemID != null)?itemID.ItemName:null, ref pos.ContinuationPoint, filters.MaxElementsReturned, OpcXml.Da10.Request.GetBrowseFilter(filters.BrowseFilter), filters.ElementNameFilter, filters.VendorFilter, filters.ReturnAllProperties, filters.ReturnPropertyValues, pos.ReturnErrorText, out elements, out errors, out pos.MoreElements); CacheResponse(pos.Locale, reply, errors); if (pos.MoreElements || (pos.ContinuationPoint != null && pos.ContinuationPoint != "")) { position = pos; } return(OpcXml.Da10.Request.GetBrowseElements(elements)); } }
//====================================================================== // Read /// <summary> /// Reads the current values for a set of items. /// </summary> /// <param name="items">The set of items to read.</param> /// <returns>The results of the read operation for each item.</returns> public Opc.Da.ItemValueResult[] Read(Item[] items) { if (items == null) { throw new ArgumentNullException("items"); } if (items.Length == 0) { return(new Opc.Da.ItemValueResult[0]); } lock (this) { if (m_proxy == null) { throw new NotConnectedException(); } ItemList list = new ItemList(); list.AddRange(items); OpcXml.Da10.RequestOptions options = OpcXml.Da10.Request.GetRequestOptions(m_options.Locale, m_options.Filters); OpcXml.Da10.ReadRequestItemList requestList = OpcXml.Da10.Request.GetItemList(list); OpcXml.Da10.ReplyItemList replyList = null; OpcXml.Da10.OPCError[] errors = null; OpcXml.Da10.ReplyBase reply = m_proxy.Read( options, requestList, out replyList, out errors); CacheResponse(m_options.Locale, reply, errors); ItemValueResultList valueList = OpcXml.Da10.Request.GetResultList(replyList); if (valueList == null) { throw new InvalidResponseException(); } return((ItemValueResult[])valueList.ToArray(typeof(ItemValueResult))); } }
//====================================================================== // Private Methods /// <summary> /// Caches error messages and request statistics after each request. /// </summary> internal void CacheResponse( string locale, OpcXml.Da10.ReplyBase reply, OpcXml.Da10.OPCError[] errors) { lock (this) { if (reply != null) { // check for revised locale id. if (reply.RevisedLocaleID != null) { locale = reply.RevisedLocaleID; } // calculate the bias to use when calculating request timeouts. m_timebias = reply.ReplyTime.Subtract(DateTime.Now); } if (errors != null && errors.Length > 0) { // check for null locale. if (locale == null) { locale = ""; } // find the message table for the locale. Hashtable messageTable = (Hashtable)m_messageTables[locale]; if (messageTable == null) { m_messageTables[locale] = messageTable = new Hashtable(); } // index message texts by message id. foreach (OpcXml.Da10.OPCError error in errors) { messageTable[OpcXml.Da10.Request.GetResultID(error.ID)] = error.Text; } } } }
//====================================================================== // GetProperties /// <summary> /// Returns the item properties for a set of items. /// </summary> /// <param name="itemIDs">A list of item identifiers.</param> /// <param name="propertyIDs">A list of properties to fetch for each item.</param> /// <param name="returnValues">Whether the property values should be returned with the properties.</param> /// <returns>A list of properties for each item.</returns> public ItemPropertyCollection[] GetProperties( ItemIdentifier[] itemIDs, PropertyID[] propertyIDs, bool returnValues) { if (itemIDs == null) { throw new ArgumentNullException("itemIDs"); } if (m_proxy == null) { throw new NotConnectedException(); } lock (this) { OpcXml.Da10.RequestOptions options = OpcXml.Da10.Request.GetRequestOptions(m_options.Locale, m_options.Filters); OpcXml.Da10.PropertyReplyList[] properties = null; OpcXml.Da10.OPCError[] errors = null; OpcXml.Da10.ReplyBase reply = m_proxy.GetProperties( OpcXml.Da10.Request.GetItemIdentifiers(itemIDs), OpcXml.Da10.Request.GetPropertyNames(propertyIDs), options.LocaleID, options.ClientRequestHandle, null, (propertyIDs == null), returnValues, options.ReturnErrorText, out properties, out errors); CacheResponse(options.LocaleID, reply, errors); return(OpcXml.Da10.Request.GetItemPropertyCollections(properties)); } }
/// <summary> /// Establishes a subscription for the current set of items. /// </summary> private ItemValueResultList Subscribe(ArrayList items) { lock (this) { // cancel any current subscription. Unsubscribe(); // check if there is nothing to do. if (items == null || items.Count == 0) { return(new ItemValueResultList()); } // create a single item list and use the subscription state to set list level parameters. ItemList itemList = new ItemList(); itemList.ClientHandle = Guid.NewGuid().ToString(); itemList.ItemPath = null; itemList.ReqType = null; itemList.Deadband = m_state.Deadband; itemList.DeadbandSpecified = true; itemList.SamplingRate = m_state.UpdateRate; itemList.SamplingRateSpecified = true; itemList.EnableBuffering = false; itemList.EnableBufferingSpecified = false; // set the ping rate based on the keep alive (if specified) or five times update rate. m_pingRate = (m_state.KeepAlive != 0)?m_state.KeepAlive:m_state.UpdateRate * 5; // stop any existing timer. if (m_pollTimer != null) { m_pollTimer.Dispose(); m_pollTimer = null; } // create copies of each and replace the client handle with the server handle. foreach (Item item in items) { Item clone = (Item)item.Clone(); clone.ClientHandle = clone.ServerHandle; itemList.Add(clone); } string subscription = null; // establish the subscription on the server. OpcXml.Da10.RequestOptions options = OpcXml.Da10.Request.GetRequestOptions(m_state.Locale, m_filters); OpcXml.Da10.SubscribeReplyItemList replyList = null; OpcXml.Da10.OPCError[] errors = null; OpcXml.Da10.ReplyBase reply = m_proxy.Subscribe( options, OpcXml.Da10.Request.GetSubscribeList(itemList), true, m_pingRate * 2, out replyList, out errors, out subscription); // cache results with the server object. m_server.CacheResponse(m_state.Locale, reply, errors); // save subscription handle. m_state.ServerHandle = subscription; // check for valid response. if (replyList == null) { throw new InvalidResponseException(); } // save the revised update rate. if (replyList.RevisedSamplingRateSpecified) { m_state.UpdateRate = (int)replyList.RevisedSamplingRate; } // update items. ItemValueResultList resultList = OpcXml.Da10.Request.GetSubscribeResultList(replyList); for (int ii = 0; ii < itemList.Count; ii++) { SubscribeItemValueResult resultItem = (SubscribeItemValueResult)resultList[ii]; // restore the client/server handles in the result. resultItem.ServerHandle = resultItem.ClientHandle; resultItem.ClientHandle = ((Item)items[ii]).ClientHandle; // check if the requested sampling rate was accepted. if (!resultItem.SamplingRateSpecified) { resultItem.SamplingRate = itemList[ii].SamplingRate; resultItem.SamplingRateSpecified = itemList[ii].SamplingRateSpecified; } } // schedule polling. SchedulePoll(); // return result list. return(resultList); } }
/// <summary> /// Called when a poll completes. /// </summary> private void OnPollCompleted(IAsyncResult handle) { // restore async state parameters. string[] args = (string[])handle.AsyncState; try { lock (this) { // check if object has been disposed. if (m_proxy == null) { return; } // fetch the poll results. string[] invalidHandles = null; bool bufferOverflow = false; OpcXml.Da10.SubscribePolledRefreshReplyItemList[] replyLists = null; OpcXml.Da10.OPCError[] errors = null; OpcXml.Da10.ReplyBase reply = null; reply = m_proxy.EndSubscriptionPolledRefresh( handle, out invalidHandles, out replyLists, out errors, out bufferOverflow); // cache results with the server object. m_server.PollCompleted((string)args[1], reply, errors); // check if the server handle has changed - ignore results in this case. if (!args[0].Equals(m_state.ServerHandle)) { return; } ItemValueResultList resultList = null; // restore subscription if it has been dropped by the server for some reason. if (invalidHandles != null && invalidHandles.Length > 0) { resultList = Subscribe(m_items); if (resultList != null && resultList.Count == m_items.Count) { // replace the client handles. for (int ii = 0; ii < m_items.Count; ii++) { SubscribeItemValueResult result = (SubscribeItemValueResult)resultList[ii]; if (result.ResultID.Succeeded()) { Item item = (Item)m_items[ii]; // save revised sampling rate. item.SamplingRate = result.SamplingRate; item.SamplingRateSpecified = result.SamplingRateSpecified; } } } } // get the list of changed items. else { ItemValueResultList[] resultLists = OpcXml.Da10.Request.GetSubscribeRefreshLists(replyLists); // validate items and replace the client handles. resultList = new ItemValueResultList(); if (resultLists != null && resultLists.Length == 1) { foreach (ItemValueResult result in resultLists[0]) { foreach (Item item in m_items) { if (item.ServerHandle.Equals(result.ClientHandle)) { result.ServerHandle = item.ServerHandle; result.ClientHandle = item.ClientHandle; resultList.Add(result); break; } } } } } // send the data change notifications. OnDataChange(resultList); // check if a new poll should be scheduled. if (args[2] != null) { SchedulePoll(); } } } catch (Exception e) { string message = e.Message; // schedule a new poll on exception. if (args[2] != null) { SchedulePoll(); } } }