开发者中心 开发者中心
  • 简体中文
  • English
视频教程
敢为云网站
  • 6.0版本
  • 6.1 版本
视频教程
敢为云网站
  • 平台概述
  • 平台功能
  • 平台安装
  • 开发者指南
    • 协议插件开发
      • 原理解析
        • 协议插件介绍
        • 运行示意图
        • 重载方法介绍
          • init() 初始化设备参数
          • OnLoaded() 设备连接初始化或实现设备重连
          • GetData() 方法
          • GetYC() 遥测方法
          • GetYX() 遥信方法
          • SetParm() 设置方法
          • GetEvent() 获取设备事件方法
        • CEquipBase类
      • 开发第一个插件
      • 进阶
      • Java协议插件开发实践
      • Python协议插件开发实践
      • GWDataCenter常用接口API
    • 扩展插件开发
    • 报警插件开发
    • 应用插件开发
    • Web可视化开发
    • 3D可视化开发
    • 桌面可视化开发
    • 小程序开发
    • 应用模块接口
  • 项目实战
  • 附录

原理解析

# 协议插件介绍

详情

协议插件是IoTCenter用以实现数据采集的业务单元,必须遵循一定的开发规范,该规范主要包括以下内容:

  1. 协议插件依赖于敢为网关数据采集服务(GWDataCenter),并作为其插件运行,GWDataCenter作为宿主程序,负责管理插件的加载、生命周期,并负责对实时数据进行存储,本身提供了一组读写实时数据的逻辑。

  2. 协议插件的.NET框架应保持与宿主程序一致,过低或过高的版本均可能导致插件无法运行,宿主将会主动停止运行。

  3. 协议插件的入口类名为CEquip,并必须重载init、GetData、GetYC、GetYX、SetParm五个模板方法。

  4. 命名:为了更好的标识协议插件的产权信息,建议使用公司代号作为统一命名,敢为软件对协议插件的命名规则为:

    • 适用于公司范围的插件,基本命名规则为GW{行业协议}.{.NET 版本}.dll。

    • 如果该协议插件为基于 .NET(包括且不限定于 .NET Core/.NET Strandard/.NET 6及后续版本),其命名格式为GW{行业协议}.STD.dll。

    • 例如:GWJieLink.STD.dll,表示基于捷顺RestFulApi标准协议文档开发的协议插件。

  5. 程序包定义:创建好的协议插件,应在其项目包属性中,定义公司版权信息和产品包信息,且每个版本发布时,应升级相应的版本号。

image-20210813133801465

# 运行示意图

运行示意图

下图为协议插件的运行示意图

1.1.1-8

  1. 插件运行前准备:该工作在数据采集平台调用插件前提前准备线程资源。

  2. 协议初始化(init):用于初始化线程资源,建立与设备侧的连接状态。

  3. 协议初始化(OnLoaded):用于初始化初始化设备,实现设备连接通信。

  4. 获取数据(GetData):按照配置的通讯时间参数,定时从设备侧获取数据。

  5. 解析遥测数据(GetYC):根据设备关联的遥测点,对采集的数据进行解析,并调用SetYCData方法,将数据写入到物联网平台的共享内存状态中。

  6. 解析遥信(GetYX):对设备遥信状态进行解析,并调用SetYXData方法,并将实时设备遥信状态写入到宿主实时内存中。

  7. 解析遥信(GetEvent):从当前设备连接中获取事件列表,事件发布如门禁设备的一些通行记录数据。。

  8. 设置命令(SetParm):外部应用可通过调用设置,将状态回调到协议插件中,实现某些特定的功能。

# 重载方法介绍

# init() 初始化设备参数

init() 设备初始化方法

init() 方法用于初始化设备,在数据库中提前设备信息、开始对通讯设备建立连接等,是动态库最先被执行的方法。

定义:

public override bool init(EquipItem item)

使用示例:



    /// <summary>
    /// 初始化设备相关参数
    /// 在界面添加完成后,会进入到该方法进行初始化
    /// 之后再界面修改连接参数后,会再一次进入该方法。
    /// </summary>
    /// <param name="item">equip表对象属性</param>
    /// <returns></returns>
    public override bool init(EquipItem item)
    {
        /*
        item.Equip_addr 设备地址
        解释:通常存放设备的唯一标识或者设备的连接地址。这里需要根据具体的协议来区分,如果一对一的直连设备

        item.communication_param 设备连接参数
        解释:通常存放设备的连接信息,具体由当前协议插件来约定,在配置文档中写明即可。

        item.Local_addr 通讯端口(也叫通讯线程),任意字符,不宜过长。
        解释:在Equip表,你可能会发现不少设备的Local_addr字段可能都是空的,也可能都是一个具体的字符串。
        我们按照该字段的值进行Group By归类后,就得到了同一个值的设备数量有多少个,这个就代表一个线程管控了多少个设备。

        item.communication_time_param
        解释:在设备线程组里面,一个设备多久通信一次,即多久采集一次数据,单位毫秒。 
        如果communication_time_param职能比较多,也可以将多个参数的拼接,此时需要自行处理拆分后再转换。
        配置举例:假设1个线程管控10个设备,要求每个设备每秒采集一次数据,那么这个字段的值应不大于100毫秒。其他场景同理计算即可。

        item.Reserve2 设备自定义参数
        解释:一般一些连接参数较多,需要规范化存储时,可以将属性放到自定义参数中,直观一些。当然也可以使用其他字段去拼接起来,但不建议这样做。
        在6.1版本中,该字段在数据库中存储的值为一个JSON格式的数据。
        在低版本中可以按照JSON格式来存储这个数据。
         */

        //获取设备连接通讯的间隔时间。
        _ = int.TryParse(item.communication_time_param, out _sleepInterval);

        /*
        在构造连接参数数,根据实际情况,以下展示一个连接参数模型的赋值。
        如果连接参数简单,也可以使用自定义连接参数,直接使用communication_param更好,减少配置项,这里需要开发人员自己确定好。
         */
        if (!string.IsNullOrWhiteSpace(item.Reserve2))
        {
            var dictParams = JsonConvert.DeserializeObject<Dictionary<string, string>>(item.Reserve2);
            _connectionConfig = new ConnectionConfig
            {
                ServerUrl = item.Equip_addr,
                UserName = dictParams.TryGetValue("UserName", out var userName) ? userName : string.Empty,
                Password = dictParams.TryGetValue("Password", out var password) ? password : string.Empty,
                CertificatePath = dictParams.TryGetValue("CertificatePath", out var certPath) ? certPath : string.Empty,
                CertificatePwd = dictParams.TryGetValue("CertificatePwd", out var certPwd) ? certPwd : string.Empty,
            };

            //我们可以定义多个事件名称的级别,命名方式如DefaultEventMessageLevel,如果未取到,默认值给0,但最好要区分好,因为使用0的事件级别很多场景都使用。
            _ = int.TryParse(dictParams.TryGetValue("DefaultEventMessageLevel", out var defaultEventMessageLevelStr) ? defaultEventMessageLevelStr : "0", out _defaultEventMessageLevel);
        }
        return base.init(item);
    }

工作原理:

CEquip类里的方法会被不断循环执行,但init方法只在设备第一次运行以及设备或测点(包括设置点)配置进行了更改后执行。同时在基类中提供了Onloaded虚方法在第一次加载之后或者系统重置之后自动执行。比如可以将写日志的操作写在Onloaded方法中。base.init(item)是基类方法用于初始化。基类中默认支持Modbus等协议。

解析:

  1. Init()方法在框架中会被循环调用,返回值类型为 bool类型,当其返回值为 Ture 时代表设备初始化成功,并开始执行其他成员方法,为 false 时则代表设备初始化失败,初始化失败时则阻塞此类中其他方法的运行。

  2. Equipitem 参数为 GWDataCenter.DataCenter 命名空间下的 Equipitem 类,并继承IComparable 接口,其包含了设备的所有属性,并聚合了数据库的操作类 Database 和用于与设备通讯的 SerialPort类。

  3. DataCenter.WriteLogFile() 是 GWDataCenter.DataCenter 命名空间下的 DataCenter 类中的一个记录错误日志的方法,其日志记录在 \IoTCenter\log\XLog.txt 文件中。

  4. item.communication_time_param是 Equipitem 类中的一个字段,它映射自数据库 Equip 表的 communication_time_param 字段,代表刷新间隔。

# OnLoaded() 设备连接初始化或实现设备重连

OnLoaded() 方法

OnLoaded()方法是一个在init方法执行成功后会被调用的函数。它不需要任何参数,可以在init()方法中获取所需的参数,然后在OnLoaded方法中进行业务操作。

定义:

public override bool OnLoaded()

使用示例:

    /// <summary>
    /// 设备连接初始化
    /// 对于设备的连接地址,连接账号密码发生更改后,可以进行重连。
    /// </summary>
    /// <returns></returns>
    public override bool OnLoaded()
    {
        //TODO 这里可以写于设备连接的具体代码了。根据_connectionConfig连接参数,去创建自己的连接对象。
        ConnClientManager.Instance.CreateClientSession(_connectionConfig);
        //返回默认值
        return base.OnLoaded();
    }

# GetData() 方法

GetData() 方法

初始化设备后,开始执行此方法,根据 CEquip 的工作任务,可以分为 ycp/yxp 表读取数据、setparm 表设置数据两大部分。一般来说,setparm 控制某些动作,这样会影响数据的准确性,因此 GetData() 方法在此控制程序的工作。

定义:

public override CommunicationState GetData(CEquipBase pEquip)

使用示例:

    /// <summary>
    /// 获取设备状态及实时数据
    /// 注意要控制好该方法不要出异常,否则会出现设备一直处于初始化状态中
    /// </summary>
    /// <param name="pEquip">设备基类对象</param>
    /// <returns></returns>
    public override CommunicationState GetData(CEquipBase pEquip)
    {
        //通过等待间隔时间,来达到多久取一次的。
        if (_sleepInterval > 0)
            base.Sleep(_sleepInterval);

        //当然开发者也可以在此次在增加相关业务逻辑。

        //获取当前连接地址的状态
        var equipStatus = ConnClientManager.Instance.GetClientSessionStatus(_connectionConfig.ServerUrl);

        //如果连接状态正常,设置为在线
        if (equipStatus)
        {
            //只有在线是才采集数据
            _currentValue = ConnClientManager.Instance.GetCurrentValues(_connectionConfig.ServerUrl, pEquip.m_equip_no);
            return CommunicationState.ok;
        }
        else
        {
            //否则设置离线
            return CommunicationState.fail;
        }

        return base.GetData(pEquip);
    }

解析:

  1. CommunicationState 是一个状态枚举,代表当前数据解析成功,失败等状态信息,如果验证成功后则返回基类的 GetData() 方法继续执行,失败则阻塞 GetYC()、 GetYX(),SetParm() 方法的运行。

  2. GetData 方法在运行时包含 CEquipBase 类对象,它包含了所有测点信息,可以在其中做数据校验,因为在具体设备通讯中,需要对返回的数据进行验证。一般进行数据验证会从以下几个方面开始,如:设备地址(如果协议有定义的话)、返回数据长度、完整性校验(根据协议可能有CRC、和校验等)。如 Modbus 协议中,最后两个字节进行 CRC 校验,同时还需要对设备地址进行全面的校验,否则收到错误的数据包将导致实际设备异常响应。

  3. Sleep() 方法是 CEquipBase 基类中提供用于休眠 GetData() 方法。

注意

# GetYC() 遥测方法

GetYC() 遥测方法

定义:

public override bool GetYC(YcpTableRow r)

使用示例:

    /// <summary>
    /// 遥测点设置
    /// </summary>
    /// <param name="r">ycp表对象属性(不是全部)</param>
    /// <returns></returns>
    public override bool GetYC(YcpTableRow r)
    {
        /*
        注意:在此处最好不用打印日志,因为这里会产生大量的日志,如果需要调试某个点位时,可以在自定义参数里面加参数,针对固定的遥测进行日志调试。
        r.main_instruction 操作命令,如EquipCurrentInfo
        r.minor_instruction 操作参数,如Temperature,Humidness等
        r.Reserve2 自定义参数,以json结构存储,同设备的自定义参数一样。

        在给遥测赋值时提供了诸多方法,支持单个类型,多元组类型,可以根据实际需要使用。
        SetYCData(YcpTableRow r, object o);
        SetYCDataNoRead(IQueryable<YcpTableRow> Rows);
        SetYcpTableRowData(YcpTableRow r, float o);
        SetYcpTableRowData(YcpTableRow r, (double, double, double, double, double, double) o);
        SetYcpTableRowData(YcpTableRow r, string o);
        SetYcpTableRowData(YcpTableRow r, int o);
        SetYcpTableRowData(YcpTableRow r, double o);
        SetYcpTableRowData(YcpTableRow r, (double, double, double, double, double, double, double) o);
        SetYcpTableRowData(YcpTableRow r, (double, double) o);
        SetYcpTableRowData(YcpTableRow r, (DateTime, double) o);
        SetYcpTableRowData(YcpTableRow r, (double, double, double, double) o);
        SetYcpTableRowData(YcpTableRow r, (double, double, double, double, double) o);
        SetYcpTableRowData(YcpTableRow r, (double, double, double) o);
            */

        /* 实时数据示例代码,可以根据自己的业务进行处理*/
        if (_currentValue == null) return true;
        try
        {
            //此处的Key值需要根据实际情况去处理。如果构造实时数据缓存字典是需要由开发去定义。
            //总的来说,按照设备+遥测遥信的方式构造缓存数据是比较合理的。
            string key = r.equip_no + "_" + r.main_instruction;
            if (_currentValue.ContainsKey(key))
            {
                var objValue = _currentValue[key];
                if (objValue == null) SetYCData(r, "");//此处不可以设置为null。
                else SetYCData(r, objValue);
            }
            else
            {
                SetYCData(r, "***");
            }
        }
        catch (Exception ex)
        {
            SetYCData(r, "测点赋值出现异常,请查看日志");
            DataCenter.Write2Log($"记录报错日志:{ex}", LogLevel.Error);
        }


        //此处默认都返回true,否则设备会处于离线。
        return true;
    }

解析:

  1. GetYC()方法根据当前设备所拥有的遥测点数 N 条执行 N 次,参数 YcpTableRow 表示映射自数据库 ycp 遥测表的某一个实体即数据行。

  2. bool 表示为当前测点设置值是否成功,不成功则设备故障。

  3. r.main_instruction 表示获取数据库中的 main_instruction 列的值,对应界面中的操作命令可以根据不同测点配置的参数,判断该测点需要何种数据。

  4. SetYCData() 上传遥测值给上层应用,该方法有多个重载版本支持string、int、float、double、元组等多种数据类型的数据,其中元组类型支持指定时间插入double值以及double类型的元组(元组的中double类型元素的数量支持2-7个)。其方法原型为:

注意:

  1. 返回值

动态库开发中对于获取失败的测点应当返回false,不可将值设置为类似“无数据”。

public override bool GetYC(YcpTableRow r)
{
    //获取数据失败
    return false;
}

错误的写法

public override bool GetYC(YcpTableRow r)
{
    //获取数据失败
    SetYCData(r, "无数据");
    return true;
}
  1. 数据类型

动态库开发中,获取到设备的值需要转换为对应数据类型,一般情况下,遥测值为double类型,遥信值为Bool类型;

正确的写法

public override bool GetYC(YcpTableRow r)
{
    //获取到字符串值
    var value = "67.8";
    SetYCData(r, double.Parse(value));
    return true;
}

错误的写法

public override bool GetYC(YcpTableRow r)
{
    //获取到字符串值
    var value = "67.8";
    SetYCData(r, value);
    return true;
}

# GetYX() 遥信方法

GetYX() 遥信方法

定义:

public override bool GetYX(YxpTableRow r)

使用示例:

    /// <summary>
    /// 遥信点设置
    /// </summary>
    /// <param name="r">yxp表对象属性(不是全部)</param>
    /// <returns></returns>
    public override bool GetYX(YxpTableRow r)
    {
        /*
        注意:在此处最好不用打印日志,因为这里会产生大量的日志,如果需要调试某个点位时,可以在自定义参数里面加参数,针对固定的遥测进行日志调试。
        r.main_instruction 操作命令,如EquipCurrentInfo
        r.minor_instruction 操作参数,如Temperature,Humidness等
        r.Reserve2 自定义参数,以json结构存储,同设备的自定义参数一样。

        在给遥测赋值时提供了诸多方法,支持bool、string类型,正常使用bool就够了,特殊情况可自行处理。
        SetYXData(YxpTableRow r, object o);
        SetYxpTableRowData(YxpTableRow r, string o);
        SetYxpTableRowData(YxpTableRow r, bool o);
         */

        /* 实时数据示例代码,可以根据自己的业务进行处理*/
        if (_currentValue == null) return false;
        try
        {
            string key = r.equip_no + "_" + r.main_instruction;
            if (_currentValue.ContainsKey(key))
            {
                var nodeIdObj = _currentValue[key];
                if (nodeIdObj == null) SetYXData(r, "***");
                else SetYXData(r, nodeIdObj);
            }
            else
            {
                SetYXData(r, "***");
            }
        }
        catch (Exception ex)
        {
            SetYXData(r, "遥信赋值出现异常,请查看日志");
            DataCenter.Write2Log($"记录报错日志:{ex}", LogLevel.Error);
        }
        return true;
    }

解析:

  1. YxpTableRow 映射了 yxp 表的数据行,bool 表示为当前测点设置状态是否成功,不成功则设备状态显示为故障(灰色灯)。

  2. GetYX() 方法根据当前设备所拥有的遥信点数 N 条执行 N 次。

  3. SetYXData() 表示为当前的测点设置状态,这个状态是一个 bool 类型,返回True 则正常(绿灯),False 则警报(红灯)。该方法有两个重载版本分别支持bool和string类型的参数。

# SetParm() 设置方法

SetParm() 设置方法

定义:

public override bool SetParm(string MainInstruct, string MinorInstruct, string Value)

使用示例:

    /// <summary>
    /// 设备命令下发
    /// </summary>
    /// <param name="mainInstruct">操作命令</param>
    /// <param name="minorInstruct">操作参数</param>
    /// <param name="value">传入的值</param>
    /// <returns></returns>
    public override bool SetParm(string mainInstruct, string minorInstruct, string value)
    {
        /*
        注意:建议在此处打印日志,便于记录由平台执行命令的情况,用于追溯命令下发情况。
        mainInstruct 操作命令,如:Control
        minorInstruct 操作参数,如:SetTemperature,SetHumidness
        value 命令下发的参数值,如:22
        */

        //获取设备实际执行的结果
        dynamic controlResponse = ConnClientManager.Instance.WriteValueAsync(_connectionConfig.ServerUrl, mainInstruct, value);

        //将执行结果对象转换成json字符串
        var csResponse = JsonConvert.SerializeObject(controlResponse);

        //给当前设置点赋值响应内容,用于北向转发时告知设备实际执行结果
        this.equipitem.curSetItem.csResponse = csResponse;

        //记录执行传参及响应结果到日志中,便于追溯。
        string logMsg = string.Format("命令下发参数,设备号:{0},mainInstruct:{1},minorInstruct:{2},value:{3},下发执行结果:{4}",
            this.equipitem.iEquipno, mainInstruct, minorInstruct, value, csResponse);
        DataCenter.Write2Log(logMsg, LogLevel.Warn);

        //根据设备执行状态,返回状态,对于发布订阅模式可直接返回true,在相关地方做好日志记录即可。
        if (controlResponse.Code == 200) return true;
        else return false;
    }

解析:

  1. SetParm()方法用以从外部向协议插件内部进行参数设置,也用于向设备发送命令。

  2. 提供映射 Setparm 表的三个字段 设置命令、设置参数、设置值 用于提供命令参数和区分发送的命令。

  3. 返回值 True 表示当前命令设置成功,False 表示设置失败。

备注:

如果插件运行失败,可在宿主程序GWHost1的控制台输出中看到错误信息,此时根据该输出堆栈信息中针对性采取解决措施。

image-20210813141726700

# GetEvent() 获取设备事件方法

GetEvent() 获取设备事件

定义:

public override bool GetEvent()

使用示例:


/// <summary>
    /// 事件发布
    /// 如门禁设备的一些通行记录数据。
    /// 如果对事件记录实时性有非常高的要求,可以接收到事件后直接转。
    /// </summary>
    /// <returns></returns>
    public override bool GetEvent()
    {
        //从当前设备连接中获取事件列表
        _currentEvents = ConnClientManager.Instance.GetCurrentEvents(_connectionConfig.ServerUrl, this.m_equip_no);
        if (_currentEvents == null) return true;

        //假设_currentEvents对象每次都是新的数据,不存在旧数据,需开发者自行处理好.
        foreach (var eventItem in _currentEvents)
        {
            //EquipEvent中的事件级别根据当前事件名称定义好的级别。便于北向上报数据时的甄别。
            var evt = new EquipEvent(JsonConvert.SerializeObject(eventItem), "可以自定义的消息格式", (MessageLevel)_defaultEventMessageLevel, DateTime.Now);
            EquipEventList.Add(evt);
        }
        _currentEvents = null; //循环完成后,将事件记录置空,避免下次重复产生相同的事件.
        return true;
    }

解析:

  1. 在设备dll框架中,原生就是支持事件获取的。重载基类的GetEvent(),就像重载GetYC和GetYX一样。

  2. 获取到的事件也会出现在实时快照中。如无特殊情况,事件内容和事件联动内容最好保持一致,如果事件联动内容为空,则会自动把事件内容代入。

需要用事件做联动设置可参考以下配置

获取到的事件也会出现在实时快照中。如无特殊情况,事件内容和事件联动内容最好保持一致,如果事件联动内容为空,则会自动把事件内容代入。

然后在联动配置中进行设置,举例如图:

注意:联动类型要填evt,联动的时候会把事件传入到被设置的设备的strLinkageEvent变量,即200号设备的EquipItem对象的strLinkageEvent等于60000号设备的设备号+"#"+事件内容。比如60000号设备的事件联动内容是“王鹏进入公司大门”,则strLinkageEvent=“60000#王鹏进入公司大门”,之所以传入设备号,是便于其它程序根据设备号做不同的内容解析。 事件联动也支持正则表达式的判定,比如某个事件必须包含“张三”才触发联动,则对应的正则表达式为.张三.,保存在iycyx_type字段中,用{}括起来,也就是evt{.张三.}。

正则表达式后面如果用<>包含内容,则<>里面的内容会作为被设置动作的Value值传入,比如正则表达式为.张三.<欢迎张三来访>,那么事件中包含张三的时候,“欢迎张三来访”就会作为设置项的value值传入,这对人员进入联动信息发布的场景就可以直接设置。

更复杂的联动解析需要在程序中去完成,那就根据被联动设备dll中对EquipItem对象的strLinkageEvent值去做处理。要注意的是,项目中的个性化联动不要写在通用的设备dll中,另外写一个中间设备dll去做桥接。

下载示例代码

# CEquipBase类

using GWDataCenter.Database;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading;

namespace GWDataCenter
{
    /// <summary>
    /// 设备动态库的基类,派生于IEquip
    /// </summary>
    public class CEquipBase : IEquip
	{
        int iSta_no, iEquip_no, iRetrytime;
        bool b = true;
        EquipItem myequipitem;
        public List<YcpTableRow> ycprows;
        public List<YxpTableRow> yxprows;
        public EquipTableRow equiprow;
        public string EquipNm;

        public SerialPort serialport;

        Dictionary<int, object> ycresults;
        Dictionary<int, object> yxresults;
        List<EquipEvent> equipEventlist;

        object runSetParmFlag = (object)false;
        object resetFlag = (object)false;

        public Dictionary<int, bool> ycpdataflag = new Dictionary<int, bool>();
        public Dictionary<int, bool> yxpdataflag = new Dictionary<int, bool>();

        public string setparmexecutor = null;

        bool bSetCacheData = false;//设备是否缓存标记,缓存意味着实时数据会记录到数据库,作为重启系统的初始数据
        bool bFirstGetData = true;//第一次调用GetData
        public bool RunSetParmFlag
        {
            get
            {
                lock(runSetParmFlag)
                {
                    return (bool)runSetParmFlag;
                }
            }
            set
            {
                lock(runSetParmFlag)
                {
                    runSetParmFlag = (object)value;
                }
            }
        }

        public bool ResetFlag
        {
            get
            {
                lock (resetFlag)
                {
                    return (bool)resetFlag;
                }
            }
            set
            {
                lock (resetFlag)
                {
                    resetFlag = (object)value;
                }
            }
        }

        public EquipItem equipitem
        {
            get
            {
                return myequipitem;
            }
            set
            {
                myequipitem = value;
                serialport = myequipitem.serialport;
            }
        }     

        public EquipItem Equipitem
        {
            get
            {
                return DataCenter.GetEquipItem(iEquip_no);
            }
        }
        
        public Dictionary<int, object> YCResults
        {
            get
            {
                return ycresults;
            }
        }
        public Dictionary<int, object> YXResults
        {
            get
            {
                return yxresults;
            }
        }
        public List<EquipEvent> EquipEventList
        {
            get
            {
                return equipEventlist;
            }
        }

        public int m_sta_no
        {
            get
            {
                return iSta_no;
            }
            set
            {
                iSta_no = value;
            }
        }

        public int m_equip_no
        {
            get
            {
                return iEquip_no;
            }
            set
            {
                iEquip_no = value;
            }
        }

        public int m_retrytime
        {
            get
            {
                return iRetrytime;
            }
            set
            {
                iRetrytime = value;
            }
        }

        public bool bCanConfirm2NormalState
        {
            get
            {
                return Equipitem.bCanConfirm2NormalState;
            }
            set
            {
                Equipitem.bCanConfirm2NormalState = value;
            }
        }

        public string SetParmExecutor
        {
            get
            {
                return setparmexecutor;
            }
            set
            {
                setparmexecutor = value;
            }
        }

        public CEquipBase()
        {
            ycresults = null; 
            yxresults = null;
            equiprow= null;

            ycresults = new Dictionary<int, object>();
            yxresults = new Dictionary<int, object>();
            equipEventlist = new List<EquipEvent>();
            
            iRetrytime = 3;//默认通讯失败重试次数为3
        }

        /// <summary>
        /// 
        /// 在调用GetData 之前调用
        /// </summary>
        /// <param name="sta_no"></param>
        /// <param name="equip_no"></param>
        /// <param name="equip_addr"></param>
        /// <returns></returns>
        public virtual bool init(EquipItem item)
        {
            if (item == null)
            {
                GWDataCenter.DataCenter.Write2Log("CEquipBase调用init(EquipItem item)时,item==null)");
                return false;
            }
            iSta_no = item.iStano;
            iEquip_no = item.iEquipno;
            m_retrytime = serialport.CommFaultReTryTime;
            bSetCacheData = item.bSetCacheData;
            // 设备第一次运行时调用;
            //或者数据库配置进行了更改后调用
            if (b || ResetFlag)
            {
                myequipitem = item;
         //       equiprow = db.GetDataRowOfEquip(iSta_no, iEquip_no);
                //if (ResetFlag)
                //    ResetFlag = false;//这个复位在外部执行,而不是在这里,否则会导致dll里面的对应标识失去意义
                equiprow = StationItem.db_Eqp.Where(m => m.equip_no == iEquip_no).FirstOrDefault();
                if (equiprow == null)
                    return false;
                //      ycptable = db.GetDataTableOfYCP(iSta_no, iEquip_no);

                List<YcpTableRow> rs = StationItem.db_Ycp.Where(m => m.equip_no == iEquip_no).ToList();
                if (rs.Any())
                {
                    ycprows = rs;
                }
                else
                {
                    ycprows = null;
                }

                //        yxptable = db.GetDataTableOfYXP(iSta_no, iEquip_no);
                List<YxpTableRow> rs1 = StationItem.db_Yxp.Where(m => m.equip_no == iEquip_no).ToList();
                if (rs1.Any())
                {
                    yxprows = rs1;
                }
                else
                {
                    yxprows = null;
                }

                EquipNm = equiprow.equip_nm;
                b = false;
                OnLoaded();
            }
            return true;
        }

        /// <summary>
        /// 休眠
        /// </summary>
        /// <param name="t">休眠时间,单位:毫秒</param>
        /// <param name="bBreak">当设置事件发生时,是否中断。在设置函数中休眠,不要中断。</param>
        public void Sleep(int t, bool bBreak = true)
        {
            if (!equipitem.IsRageMode)//非急速模式,正常休眠,避免海量线程场景下急剧的资源消耗(10000线程可能会卡死)
            {
                Thread.Sleep(t);
                return;
            }
            else//急速模式慎用,
            {
                if (!bBreak)
                {
                    Thread.Sleep(t);
                    return;
                }
                int count = t / 10;
                for (int k = 0; k < count; k++)
                {
                    if (RunSetParmFlag)
                        break;
                    Thread.Sleep(10);
                }
            }
            //if (!bBreak)
            //{
            //    GWDataCenter.DataCenter.Delay(t);
            //    return;
            //}
            //for (int k = 0; k < t; k++)
            //{
            //    if (RunSetParmFlag)
            //        break;
            //    GWDataCenter.DataCenter.Delay(1);
            //}
        }
        /// <summary>
        /// 获取设备数据
        /// </summary>
        /// <returns></returns>

        public virtual CommunicationState GetData(CEquipBase pEquip)
        {
            try
            {
                if (RunSetParmFlag)
                {
                    return CommunicationState.setreturn;
                }
                if (ycprows != null)
                {
                    foreach (YcpTableRow r in ycprows)
                    {
                        ///if have setparm, immediately return              
                        if (RunSetParmFlag)
                        {
                            return CommunicationState.setreturn;
                        }
                        if (bFirstGetData && bSetCacheData)//第一次调用直接取缓存数据
                        {
                            SetYCDataFromCache(r.yc_no);
                        }
                        else
                        {
                            if (pEquip.GetYC(r))
                            {
                            }
                            else
                            {
                                return CommunicationState.fail;
                            }
                        }
                    }
                }
                if (yxprows != null)
                {
                    foreach (YxpTableRow r in yxprows)
                    {
                        if (RunSetParmFlag)
                        {
                            return CommunicationState.setreturn;
                        }
                        if (bFirstGetData && bSetCacheData)//第一次调用直接取缓存数据
                        {
                            SetYXDataFromCache(r.yx_no);
                        }
                        else
                        {
                            if (pEquip.GetYX(r))
                            {
                            }
                            else
                            {
                                return CommunicationState.fail;
                            }
                        }
                    }
                }
                if (!pEquip.GetEvent())
                    return CommunicationState.fail;
                if (bFirstGetData)
                    bFirstGetData = false;
            }
            catch (Exception e)
            {
                GWDataCenter.DataCenter.Write2Log(General.GetExceptionInfo(e));
                MessageService.AddMessage(MessageLevel.Debug, General.GetExceptionInfo(e), m_equip_no, false);
                return CommunicationState.fail;
            }
            return CommunicationState.ok;
        }

        /// <summary>
        /// 加载完成处理函数,在第一次加载之后或者系统重置之后调用
        /// </summary>
        /// <param name="sender"></param>
        public virtual bool OnLoaded()
        {
            return true;
        }

        public virtual bool GetYC(YcpTableRow r)
        {
            return false;
        }

        public virtual bool GetYX(YxpTableRow r)
        {
            return false;
        }

        public virtual bool GetEvent()
        {
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="MainInstruct"></param>
        /// <param name="MinorInstruct"></param>
        /// <param name="Value"></param>
        /// <returns></returns>
        public virtual bool SetParm(string MainInstruct, string MinorInstruct, string Value)
        {
            return false;
        }

        public virtual bool Confirm2NormalState(string sYcYxType, int iYcYxNo)
        {
            return false;
        }

        public virtual bool CloseCommunication()
        {
            return true;
        }

        public void YCToPhysic(YcpTableRow r)
        {
            return;
        }
        public void YXToPhysic(YxpTableRow r)
        {
            return;
        }
        /// <summary>
        /// 设置YC点为未取值状态
        /// </summary>
        /// <param name="ycp"></param>
        public void SetYCDataNoRead(IQueryable<YcpTableRow> Rows)
        {
            if (Rows == null)
                return;
            foreach (YcpTableRow r in Rows)
            {
                int iycno = r.yc_no;
                if (!ycpdataflag.ContainsKey(iycno))
                {
                    ycpdataflag.Add(iycno, false);
                }
                else
                    ycpdataflag[iycno] = false;
            }
        }
        /// <summary>
        /// 设置YX点为未取值状态
        /// </summary>
        /// <param name="yxp"></param>
        public void SetYXDataNoRead(IQueryable<YxpTableRow> Rows)
        {
            if (Rows == null)
                return;
            foreach (YxpTableRow r in Rows)
            {
                int iyxno = r.yx_no;
                if (!yxpdataflag.ContainsKey(iyxno))
                {
                    yxpdataflag.Add(iyxno, false);
                }
                else
                    yxpdataflag[iyxno] = false;
            }
        }
        public void SetYcpTableRowData(YcpTableRow r, string o)
        {
            SetYCData(r, o);
        }
        public void SetYcpTableRowData(YcpTableRow r, int o)
        {
            SetYCData(r, o);
        }
        public void SetYcpTableRowData(YcpTableRow r, float o)
        {
            SetYCData(r, o);
        }

        public void SetYcpTableRowData(YcpTableRow r, double o)
        {
            SetYCData(r, o);
        }

        public void SetYcpTableRowData(YcpTableRow r, ValueTuple<DateTime, double> o)
        {
            SetYCData(r, o);
        }
        public void SetYcpTableRowData(YcpTableRow r, ValueTuple<double, double> o)
        {
            SetYCData(r, o);
        }
        public void SetYcpTableRowData(YcpTableRow r, ValueTuple<double, double, double> o)
        {
            SetYCData(r, o);
        }
        public void SetYcpTableRowData(YcpTableRow r, ValueTuple<double, double, double, double> o)
        {
            SetYCData(r, o);
        }
        public void SetYcpTableRowData(YcpTableRow r, ValueTuple<double, double, double, double, double> o)
        {
            SetYCData(r, o);
        }
        public void SetYcpTableRowData(YcpTableRow r, ValueTuple<double, double, double, double, double, double> o)
        {
            SetYCData(r, o);
        }
        public void SetYcpTableRowData(YcpTableRow r, ValueTuple<double, double, double, double, double, double, double> o)
        {
            SetYCData(r, o);
        }
        public void SetYCData(YcpTableRow r, object o)
        {
            int iycno = r.yc_no;
            lock (YCResults)
            {
                if (!YCResults.ContainsKey(iycno))
                    YCResults.Add(iycno, o);
                else
                    YCResults[iycno] = o;
            }
        }

        void SetYCDataFromCache(int iYcNo)
        {
            lock (YCResults)
            {
                if (!YCResults.ContainsKey(iYcNo))
                    YCResults.Add(iYcNo, myequipitem.YCValueCacheDict[iYcNo].Item1);
                else
                    YCResults[iYcNo] = myequipitem.YCValueCacheDict[iYcNo].Item1;
            }
        }

        void SetYXDataFromCache(int iYxNo)
        {
            lock (YXResults)
            {
                if (!YXResults.ContainsKey(iYxNo))
                    YXResults.Add(iYxNo, myequipitem.YXValueCacheDict[iYxNo].Item1);
                else
                    YXResults[iYxNo] = myequipitem.YXValueCacheDict[iYxNo].Item1;
            }
        }

        public object GetYCData(YcpTableRow r)
        {
            lock (YCResults)
            {
                int iycno = r.yc_no;
                if (YCResults.ContainsKey(iycno))
                {
                    return YCResults[iycno];
                }
                return null;
            }
        }

        public void SetYxpTableRowData(YxpTableRow r, bool o)
        {
            SetYXData(r, o);
        }
        public void SetYxpTableRowData(YxpTableRow r, string o)
        {
            SetYXData(r, o);
        }

        public void SetYXData(YxpTableRow r, object o)
        {
            int iyxno = r.yx_no;
            lock (YXResults)
            {
                if (!YXResults.ContainsKey(iyxno))
                    YXResults.Add(iyxno, o);
                else
                    YXResults[iyxno] = o;
            }
        }

        public object GetYXData(YxpTableRow r)
        {
            lock (YXResults)
            {
                int iyxno = r.yx_no;
                if (YXResults.ContainsKey(iyxno))
                {
                    return YXResults[iyxno];
                }
                return null;
            }
        }

	}
}

上次更新: 2024/9/5 14:23:24

← 常见问题 开发第一个插件→

目录
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式