Exemple #1
0
        public TabTreeListView()
        {
            //Console.WriteLine("VarEnum.VT_ARRAY | VarEnum.VT_EMPTY is: " + (VarEnum.VT_ARRAY | VarEnum.VT_VARIANT));
            //Console.WriteLine("typeof(System.Type).GetFields(BindingFlags.Public | BindingFlags.Static) is : " + (System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static));

            CultureInfo culture = new CultureInfo("zh-CN");             // Saudi Arabia

            System.Threading.Thread.CurrentThread.CurrentCulture = culture;


            //CheckForIllegalCrossThreadCalls = false;
            InitializeComponent();
            DllHelper.RegisterDllAndExe();

            this.ListView = treeListView;
            //this.ListView.CellClick += (sender, args) => Debug.WriteLine("CellClicked: {0}", args);

            this.ListView.SelectionChanged += delegate(object sender, EventArgs args) {
                if (this.ListView.SelectedObject != null)
                {
                    lastTag = (OPCTag)(this.ListView.SelectedObject);
                    ShowTagProperties((OPCTag)(this.ListView.SelectedObject));
                }
            };

            createTagStructure();

            opcClient = new OPCClient();
            opcClient.AddTagsComplete        += new EventHandler(OnAddTagsComplete);
            opcClient.GetAllTagNamesComplete += new EventHandler(OnGetAllTagNamesComplete);
            //opcClient.GetLocalServer();
        }
        ///////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Writes the opc tag value to the server asyncronously
        /// </summary>
        /// <param name="tag"></param>
        /// <returns></returns>
        ///////////////////////////////////////////////////////////////////////////////////////////
        private void AsyncWrite(OPCTag tag)
        {
            try {
                Array serverErrors  = default(Array);
                Array serverHandles = new int[2];
                serverHandles.SetValue((int)tag.OPCItem.ServerHandle, 1);


                // 它的这个根据自己的DataType 来Write 数据的,我可以不必理会
                //Array values = GetValue(tag);


                // 这是网上的一种写法
                //object[] valueTemp = new object[2] {"",tag.Value };
                //Array values=(Array)valueTemp;

                // 这是那个 OPCClient 的写法
                Array values = new object[2];
                values.SetValue(tag.Value, 1);

                int cancelID;
                int transID = GetNextTransID();
                tag.OPCGroup.AsyncWrite(1, ref serverHandles, ref values, out serverErrors, transID, out cancelID);
                mAcyncWriteTags.Add(transID, tag);

                HandleServerError(serverErrors);
            } catch (Exception ex) {
                //Exception EVent
            }
        }
Exemple #3
0
        private void ShowTagPropertiesWorker(OPCTag tag)
        {
            //OPCTag tag = listView.SelectedObject as OPCTag;
            //OPCTag focusedTag = listView.FocusedObject as OPCTag;

            if (tag != null && tag.isRealTag)
            {
                if (tag.Value == null)
                {
                    this.TagValueBox.Text = "<null>";
                }
                else
                {
                    this.TagValueBox.Text = tag.Value.ToString();
                }
                this.TagQualityBox.Text   = tag.QualityText;
                this.TagTimestampBox.Text = tag.ItemTimeStampText;
            }
            else if (tag != null && !tag.isRealTag)
            {
                this.TagValueBox.Text     = "";
                this.TagQualityBox.Text   = "";
                this.TagTimestampBox.Text = "";
            }

            this.WriteValueInput.Text = "";
        }
        public void ReadPropertyValues(ref OPCTag tag)
        {
            // count 必须比 propIds.Length 少一个
            int count = 4;

            List <int> propIds = new List <int>();

            propIds.Add(0);
            propIds.Add(1);
            propIds.Add(2);
            propIds.Add(3);
            propIds.Add(4);


            Array PropertyIDs = propIds.ToArray();
            Array PropertyValues;
            Array Errors;

            try{
                mServer.GetItemProperties(tag.Name, count, ref PropertyIDs, out PropertyValues, out Errors);
                if ((count == 0) || (count > 10000))
                {
                    return;
                }

                for (int i = 1; i <= count; i++)
                {
                    if (i == 1)
                    {
                        VarEnum dataType = (VarEnum)VarEnum.ToObject(typeof(VarEnum), PropertyValues.GetValue(i));

                        //tag.DataType = dataType;

                        //var text = dataType.ToString();

                        // these methods are clunkier but run 25% faster on my machine
                        var text2 = Enum.GetName(typeof(VarEnum), dataType);
                        //var text3 = typeof(VarEnum).GetEnumName(dataType);
                        tag.DataTypeText = text2;
                    }
                    else if (i == 2)
                    {
                        tag.SetValue(this, PropertyValues.GetValue(i));
                    }
                    else if (i == 3)
                    {
                        tag.Quality = (OPCQuality)PropertyValues.GetValue(i);
                    }
                    else if (i == 4)
                    {
                        tag.ItemTimeStamp = (DateTime)PropertyValues.GetValue(i);
                    }
                    break;
                }
            } catch (Exception ex)
            {
            }
        }
Exemple #5
0
        public void OnTagValueUpdate(object sender, TagEventArgs e)
        {
            if (this.ListView.InvokeRequired)
            {
                //instansiate a delegate with the method
                OnTagValueUpdateDelegate myDelegate = new OnTagValueUpdateDelegate(OnTagValueUpdateWorker);
                //Invoke delegate
                this.ListView.Invoke(myDelegate, sender, e);
            }
            else
            {
                //Console.WriteLine("in OnTagValueUpdate 1");
                // Server端的Value变了,treelistview 的回调函数
                if (sender.Equals(opcClient))
                {
                    // 这里的 sender 到底是不是  opcclient 呢?
                    try {
                        OPCTag tag = keyTagMap[e.Tag.Name];

                        if (this.ListView.SelectedObject != null)
                        {
                            if (tag == (OPCTag)this.ListView.SelectedObject)
                            {
                                ShowTagProperties(tag);
                            }
                        }
                        else if (lastTag != null)
                        {
                            if (tag == lastTag)
                            {
                                ShowTagProperties(tag);
                            }
                        }
                        //this.treeListView.Refresh();
                        //this.treeListView.RefreshOverlays();
                        //this.treeListView.RefreshHotItem();
                        this.treeListView.Invalidate();

                        //if (tag.isFirstUpdate)
                        //{
                        //tag.isFirstUpdate = false;

                        ////opcClient.QueryAvailableProperties(tag.Name);
                        ////opcClient.GetItemProperties(tag.Name);
                        //}

                        //Console.WriteLine("in OnTagValueUpdate, Name is: " + e.Tag.Name + ", Value is: " + e.Tag.Value);

                        // 这里,应该让 TreeListView 刷新。 能不能做到单独刷新一个 元素呢?
                    } catch (Exception ex) {
                        //Error event
                    }
                }
                //Console.WriteLine("in OnTagValueUpdate 2");
            }
        }
        ///////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Converts the tag value to an array for writing to the OPC Server
        /// </summary>
        /// <param name="tag"></param>
        /// <returns></returns>
        ///////////////////////////////////////////////////////////////////////////////////////////////
        private Array GetValue(OPCTag tag)
        {
            Array values = new object[2];

            switch (tag.DataType)
            {
            case DataType.Bool:
                values.SetValue(Convert.ToBoolean(tag.Value), 1);
                break;

            case DataType.Byte:
                values.SetValue(Convert.ToByte(tag.Value), 1);
                break;

            case DataType.Int16:
                values.SetValue(Convert.ToInt16(tag.Value), 1);
                break;

            case DataType.Int32:
                values.SetValue(Convert.ToInt32(tag.Value), 1);
                break;

            case DataType.Int64:
                values.SetValue(Convert.ToInt64(tag.Value), 1);
                break;

            case DataType.UInt16:
                values.SetValue(Convert.ToUInt16(tag.Value), 1);
                break;

            case DataType.UInt32:
                values.SetValue(Convert.ToUInt32(tag.Value), 1);
                break;

            case DataType.UInt64:
                values.SetValue(Convert.ToUInt64(tag.Value), 1);
                break;

            case DataType.Real32:
                values.SetValue(Convert.ToSingle(tag.Value), 1);
                break;

            case DataType.Real64:
                values.SetValue(Convert.ToDouble(tag.Value), 1);
                break;

            case DataType.String:
                values.SetValue(Convert.ToString(tag.Value), 1);
                break;
            }
            return(values);
        }
 ///////////////////////////////////////////////////////////////////////////////////////////
 /// <summary>
 /// Writes the opc tag value to the server syncronously
 /// </summary>
 /// <param name="tag"></param>
 /// <returns></returns>
 ///////////////////////////////////////////////////////////////////////////////////////////
 private void SyncWrite(OPCTag tag)
 {
     try {
         Array serverErrors  = default(Array);
         Array serverHandles = new int[2];
         serverHandles.SetValue((int)tag.OPCItem.ServerHandle, 1);
         Array values = GetValue(tag);
         tag.OPCGroup.SyncWrite(1, ref serverHandles, ref values, out serverErrors);
         HandleServerError(serverErrors);
     } catch (Exception ex) {
         //Excpetion event
     }
 }
Exemple #8
0
        //private void comboBoxHotItemStyle_SelectedIndexChanged(object sender, EventArgs e)
        //{
        //Coordinator.ChangeHotItemStyle(this.ListView, (ComboBox)sender);
        //}

        //private void buttonRefresh_Click(object sender, EventArgs e)
        //{
        //this.treeListView.RefreshObjects(this.treeListView.SelectedObjects);
        //}

        //private void buttonCheck_Click(object sender, EventArgs e) {
        //this.treeListView.ToggleSelectedRowCheckBoxes();
        //}

        //private void buttonSaveState_Click(object sender, EventArgs e)
        //{
        //// SaveState() returns a byte array that holds the current state of the columns.
        //// For this demo, we just hold onto that value in an instance variable. For your
        //// application, you should persist it some more permanent fashion than this.
        //this.treeListViewViewState = this.treeListView.SaveState();
        //this.buttonRestoreState.Enabled = true;
        //}

        //private void buttonRestoreState_Click(object sender, EventArgs e)
        //{
        //this.treeListView.RestoreState(this.treeListViewViewState);
        //}

        //private void buttonColumns_Click(object sender, EventArgs e)
        //{
        //ColumnSelectionForm form = new ColumnSelectionForm();
        //form.OpenOn(this.treeListView);
        //}

        //private void buttonDisable_Click(object sender, EventArgs e)
        //{
        //bool isControlKeyDown = ((Control.ModifierKeys & Keys.Control) == Keys.Control);
        //if (isControlKeyDown)
        //this.ListView.EnableObjects(this.ListView.DisabledObjects);
        //else
        //this.ListView.DisableObjects(this.ListView.SelectedObjects);
        //}

        #endregion

        //private void comboBoxExpanders_SelectedIndexChanged(object sender, EventArgs e) {
        //TreeListView.TreeRenderer treeColumnRenderer = this.treeListView.TreeColumnRenderer;
        //ComboBox cb = (ComboBox)sender;
        //switch (cb.SelectedIndex)
        //{
        //case 0:
        //treeColumnRenderer.IsShowGlyphs = false;
        //break;
        //case 1:
        //treeColumnRenderer.IsShowGlyphs = true;
        //treeColumnRenderer.UseTriangles = false;
        //break;
        //case 2:
        //treeColumnRenderer.IsShowGlyphs = true;
        //treeColumnRenderer.UseTriangles = true;
        //break;
        //}

        //// Cause a redraw so that the changes to the renderer take effect
        //this.treeListView.Refresh();
        //}

        private void WriteValue_Click(object sender, EventArgs e)
        {
            OPCTag tag = this.ListView.SelectedObject as OPCTag;

            if (tag != null)
            {
                tag.Value = this.WriteValueInput.Text;
            }
            else if (lastTag != null)
            {
                lastTag.Value = this.WriteValueInput.Text;
            }
        }
        void OnTagChanged(object sender, TagEventArgs e)
        {
            //If we changed the tag then do not write the tag.
            if (sender != null)
            {
                if (sender.Equals(this))
                {
                    return;
                }
            }

            try {
                OPCTag tag = mTagsByName[e.Tag.Name];
                AsyncWrite(tag);
            } catch (Exception ex) {
                //Error event
            }
        }
Exemple #10
0
        private void addNode(string name, string parentName = "")
        {
            // 应该先判断一下,这个 Key 在 map 中存在不存在。不存在,就添加
            if (!keyTagMap.ContainsKey(name))
            {
                OPCTag tag;
                if (parentName != "")
                {
                    tag    = new OPCTag(parentName, name);
                    tag.id = seqId;
                    if (keyTagMap.ContainsKey(parentName))
                    {
                        OPCTag parent = keyTagMap[parentName];
                        tag.parent_id = parent.id;
                        tag.root_id   = parent.root_id;
                        tag.isLeaf    = true;
                        if (allTagNames.Contains(name))
                        {
                            tag.isRealTag = true;
                        }
                        parent.AddChild(tag);
                    }
                }
                else
                {
                    tag = new OPCTag(name);
                    tag.parent_fullname = "root";
                    tag.id        = seqId;
                    tag.parent_id = 0;
                    tag.root_id   = tag.id;
                    tag.isLeaf    = false;
                    tag.isRealTag = false;

                    rootTagMap.Add(name, tag);
                }

                seqId++;

                tag.TagValueUpdate += new TagEventHandler(OnTagValueUpdate);
                keyTagMap.Add(name, tag);

                //Console.WriteLine("Value added for key = \"" + name  + "\"");
            }
        }
        void OnGroupAsyncWriteComplete(int TransactionID, int NumItems, ref Array ClientHandles, ref Array Errors)
        {
            try {
                for (int i = 1; i <= ClientHandles.Length; i++)
                {
                    int    handle = (int)ClientHandles.GetValue(i);
                    OPCTag tag    = null;

                    if (mTagsByClientHandle.TryGetValue(handle, out tag))
                    {
                        tag.Quality = ((int)Errors.GetValue(i) > 0) ? OPCQuality.OPCQualityBad : OPCQuality.OPCQualityGood;
                    }
                    mAcyncWriteTags.Remove(TransactionID);

                    // 这里应该显示日志,或者其他的,可以展示错误的东西
                }
            } catch (Exception ex) {
            }
        }
Exemple #12
0
        private void ShowTagProperties(OPCTag tag)
        {
            //OPCTag tag = listView.SelectedObject as OPCTag;
            //OPCTag focusedTag = listView.FocusedObject as OPCTag;

            if (this.TagValueBox.InvokeRequired)
            {
                //instansiate a delegate with the method
                ShowTagPropertiesDelegate myDelegate = new ShowTagPropertiesDelegate(ShowTagPropertiesWorker);
                //Invoke delegate
                this.TagValueBox.Invoke(myDelegate, tag);
            }
            else
            {
                if (tag != null && tag.isRealTag)
                {
                    if (tag.Value == null)
                    {
                        this.TagValueBox.Text = "<null>";
                    }
                    else
                    {
                        this.TagValueBox.Text = tag.Value.ToString();
                    }
                    this.TagQualityBox.Text   = tag.QualityText;
                    this.TagTimestampBox.Text = tag.ItemTimeStampText;
                }
                else if (tag != null && !tag.isRealTag)
                {
                    this.TagValueBox.Text     = "";
                    this.TagQualityBox.Text   = "";
                    this.TagTimestampBox.Text = "";
                }

                this.WriteValueInput.Text = "";
            }
        }
Exemple #13
0
        public void HandleSelectionChanged(ObjectListView listView)
        {
            // Most ListViews in the demo handle lists of people, so we try to cast SelectedObject and FocusedObject to be Persons.

            OPCTag p   = listView.SelectedObject as OPCTag;
            string msg = p == null?listView.SelectedIndices.Count.ToString(CultureInfo.CurrentCulture) : String.Format("'{0}'", p.Name);

            OPCTag focused    = listView.FocusedObject as OPCTag;
            string focusedMsg = focused == null ? "" : String.Format(". Focused on '{0}'", focused.Name);

            this.ToolStripStatus1 =
                String.IsNullOrEmpty(this.prefixForNextSelectionMessage) ?
                String.Format("Selected {0} of {1} items" + focusedMsg, msg, listView.GetItemCount())
                    : String.Format("{2}. Selected {0} of {1} items" + focusedMsg, msg, listView.GetItemCount(), this.prefixForNextSelectionMessage);


            //this.ToolStripStatus1 = String.Format("{2}. Selected {0} of {1} items" + focusedMsg, msg, listView.GetItemCount(), this.prefixForNextSelectionMessage);



            this.prefixForNextSelectionMessage = null;

            Console.WriteLine("this is in public void HandleSelectionChanged(ObjectListView listView)");
        }
        void OnGroupDataChange(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps)
        {
            try {
                Console.WriteLine("in OPCClient, OnGroupDataChange, Thread id is: " + System.Threading.Thread.CurrentThread.ManagedThreadId);

                for (int i = 1; i <= NumItems; i++)
                {
                    int handle = (int)ClientHandles.GetValue(i);

                    if (mTagsByClientHandle.ContainsKey(handle))
                    {
                        OPCTag tag = mTagsByClientHandle[handle];


                        tag.SetValue(this, ItemValues.GetValue(i));
                        tag.ItemTimeStamp = (DateTime)TimeStamps.GetValue(i);
                        tag.Quality       = (OPCQuality)Qualities.GetValue(i);

                        tag.UpdateCount++;
                    }
                }
            } catch (Exception ex) {
            }
        }
Exemple #15
0
        //public OPCTag()
        //{
        //Children = new List<OPCTag>();
        //}

        public void AddChild(OPCTag t)
        {
            t.Parent = this;
            Children.Add(t);
        }
Exemple #16
0
 public TagEventArgs(OPCTag tag)
 {
     Tag = tag;
 }
        public bool AddTags(SortedDictionary <string, OPCTag> opcTags, string groupName = null, bool removeFormer = false)
        {
            Console.WriteLine("in OPCClient, AddTags 1");
            Console.WriteLine("in OPCClient, AddTags, Thread id is: " + System.Threading.Thread.CurrentThread.ManagedThreadId);
            if (opcTags.Count == 0)
            {
                return(false);
            }

            if (removeFormer)
            {
                RemoveAllItems();
            }

            if (groupName == null || groupName == "")
            {
                groupName = DefaultGroupName;
            }

            OPCGroup group = GetGroup(groupName);


            int[]    ClientHandles = new int[opcTags.Count];
            string[] tagNames      = new string[opcTags.Count];
            int      handle        = mTagsByName.Count + 1;

            int counter = 0;

            foreach (KeyValuePair <string, OPCTag> opcTag in opcTags)
            {
                if (mTagsByName.ContainsKey(opcTag.Value.Name))
                {
                    continue;
                }

                if ((opcTag.Value.Direction & TagDirection.Output) == TagDirection.Output)
                {
                    opcTag.Value.TagChanged += new TagEventHandler(OnTagChanged);
                }

                ClientHandles[counter] = handle;
                tagNames[counter]      = opcTag.Value.Name;

                handle++;
                counter++;
            }

            try {
                Array ar_itemNames = Array.CreateInstance(typeof(string), tagNames.Length + 1);
                ((Array)tagNames).CopyTo(ar_itemNames, 1);

                Array ar_itemClientHandles = Array.CreateInstance(typeof(int), tagNames.Length + 1);
                ((Array)ClientHandles).CopyTo(ar_itemClientHandles, 1);

                Array ar_Errors;

                group.OPCItems.AddItems(ar_itemClientHandles.Length - 1, ref ar_itemNames, ref ar_itemClientHandles, out itemServerHandles, out ar_Errors);
                foreach (object item in ItemServerHandles)
                {
                    // 这里 如果 item 是 0 的话,会直接 crash。为什么 ItemServerHandles 会有大量的 0 呢?
                    // 判断一下。或者用  nested try catch
                    if (0 != (int)item)
                    {
                        OPCItem opcItem = group.OPCItems.GetOPCItem((int)item);

                        if (opcItem != null)
                        {
                            //Save the opctag in a dictionary for quick lookup by name or by ClientHandle

                            // 得到的 opcItem 的 ItemID  是什么?是传进去的Name吗? 是!
                            OPCTag opcTag = opcTags[opcItem.ItemID];

                            opcTag.OPCItem  = opcItem;
                            opcTag.OPCGroup = group;
                            mGroupTagsMap[group].Add(opcTag);

                            mTagsByName.Add(opcTag.Name, opcTag);
                            mTagsByClientHandle.Add(opcItem.ClientHandle, opcTag);
                        }
                    }
                }

                Console.WriteLine("in OPCClient, AddTags 2");
                return(true);
            }
            catch (Exception err)
            {
                Console.WriteLine("in OPCClient, AddTags 3");
                //没有任何权限的项,都是OPC服务器保留的系统项,此处可不做处理。
                //MessageBox.Show("此项为系统保留项:"+err.Message,"提示信息");

                // 这里应该写一个 错误处理 的方法,把每一个error code 对应的 错误信息,反馈给用户
            }
            Console.WriteLine("in OPCClient, AddTags 4");
            return(false);
        }
Exemple #18
0
        /// <summary>
        /// 【按钮】连接OPC服务器
        /// </summary>
        private void btnConnLocalServer_Click(object sender, EventArgs e)
        {
            try
            {
                if (this.btnConnServer.Text == "连接")
                {
                    if (String.IsNullOrEmpty(this.txtRemoteServerIP.Text))
                    {
                        this.txtRemoteServerIP.Text = "127.0.0.1";
                    }

                    opcClient.Initialize(cmbServerName.Text, this.txtRemoteServerIP.Text);

                    bool result = opcClient.Connect();
                    if (!result)
                    {
                        // 在这里处理连接错误
                        MessageBox.Show("不能连接到OPC Server:" + cmbServerName.Text, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                        return;
                    }

                    this.btnConnServer.Enabled = false;

                    // 启动Timer,去定时坚持是否连接正常
                    aTimer          = new System.Timers.Timer();
                    aTimer.Interval = 1000;

                    // Alternate method: create a Timer with an interval argument to the constructor.
                    //aTimer = new System.Timers.Timer(2000);

                    // Create a timer with a two second interval.
                    //aTimer = new System.Timers.Timer(5000);

                    // Hook up the Elapsed event for the timer.
                    aTimer.Elapsed += OnTimedEvent;

                    // Have the timer fire repeated events (true is the default)
                    aTimer.AutoReset = false;

                    // Start the timer
                    aTimer.Enabled = true;

                    TextOverlay textOverlay = this.treeListView.OverlayText as TextOverlay;
                    textOverlay.Alignment   = System.Drawing.ContentAlignment.MiddleCenter;
                    textOverlay.TextColor   = Color.Firebrick;
                    textOverlay.BackColor   = Color.AntiqueWhite;
                    textOverlay.BorderColor = Color.DarkRed;
                    textOverlay.BorderWidth = 4.0f;
                    textOverlay.Font        = new Font("宋体", 36);
                    //textOverlay.Rotation = -5;
                    textOverlay.Text = "正在加载Tag...";
                    this.treeListView.RefreshOverlays();


                    if (!treeInitialized)
                    {
                        InitializeTree();
                    }


                    // 添加Tags,AddTags

                    //opcClient.AddTags1(keyTagMap);
                    AddTagsDelegate worker      = AddTagsWorker;
                    IAsyncResult    asyncResult = worker.BeginInvoke(AddTagsCompleted, worker);
                    //this.treeListView.BeginInvoke(new AddTagsDelegate(AddTagsWorker1));

                    this.btnConnServer.Text = "断开";
                }
                else
                {
                    aTimer.Enabled = false;
                    aTimer         = null;

                    Thread.Sleep(1000);
                    if (opcClient.Connected == true)
                    {
                        opcClient.Disconnect();
                    }

                    // 这样行不行?
                    // 显然不够,因为还得加 Column 类的
                    //this.treeListView = new BrightIdeasSoftware.TreeListView();
                    //this.treeListView.Clear();
                    this.treeListView.ClearObjects();
                    Thread.Sleep(1000);
                    treeInitialized = false;
                    keyTagMap.Clear();
                    rootTagMap.Clear();
                    allTagNames.Clear();
                    seqId = 1;

                    lastTag = null;
                    this.btnConnServer.Text     = "连接";
                    this.ExpandAllNodes.Enabled = false;
                    this.ExpandAllNodes.Text    = "全部展开";
                }
            }

            catch (Exception err)
            {
                MessageBox.Show("初始化出错:" + err.Message, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }
        ///////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Adds a tag to the opc server
        /// </summary>
        /// <param name="tag"></param>
        ///////////////////////////////////////////////////////////////////////////////////////////
        public bool AddTag(OPCTag opcTag, bool readProperty = true, string groupName = null)
        {
            //OPCTag opcTag = new OPCTag(tag);
            if (mTagsByName.ContainsKey(opcTag.Name))
            {
                return(false);
            }


            // 这里要加 事件处理 ??
            // 这里是处理 input 点、output点的。
            // 如果设置成output点,当外部修改了这个点的value的时候,就通过事件处理,自动写到server端
            // 先注掉吧。

            // 这是是处理 往 server 端写数据的。如果把这个注掉,就没有主动写数据的地方了。
            // 如果在其他地方,要写一个tag的值到server,只需要 tag.Value = xxxx,这样就可以了。
            // 因为绑定了事件,最终会绕到这里这个OPCClient 的  OnTagChanged 方法
            // 这个设计真绕
            if ((opcTag.Direction & TagDirection.Output) == TagDirection.Output)
            {
                opcTag.TagChanged += new TagEventHandler(OnTagChanged);
            }


            //Add an OPC Group if the update time does not alread exist
            //OPCGroup group = GetGroup(opcTag.UpdateTime);
            // 这里用 Updatetime 来作为 group 的 Name,好,还是不好?好在:同一个 Updatetime 的在同一个group,更新的话,一起更新。
            // 不好在:不够明确。
            // 而且,这么写,添加 group 成了 添加item 驱动的了。 主动控制性不强
            // 我希望的是,主动性更强一点。那设置一个 defaultGroupName


            if (groupName == null || groupName == "")
            {
                groupName = DefaultGroupName;
            }

            OPCGroup group = GetGroup(groupName);


            // 这里应该加一个 try  catch,以防 这个Tag 在 server 端不存在
            try {
                // 这里的AddItem 的第二个参数,应该是什么?
                // 一个数字?这个数字传到server,server数据有变化的时候再传回来,作为一个handle?
                OPCItem item = group.OPCItems.AddItem(opcTag.Name, mTagsByName.Count + 1);
                opcTag.OPCItem  = item;
                opcTag.OPCGroup = group;

                if (item != null)
                {
                    //Save the opctag in a dictionary for quick lookup by name or by ClientHandle
                    mTagsByName.Add(opcTag.Name, opcTag);
                    mTagsByClientHandle.Add(item.ClientHandle, opcTag);

                    opcTag.ClientHandle = item.ClientHandle;
                    opcTag.ServerHandle = item.ServerHandle;

                    mGroupTagsMap[group].Add(opcTag);

                    //ItemServerHandles.Add(opcTag.OPCItem.ServerHandle);
                    //ItemServerHandles.SetValue
                    //ItemServerHandles.SetValue(opcTag.OPCItem.ServerHandle, ItemServerHandles.Length);
                    // 控制是不是添加Tag之后,就读一下Value呢
                    if (readProperty)
                    {
                        if (opcTag.isFirstUpdate)
                        {
                            ReadPropertyValues(ref opcTag);
                            opcTag.isFirstUpdate = false;
                        }
                    }
                    return(true);
                }
                else
                {
                    return(false);
                }
            }

            catch (Exception err)
            {
                //没有任何权限的项,都是OPC服务器保留的系统项,此处可不做处理。
                //MessageBox.Show("此项为系统保留项:"+err.Message,"提示信息");
                return(false);

                // 这里应该写一个 错误处理 的方法,把每一个error code 对应的 错误信息,反馈给用户
            }
        }