zoukankan      html  css  js  c++  java
  • 通用访问

    1. 效果演示

    2. 通信协议
    • 功能介绍
    • 特点
    • TCP协议
    • WebApi协议
    3. SDK与工具
    4. 应用示例
    • 迷你网管
    • 通用GIS
    • 系统管理
    5. 设计初衷与演化
     

    1. 效果演示

      
     服务端代码
        //创建服务器
        var __服务端 = FT通用访问工厂.创建服务端();
        __服务端.端口 = 8888;
        __服务端.开启();
        //实际对象
        var __基本状态 = new M基本状态();
        __基本状态.版本 = "1.0.0.0";
        __基本状态.待处理问题.Add(new M问题 { 等级 = E重要性.普通, 内容 = "xxxx" });
        __基本状态.待处理问题.Add(new M问题 { 等级 = E重要性.重要, 内容 = "yyyy" });
        __基本状态.开启时间 = DateTime.Now;
        __基本状态.连接设备.Add(new M设备连接 { IP = IPAddress.Parse("192.168.0.1"), 标识 = "X1", 类型 = "X", 连接中 = true });
        __基本状态.连接设备.Add(new M设备连接 { IP = IPAddress.Parse("192.168.0.2"), 标识 = "Y1", 类型 = "Y", 连接中 = true });
        __基本状态.位置 = "威尼斯";
        __基本状态.业务状态 = new Dictionary<string, string> { { "参数1", "参数1值" }, { "参数2", "参数2值" } };
        //可通信对象
        var __对象 = new M对象("基本状态", "");
        __对象.添加属性("版本", () => __基本状态.版本, E角色.所有, null);//最后一个参数是元数据定义
        __对象.添加属性("位置", () => __基本状态.位置, E角色.所有, null);
        __对象.添加属性("开启时间", () => __基本状态.开启时间.ToString(), E角色.所有, null);
        __对象.添加属性("待处理问题", () => HJSON.序列化(__基本状态.待处理问题), E角色.所有, null);
        __对象.添加属性("连接设备", () => HJSON.序列化(__基本状态.连接设备), E角色.所有, null);
        __对象.添加属性("业务状态", () => HJSON.序列化(__基本状态.业务状态), E角色.所有, null);
        __对象.添加方法("重启", __实参 => {
            //处理实参 
            return string.Empty;  //返回空字符串或者json格式的对象
        }, E角色.所有, null, null); //最后两个参数是对参数和返回值的元数据定义
        __对象.添加事件("发生了重要变化", E角色.所有, null);
        //将对象加入到服务器
        __服务端.添加对象("基本状态", () => __对象);
        //触发服务器端事件
        __服务端.触发事件("基本状态", "发生了重要变化", null, null);//最后两个参数是实参和客户端地址列表(可选)
    客户端代码
    图片中的客户端是通用的,所以不用写代码,如果用于面向客户的定制界面,示例如下
        //创建客户端
        var __客户端 = FT通用访问工厂.创建客户端();
        __客户端.连接(new IPEndPoint(IPAddress.Any, 8888));
        var __版本 = __客户端.查询属性值("基本状态", "版本");
        var __待处理问题 = HJSON.反序列化<List<M问题>>(__客户端.查询属性值("基本状态", "待处理问题"));
        __客户端.执行方法("基本状态", "重启", null); //最后一个参数是实参
        __客户端.订阅事件("基本状态", "发生了重要变化", __实参 => {
            //处理实参
        });

    2. 通信协议

    功能介绍

    • 该协议是应用层的底层协议,可以承载各种业务,开发具体业务时,只需定义需要通信的对象元数据
    • 协议支持面向3种角色,开发/工程/客户,可以优雅的解决程序设计时的角色混杂问题
    • 基于配套的SDK开发分布式应用,像开发单机应用一样容易
    • 支持通用客户端(winform/web),面向开发/工程的功能,无需写代码就能访问服务器端对象;面向客户的功能,服务器端开发也能方便的测试
     

    特点

     

    • 只要定义通信对象就可以开发分布式应用,简单强大
    • 跨平台,通用性强;开发SDK的难度不是很大,可以参考我写的.NET版本的,可以做到深度控制
     

    TCP协议

    报文格式

    字段

    类型

    长度(字节)

    说明

    起始标识

    Byte[]

    2

    固定为0xAAAA,用于识别报文开始

    报文内容长度

    Int32

    4

    余下所有字段长度

    功能标识

    Int16

    2

    用于识别报文类型

    发送方事务标识

    Int32

    4

    由发送方指定。当会话类型为请求型时,用于区别发送方的不同请求;当会话类型为通知型时,固定为0

    接收方事务标识

    Int32

    4

    由接收方指定。当会话类型为请求型时,用于区别接收方的不同请求,特别的,会话开始的第一条报文,发送方的该字段填0;当会话类型为通知型时,固定为0

    负载

    Byte[]

    *

    由报文内容长度字段指定, 推荐编码格式为UTF8文本, 采用json格式, 大数据量时采用压缩算法

     功能码

    功能

    查询对象列表(请求)

    1

    查询对象列表(响应)

    2

    查询对象明细(请求)

    3

    查询对象明细(响应)

    4

    执行方法(请求)

    5

    执行方法(响应)

    6

    查询属性值(请求)

    7

    查询属性值(响应)

    8

    订阅事件

    9

    取消订阅事件

    A

    接收事件

    B

     

    查询对象列表

    请求:

    响应: [{名称, 分类, 角色}]

     

    查询对象明细

    请求: {对象名称}

    响应: {属性列表[{名称, 元数据, 角色}], 方法列表[{名称, 形参列表[{名称, 元数据}, 角色], 返回值元数据}], 事件列表[{名称, 形参列表[{名称, 元数据}], 角色}]}

     

    元数据: {类型, 结构(单值/对象/单值数组/对象数组), 描述, 默认值, 范围, 子成员列表[{名称, 元数据}]}

     

    执行方法

    请求: {对象名称, 方法名称, 实参列表[{名称, }]}

    响应: {成功, 描述, 返回值}

     

    查询属性值

    请求: {对象名称, 属性名称}

    响应: {成功, 描述, 返回值}

     

    订阅事件

    通知: {对象名称, 事件名称}

     

    取消订阅事件

    通知: {对象名称, 事件名称}

     

    接收事件

    通知: {对象名称, 事件名称, 实参列表[{名称, }]}

     

     

    WebApi协议

    响应数据格式(ContentType)application/json;charset=utf-8

    功能

    HTTP方法

    地址

    查询对象列表

    GET/ POST

    objects.k

    查询对象明细

    GET/ POST

    object.k

    执行对象方法

    GET/ POST

    method.k

    查询对象属性值

    GET/ POST

    proterty.k

    登录

    POST

    login.k

    心跳

    GET

    heart.k

     

    :成功后会设置cookies, 键为token

     

    查询对象列表

    参数:

    响应: [{名称, 分类, 角色}]

     

    查询对象明细

    参数: 对象名称

    响应: {属性列表[{名称, 元数据, 角色}], 方法列表[{名称, 形参列表[{名称, 元数据}], 返回值元数据, 角色}]}

     

    执行方法

    参数: 对象名称, 方法名称, 实参列表[{名称, }]

    响应: {成功, 描述, 返回值}

     

    查询对象属性值

    参数: 对象名称, 属性名称

    响应: {成功, 描述, 返回值}

     

    登录

    参数: password

    响应: {成功(true/false), 描述}

     

    心跳

    参数:

    响应: 服务器时间

    3. SDK与工具

    不实时更新
    SDK核心接口
        public interface IT服务端
        {
            int 端口 { get; set; }
            int WebApi端口 { get; set; }
            void 添加对象(string 对象名称, Func<M对象> 获取对象);
            void 删除对象(string 对象名称);
            void 开启();
            event Action<IPEndPoint> 客户端已连接;
            event Action<IPEndPoint> 客户端已断开;
            List<IPEndPoint> 客户端列表 { get; }
            void 关闭();
            void 触发事件(string 对象名, string 事件名, Dictionary<string, string> 实参 = null, List<IPEndPoint> 地址列表 = null);
        }
        public interface IT客户端
        {
            IPEndPoint 设备地址 { get; }
            void 连接(IPEndPoint 设备地址);
            void 断开();
            bool 连接正常 { get; }
            /// <summary>
            /// true:主动断开,false:被动断开
            /// </summary>
            event Action<bool> 已断开;
            bool 自动重连 { get; set; }
            event Action 已连接;
            M对象列表查询结果 查询可访问对象();
            M对象明细查询结果 查询对象明细(string 对象名称);
            string 查询属性值(string 对象名, string 属性名, int 超时毫秒 = 3000);
            string 执行方法(string 对象名, string 方法名, Dictionary<string,string> 参数列表 = null, int 超时毫秒 = 3000);
            void 订阅事件(string 对象名, string 事件名, Action<Dictionary<string, string>> 处理方法);
            void 注销事件(string 对象名, string 事件名, Action<Dictionary<string, string>> 处理方法);
            /// <summary>
            /// 通常用于调试
            /// </summary>
            event Action<M接收事件> 收到了事件;
        }
    public class M对象
    {
        public string 名称
        {
            get { return 概要.名称; }
            set { 概要.名称 = value; }
        }
        public string 分类
        {
            get { return 概要.分类; }
            set { 概要.分类 = value; }
        }
        public E角色 角色
        {
            get { return 概要.角色; }
            set { 概要.角色 = value; }
        }
        internal M对象概要 概要 { get; set; }
        internal M对象明细查询结果 明细 { get; set; }
        /// <summary>
        /// 字典中的键string:方法名; 值:方法; 方法中的string:返回值
        /// </summary>
        Dictionary<string, Func<Dictionary<string, string>, string>> _所有方法 = new Dictionary<string, Func<Dictionary<string, string>, string>>();
        /// <summary>
        /// 字典中的键string:方法名; 值:方法; 方法中的string:返回值
        /// </summary>
        Dictionary<string, Func<Dictionary<string, string>, IPEndPoint, string>> _所有方法_带地址 = new Dictionary<string, Func<Dictionary<string, string>, IPEndPoint, string>>();
        /// <summary>
        /// 字典中的键string:属性名; 值:属性值计算方法
        /// </summary>
        Dictionary<string, Func<string>> _所有属性方法 = new Dictionary<string, Func<string>>();
        public M对象(string __名称, string __分类)
        {
            this.概要 = new M对象概要(__名称, __分类);
            this.明细 = new M对象明细查询结果();
        }
        public void 添加属性(string __名称, Func<string> __计算值, E角色 __角色 = E角色.开发, M元数据 __元数据 = null)
        {
            this.角色 = this.角色 | __角色;
            this.明细.属性列表.Add(new M属性(__名称, __元数据, __角色));
            _所有属性方法[__名称] = __计算值;
        }
        public void 添加方法(string __名称, Func<Dictionary<string, string>, string> __执行方法, E角色 __角色 = E角色.开发, List<M形参> __参数列表 = null, M元数据 __返回值元数据 = null)
        {
            this.角色 = this.角色 | __角色;
            _所有方法[__名称] = __执行方法;
            this.明细.方法列表.Add(new M方法(__名称, __参数列表, __返回值元数据, __角色));
        }
        public void 添加方法(string __名称, Func<Dictionary<string, string>, IPEndPoint, string> __执行方法, E角色 __角色 = E角色.开发, List<M形参> __参数列表 = null, M元数据 __返回值元数据 = null)
        {
            this.角色 = this.角色 | __角色;
            _所有方法_带地址[__名称] = __执行方法;
            this.明细.方法列表.Add(new M方法(__名称, __参数列表, __返回值元数据, __角色));
        }
        internal string 执行方法(string __名称, Dictionary<string, string> __参数列表, IPEndPoint __来源)
        {
            if (_所有方法.ContainsKey(__名称))
            {
                return _所有方法[__名称].Invoke(__参数列表);
            }
            if (_所有方法_带地址.ContainsKey(__名称))
            {
                return _所有方法_带地址[__名称].Invoke(__参数列表, __来源);
            }
            throw new ApplicationException("执行方法失败: 无此方法");
        }
        internal string 计算属性(string __属性名称)
        {
            if (_所有属性方法.ContainsKey(__属性名称))
            {
                return _所有属性方法[__属性名称]();
            }
            throw new ApplicationException("计算属性失败: 无此属性");
        }
        public void 添加事件(string __名称, E角色 __角色 = E角色.开发, List<M形参> __参数列表 = null)
        {
            this.角色 = this.角色 | __角色;
            this.明细.事件列表.Add(new M事件(__名称, __参数列表, __角色));
        }
    }
    工具
     
     
     

    4. 应用示例

    迷你网管

    协议(对象)

    名片
    属性
    名称
    描述
    版本号
    版本时间 
     
    方法
    查询
    返回值: {名称, 描述, 版本号, 版本时间}
     
    查询参数
    返回值: [{名称, 值}] 
     
    状态
    属性
    启动时间
    未清除告警数量
    健康状态(优/良/中/差)
    状态开始时间
     
    注: 健康状态推荐规则是存在紧急告警为差, 存在重要告警为中, 存在次要告警为良, 否则为优 
     
    方法
    查询概要状态
    返回值: {启动时间, 健康状态, 状态开始时间, 未清除告警数量} 
     
    查询业务概要
    参数: 类别(可为空), 属性(可为空)
    返回值: [{类别, 属性, 当前值, 正常范围, 正常, 单位, 描述}] 
     
    查询未清除告警
    参数: 条件{每页数量, 页数, 来源设备类型(可选), 来源设备标识(可选), 类别(可选), 重要性(可选), 关键字(可选)}
    返回值: {总数量, 列表[{标识, 来源设备类型, 来源设备标识, 产生时间, 类别, 重要性, 描述, 原因, 解决方案}]} 
     
    重要性: 
     

    一般(1)

    正常通知无需排除告警

    次要(2)

    非直接影响业务的使用不需要立即处理但还是需要排除告警

    重要(3)

    严重的故障可能会影响业务需要用户处理

    紧急(4)

    系统级故障严重影响业务需要用户立即处理

     
     
    事件
    上报告警
    参数: 事件参数{标识, 来源设备类型, 来源设备标识, 产生时间, 类别, 重要性, 描述, 原因, 解决方案} 
     
    上报清除
    参数: 事件参数{标识, 来源设备类型, 来源设备标识} 
     
    健康状态变化
    参数: 事件参数{启动时间, 健康状态, 状态开始时间, 未清除告警数量} 
     
    系统
    方法
    重启
    返回值: 无 
     
    关闭
    返回值: 无 
     
    查询版本记录
    返回值: [{版本号, 标签[], 修改记录}] 
     
    FTP
    属性
    支持(true/false)
    运行中(true/false)
    端口号
    目录路径
    编码(ASCII/UTF8/GB2312)
     
    方法
    设置端口号
    参数: 端口号
    返回值: 无 
     
    开启
    返回值: 无 
     
    关闭
    返回值: 无 

    客户端界面

    下图模拟500个客户端连接



        

    通用GIS

    重量级作品,单独写文章介绍
     

    系统管理

     
     
     
     
     
     
     
     

    5. 设计初衷与演化

    初衷

    公司的界面软件内容非常混杂,充斥了大量的面向开发人员的元素,与其他设备间的协议,太多的地方让人恶心,耦合度极高,稳定性和可维护性极差。年前讨论重构,其中最基本的一条思路就是区别客户、工程、开发三种角色,面向最终客户的界面软件与其他设备间的协议,应该以应用为导向设计协议(上层决定下层,下层提供可行性约束,协议接口必须抽象)。将面向开发人员的内容与面向最终客户的软件分离开后,需要重新编写面向开发人员的界面与协议,加上我之前一直重视和建议面向工程(及测试)人员开发工具类软件,这就是设计通用访问协议的初衷,因为工程和开发人员不需要最终客户那样的定制化的界面。经过反复的推敲,最终以“自描述”为核心的界面以及协议诞生了。

     

    演化

     

    以后设计分布式的软件,首先完全面向业务的设计出需要的对象及其成员元数据,形成一份简单的但是纯粹的协议,无需定义额外的功能码、报文头、结构体;然后服务端基于通用访问SDK(与平台和开发语言相关),注入对象及其成员的实现(类似于和程序内部的模块、对象挂钩子或是牵线),通过通用访问工具可以直接测试协议接口,无需最终的客户端,同时提供一些面向工程和开发人员自身的对象(这部分对象易变性很高,以往常寄生在面向客户的界面软件中,严重加大无谓的工作量),以便部署和调试;客户端方面同样基于通用访问SDK实现访问各种设备的协议约定的对象;最后如果一个设备需要与多个其他类型的设备互联,也无需定义多套功能码、报文编解码方案,只需使用对象来提供统一的服务。似乎看到了蓝天白云,鸟语花香,人人和睦相处,高效自动化工作的画面。

     





  • 相关阅读:
    程序的版式
    文件结构
    LIB和DLL的区别与使用
    静态链接库
    C++ Vector
    C++ Map
    C++ List
    快速实现十进制向二进制转换
    Fail2ban 运维管理 服务控制
    Fail2ban 配置详解 动作配置
  • 原文地址:https://www.cnblogs.com/wangk/p/4cfa380ab18cf72eb6d13d25cf7af535.html
Copyright © 2011-2022 走看看