/// <summary>
        /// Updates a result list based on the request options and sets the handles for use by the client.
        /// </summary>
        public ItemIdentifier[] ApplyFilters(int filters, ItemIdentifier[] results)
        {
            if (results == null)
            {
                return(null);
            }

            foreach (ItemIdentifier result in results)
            {
                if (result.ServerHandle != null)
                {
                    SubscriptionItem item = (SubscriptionItem)m_items[result.ServerHandle];

                    if (item != null)
                    {
                        result.ItemName     = ((filters & (int)ResultFilter.ItemName) != 0)?item.ItemID:null;
                        result.ItemPath     = null;
                        result.ServerHandle = result.ServerHandle;
                        result.ClientHandle = ((filters & (int)ResultFilter.ClientHandle) != 0)?item.ClientHandle:null;
                    }
                }

                if ((filters & (int)ResultFilter.ItemTime) == 0)
                {
                    if (typeof(ItemValue).IsInstanceOfType(result))
                    {
                        ((ItemValueResult)result).Timestamp          = DateTime.MinValue;
                        ((ItemValueResult)result).TimestampSpecified = false;
                    }
                }
            }

            return(results);
        }
        /// <remarks/>
        public IdentifiedResult[] Write(ItemValue[] items)
        {
            if (items == null)
            {
                throw new ArgumentNullException("items");
            }

            lock (this)
            {
                // handle trivial case.
                if (items.Length == 0)
                {
                    return(new IdentifiedResult[0]);
                }

                // create item results.
                IdentifiedResult[] results = new IdentifiedResult[items.Length];

                for (int ii = 0; ii < items.Length; ii++)
                {
                    // initialize result with item.
                    results[ii] = new IdentifiedResult(items[ii]);

                    // check for invalid handle.
                    if (items[ii].ServerHandle == null)
                    {
                        results[ii].ResultID = ResultID.Da.E_INVALIDHANDLE;
                        continue;
                    }

                    // lookup subscription item.
                    SubscriptionItem item = (SubscriptionItem)m_items[items[ii].ServerHandle];

                    if (item == null)
                    {
                        results[ii].ResultID = ResultID.Da.E_INVALIDHANDLE;
                        continue;
                    }

                    // write the item value.
                    results[ii] = item.Write(m_state.Locale, items[ii]);
                    results[ii].ServerHandle = items[ii].ServerHandle;
                }

                // apply result filters.
                ApplyFilters(m_filters, results);

                // return results.
                return(results);
            }
        }
        /// <remarks/>
        public IdentifiedResult[] RemoveItems(ItemIdentifier[] items)
        {
            if (items == null)
            {
                throw new ArgumentNullException("items");
            }

            lock (this)
            {
                // handle trivial case.
                if (items.Length == 0)
                {
                    return(new IdentifiedResult[0]);
                }

                // create item results.
                IdentifiedResult[] results = new IdentifiedResult[items.Length];

                for (int ii = 0; ii < items.Length; ii++)
                {
                    // initialize result with item.
                    results[ii] = new IdentifiedResult(items[ii]);

                    // check for invalid handle.
                    if (items[ii].ServerHandle == null)
                    {
                        results[ii].ResultID = ResultID.Da.E_INVALIDHANDLE;
                        continue;
                    }

                    // lookup subscription item.
                    SubscriptionItem item = (SubscriptionItem)m_items[items[ii].ServerHandle];

                    if (item == null)
                    {
                        results[ii].ResultID = ResultID.Da.E_INVALIDHANDLE;
                        continue;
                    }

                    // remove item.
                    m_items.Remove(items[ii].ServerHandle);

                    results[ii].ResultID = ResultID.S_OK;
                }

                // return results.
                return(results);
            }
        }
        /// <summary>
        /// Begins an asynchronous write operation for a set of items.
        /// </summary>
        /// <param name="items">The set of item values to write (must include the server handle).</param>
        /// <param name="requestHandle">An identifier for the request assigned by the caller.</param>
        /// <param name="callback">A delegate used to receive notifications when the request completes.</param>
        /// <param name="request">An object that contains the state of the request (used to cancel the request).</param>
        /// <returns>A set of results containing any errors encountered when the server validated the items.</returns>
        public IdentifiedResult[] Write(
            ItemValue[]               items,
            object requestHandle,
            WriteCompleteEventHandler callback,
            out IRequest request)
        {
            if (items == null)
            {
                throw new ArgumentNullException("items");
            }

            lock (this)
            {
                request = null;

                // handle trivial case.
                if (items.Length == 0)
                {
                    return(new IdentifiedResult[0]);
                }

                // validate the items.
                ArrayList validItems = new ArrayList();

                IdentifiedResult[] results = new IdentifiedResult[items.Length];

                for (int ii = 0; ii < items.Length; ii++)
                {
                    // initialize result with item.
                    results[ii] = new IdentifiedResult(items[ii]);

                    // check for invalid handle.
                    if (items[ii].ServerHandle == null)
                    {
                        results[ii].ResultID = ResultID.Da.E_INVALIDHANDLE;
                        continue;
                    }

                    // lookup subscription item.
                    SubscriptionItem item = (SubscriptionItem)m_items[items[ii].ServerHandle];

                    if (item == null)
                    {
                        results[ii].ResultID = ResultID.Da.E_INVALIDHANDLE;
                        continue;
                    }

                    // at least one valid item exists.
                    validItems.Add(items[ii]);
                }

                if (validItems.Count > 0)
                {
                    request = new Opc.Da.Request(this, requestHandle);

                    m_requests.Add(request, callback);
                    m_writeQueue.Enqueue(new object[] { request, validItems.ToArray(typeof(ItemValue)) });

                    ThreadPool.QueueUserWorkItem(new WaitCallback(OnWrite));
                }

                // apply result filters.
                ApplyFilters(m_filters, results);

                // return results.
                return(results);
            }
        }
        /// <remarks/>
        public ItemResult[] ModifyItems(int masks, Item[] items)
        {
            if (items == null)
            {
                throw new ArgumentNullException("items");
            }

            lock (this)
            {
                // handle trivial case.
                if (items.Length == 0)
                {
                    return(new ItemResult[0]);
                }

                // create item results.
                ItemResult[] results = new ItemResult[items.Length];

                for (int ii = 0; ii < items.Length; ii++)
                {
                    // initialize result with item.
                    results[ii] = new ItemResult(items[ii]);

                    // check for invalid handle.
                    if (items[ii].ServerHandle == null)
                    {
                        results[ii].ResultID = ResultID.Da.E_INVALIDHANDLE;
                        continue;
                    }

                    // lookup existing item.
                    SubscriptionItem item = (SubscriptionItem)m_items[items[ii].ServerHandle];

                    // check that the item is valid.
                    if (m_items.Contains(items))
                    {
                        results[ii].ResultID = ResultID.Da.E_INVALIDHANDLE;
                        continue;
                    }

                    // update subscription item.
                    System.Type reqType         = item.ReqType;
                    bool        active          = item.Active;
                    object      clientHandle    = item.ClientHandle;
                    float       deadband        = item.Deadband;
                    int         samplingRate    = item.SamplingRate;
                    bool        enableBuffering = item.BufferEnabled;

                    // requested data type.
                    if ((masks & (int)StateMask.ReqType) != 0)
                    {
                        // check that requested datatype is valid.
                        if (items[ii].ReqType == Type.ILLEGAL_TYPE)
                        {
                            results[ii].ResultID = ResultID.Da.E_BADTYPE;
                            continue;
                        }

                        reqType = items[ii].ReqType;
                    }

                    // client handle.
                    if ((masks & (int)StateMask.ClientHandle) != 0)
                    {
                        clientHandle = items[ii].ClientHandle;
                    }

                    // deadband.
                    if ((masks & (int)StateMask.Deadband) != 0)
                    {
                        if (items[ii].DeadbandSpecified)
                        {
                            deadband = items[ii].Deadband;

                            if (deadband < 0.0 || deadband > 100.0)
                            {
                                results[ii].ResultID = ResultID.E_INVALIDARG;
                                continue;
                            }
                        }
                        else
                        {
                            deadband = -1;
                        }
                    }

                    // sampling rate.
                    if ((masks & (int)StateMask.SamplingRate) != 0)
                    {
                        samplingRate = (items[ii].SamplingRateSpecified)?items[ii].SamplingRate:-1;

                        int requestedSamplingRate = samplingRate;

                        if (samplingRate != -1)
                        {
                            samplingRate = Cache.AdjustUpdateRate(requestedSamplingRate);
                        }

                        if (requestedSamplingRate != samplingRate)
                        {
                            results[ii].ResultID = ResultID.Da.S_UNSUPPORTEDRATE;
                        }
                    }

                    if ((masks & (int)StateMask.EnableBuffering) != 0)
                    {
                        enableBuffering = (items[ii].EnableBufferingSpecified)?items[ii].EnableBuffering:false;
                    }

                    // active.
                    if ((masks & (int)StateMask.Active) != 0)
                    {
                        active = (items[ii].ActiveSpecified)?items[ii].Active:item.Active;

                        // ensure a callback is sent on the next update.
                        if (active && !item.Active)
                        {
                            item.ResetLastUpdate();
                        }
                    }

                    // save new values.
                    item.ReqType       = reqType;
                    item.Active        = active;
                    item.ClientHandle  = clientHandle;
                    item.Deadband      = deadband;
                    item.SamplingRate  = samplingRate;
                    item.BufferEnabled = enableBuffering;

                    // update result object.
                    results[ii].Active                   = item.Active;
                    results[ii].ActiveSpecified          = true;
                    results[ii].ClientHandle             = item.ClientHandle;
                    results[ii].ReqType                  = item.ReqType;
                    results[ii].Deadband                 = (item.Deadband != -1)?item.Deadband:0;
                    results[ii].DeadbandSpecified        = item.Deadband != -1;
                    results[ii].SamplingRate             = (item.SamplingRate != -1)?item.SamplingRate:0;
                    results[ii].SamplingRateSpecified    = item.SamplingRate != -1;
                    results[ii].EnableBuffering          = item.BufferEnabled;
                    results[ii].EnableBufferingSpecified = item.BufferEnabled;
                }

                // return results.
                return(results);
            }
        }
        /// <remarks/>
        public ItemResult[] AddItems(Item[] items)
        {
            if (items == null)
            {
                throw new ArgumentNullException("items");
            }

            lock (this)
            {
                // handle trivial case.
                if (items.Length == 0)
                {
                    return(new ItemResult[0]);
                }

                // create item results.
                ItemResult[] results = new ItemResult[items.Length];

                for (int ii = 0; ii < items.Length; ii++)
                {
                    // initialize result with item.
                    results[ii] = new ItemResult((ItemIdentifier)items[ii]);

                    // check that the item is valid.
                    if (items[ii].ItemName == null || items[ii].ItemName.Length == 0)
                    {
                        results[ii].ResultID = ResultID.Da.E_INVALID_ITEM_NAME;
                        continue;
                    }

                    // check that the item exists.
                    if (!m_cache.IsValidItem(items[ii].ItemName))
                    {
                        results[ii].ResultID = ResultID.Da.E_UNKNOWN_ITEM_NAME;
                        continue;
                    }

                    // check that requested datatype is valid.
                    if (items[ii].ReqType == typeof(Opc.Type))
                    {
                        results[ii].ResultID = ResultID.Da.E_BADTYPE;
                        continue;
                    }

                    // create a new subscription item.
                    SubscriptionItem item = new SubscriptionItem(items[ii].ItemName, m_cache);

                    item.Active        = (items[ii].ActiveSpecified)?items[ii].Active:true;
                    item.ClientHandle  = items[ii].ClientHandle;
                    item.ReqType       = items[ii].ReqType;
                    item.Deadband      = (items[ii].DeadbandSpecified)?items[ii].Deadband:-1;
                    item.SamplingRate  = (items[ii].SamplingRateSpecified)?items[ii].SamplingRate:-1;
                    item.BufferEnabled = (items[ii].EnableBufferingSpecified)?items[ii].EnableBuffering:false;

                    // update sampling rate.
                    if (item.SamplingRate != -1)
                    {
                        item.SamplingRate = Cache.AdjustUpdateRate(item.SamplingRate);
                    }

                    // assign unique server handle.
                    results[ii].ServerHandle = item.ServerHandle = AssignHandle();

                    // update result object.
                    results[ii].Active                   = item.Active;
                    results[ii].ActiveSpecified          = true;
                    results[ii].ClientHandle             = item.ClientHandle;
                    results[ii].ReqType                  = item.ReqType;
                    results[ii].Deadband                 = (item.Deadband != -1)?item.Deadband:0;
                    results[ii].DeadbandSpecified        = item.Deadband != -1;
                    results[ii].SamplingRate             = (item.SamplingRate != -1)?item.SamplingRate:0;
                    results[ii].SamplingRateSpecified    = item.SamplingRate != -1;
                    results[ii].EnableBuffering          = item.BufferEnabled;
                    results[ii].EnableBufferingSpecified = item.SamplingRate != -1;

                    // save reference to new item.
                    m_items[results[ii].ServerHandle] = item;
                }

                // return results.
                return(results);
            }
        }