void watchPointValues(string jsonStr)
        {
            foreach (var client in _clients)
            {
                client.Released = true;
                client.NetClient.Close();
            }
            _clients.Clear();

            Task.Run(() =>
            {
                try
                {
                    var pointArr = (Newtonsoft.Json.Linq.JArray)Newtonsoft.Json.JsonConvert.DeserializeObject(jsonStr);
                    var groups   = (from m in pointArr
                                    group m by m.Value <int>("deviceId") into g
                                    select new
                    {
                        deviceId = g.Key,
                        points = g.ToArray(),
                    }).ToArray();
                    foreach (var group in groups)
                    {
                        _AllPoints.AddRange(group.points);

                        var device = Helper.Remote.InvokeSync <SunRizServer.Device>("GetDeviceAndDriver", group.deviceId);

                        var client                = new MyDriverClient(device.Driver.Address, device.Driver.Port.Value);
                        client.WatchingPoints     = group.points.Select(m => m.Value <string>("addr")).ToList();
                        client.WatchingPointNames = group.points.Select(m => m.Value <string>("name")).ToList();
                        client.Device             = device;
                        _clients.Add(client);
                        watchDevice(client);
                    }
                }
                catch (Exception ex)
                {
                    MainWindow.Instance.Dispatcher.Invoke(() =>
                    {
                        MessageBox.Show(MainWindow.Instance, ex.Message);
                    });
                }
            });
        }
        void watchDevice(MyDriverClient client)
        {
            client.NetClient = client.AddPointToWatch(client.Device.Address, client.WatchingPoints.ToArray(), (point, value) =>
            {
                try
                {
                    var pointObj = _AllPoints.FirstOrDefault(m => m.Value <string>("addr") == point);
                    if (pointObj != null)
                    {
                        //转换数值
                        value = SunRizDriver.Helper.Transform(pointObj, value);
                    }
                    Debug.WriteLine($"addr:{point} value:{value}");
                    _gecko.Invoke(new ThreadStart(() =>
                    {
                        using (var jsContext = new AutoJSContext(_gecko.Window))
                        {
                            jsContext.EvaluateScript($"onReceiveValueFromServer({ (new { addr = point, value = value }).ToJsonString()})");
                        }
                    }));
                }
                catch (Exception ex)
                {
                }
            }, (err) =>
            {
                if (client.Released)
                {
                    return;
                }

                Task.Run(() =>
                {
                    Thread.Sleep(2000);
                    watchDevice(client);
                });
            });
        }
        void writePointValue(string arg)
        {
            try
            {
                string[] pointValue = arg.ToJsonObject <string[]>();
                string   pointName  = pointValue[0]; // /p/a/01
                string   addr       = pointValue[1]; // 点真实路径
                if (string.IsNullOrEmpty(addr))
                {
                    //查询点真实地址
                    try
                    {
                        if (_PointAddress.ContainsKey(pointName) == false)
                        {
                            _PointAddress[pointName] = Helper.Remote.InvokeSync <PointAddrInfo>("GetPointAddr", pointName);
                        }

                        addr = _PointAddress[pointName].addr;
                    }
                    catch
                    {
                        throw new Exception($"无法获取点“{pointName}”真实地址");
                    }
                }
                string value  = pointValue[2];
                var    client = _clients.FirstOrDefault(m => m.WatchingPointNames.Contains(pointName));
                if (client == null)
                {
                    //构造MyDriverClient
                    var device = Helper.Remote.InvokeSync <SunRizServer.Device>("GetDeviceAndDriver", _PointAddress[pointName].deviceId);
                    client        = new MyDriverClient(device.Driver.Address, device.Driver.Port.Value);
                    client.Device = device;
                    client.WatchingPoints.Add(addr);
                    client.WatchingPointNames.Add(pointName);
                    _clients.Add(client);
                }

                if (client != null)
                {
                    var pointObj = _AllPoints.FirstOrDefault(m => m.Value <string>("addr") == addr);
                    if (pointObj != null)
                    {
                        //转换数值
                        value = SunRizDriver.Helper.GetRealValue(pointObj, value).ToString();
                    }

                    if (client.WriteValue(client.Device.Address, addr, value) == false)
                    {
                        System.Windows.Forms.MessageBox.Show(_gecko, "写入值失败!");
                    }
                    else
                    {
                        using (var jsContext = new AutoJSContext(_gecko.Window))
                        {
                            jsContext.EvaluateScript($"onReceiveValueFromServer({ (new { addr = addr, value = value }).ToJsonString()})");
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(_gecko, ex.Message);
            }
        }