# 交互流程数据格式说明 ## 主题说明 ### system/upstream qos = 1 用于主机注册到平台 ### host/upstream/$uuid qos = 1 用于主机向平台汇报数据 ### host/downstream/$uuid qos = 2 用于平台向主机下发所有的指令信息 ## 需要注意的点 1. 主机的uuid是采用主动发现的机制,因此host主机在启动时需要先subscribe主题: host/downstream/${uuid} ## register主机注册会话 1. 请求: 设备端向主题`system/upstream`发送注册信息 ```json { "method": "register", "params": { "uuid": "$uuid", "os_version": "系统版本", } } ``` 2. 响应: `host/downstream/$uuid` t = 0 ```json { "code": 0 | 1, // 0表示失败,1:表示成功 // 内容 "m": "$bytes" } ``` ## create_session主机注册会话 register的时候,设备端向主题`host/upstream/${uuid}`发送注册信息,由于topic里面已经携带了主机标识,因此body里面不需要$uuid, 该信息格式如下: ```json { "method": "create_session", "params": { "pub_key": "该客户端自己生成的公钥" } } ``` 其中,`pub_key`表示客户端自身的公钥,该公钥用于与服务端交换aes密钥。 需要注意,这里的交互逻辑有变更(topic: host/downstream/$uuid) 1. 当主机未激活时,返回: #{<<"a">> => false, <<"aes">> => <<"">>}, 2. 当主机已经激活时,返回: #{<<"a">> => true, <<"aes">> => Aes} 3. 当主机超过一定的时间没有收到任何返回的时候,需要重试执行create_session操作 t = 10 ```json { "a": true/false, // 表示是授权还是拒绝授权 "aes": "", // 如果a字段为true,表示服务端允许授权,该aes为一个加密字符串,以后的通信,服务端和设备端都使用该aes密钥进行加解密。 } ``` ## 主机授权消息格式 t = 8 ```json // 数据采用明文传输,主机在收到通知后,需要通过create_session操作重新协商aes { "auth": true/false, // true表示授权,此时aes的值不为空;false表示取消授权 "reply": { "topic": "", // 主机端操作成功后需要回写的topic名称 "assoc": "" // 关联的信息,回写的时候需要带上 } } // 回写的消息格式, 其中topic为: reply.topic的值; reply的值可能为空,当为空时,不需要回写 { "code": 0 | 1, // 0表示操作失败,1表示成功 message: "", assoc: "下发给你值" } ``` ## 数据上传结构 设备端在采集到数据之后,会向topic为`host/upstream/$uuid`发送消息,消息格式如下: ```json { "methods": "data", "params": "$bytes" } ``` 其中`params`代表具体的采集信息,这部分数据通过与服务端交互商量的: base64:encode(aes加密),加密之前是一个列表,列表里面的数据格式如下: ```json: [ { "service_name": "从该设备端的哪个服务采集的数据", "at": int, 精确到毫秒 // 该微服务采集的数据,是一个包含map的列表类型,map的内容可以由微服务自己指定 // 目前一般的格式是"metric-name": $value样式的数据 "fields": [ { "name1": "test" "name2": 124, "name3": false, "device_id": $uuid 非设备产生的device_id为空 } ], // 微服务自身可以生成tag,用于微服务指定自己的一些性质,目前使用得不多,以后可以扩展, // 是一个map[string]string类型的数据 "tags": { "tag1": "value1", "tag2", "value2" } // todo 在insert数据到influxdb的时候需要增加service_name + host_uuid } ] ``` ## 命令下发结构 命令下发,用于服务端或者其他的系统,通过调用接口,向设备端发送消息,设备端会监听`host/downstream/$uuid`的消息。 服务端在发送之前,应先用该客户端的aes密钥进行加密,将加密之后的二进制数据发送到该topic。 TODO 命令下发是需要增加当前的时间戳,host主机用来协调任务的一致性 加密前的消息结构如下: // 消息类型,目前支持四种消息类型: // 1代表参数下发,就是向该设备端的微服务发送消息,该消息会辗转发送给微服务进行处理,比如,设置modbus微服务的波特率等消息 // 2代表采集向下发,比如,设置某个设备短上的modbus微服务采集某个地址的数据 // 3代表下发微服务文件。 // 4代表下发数据流图,这个指令用于设置设备端上各个微服务之间的逐句流转。 占用数据项的第一个字节 "t": 1|2|3|4, ```json { // 针对不同的命令类型,这个字段里的`to`和`m`数据有所不同,具体在下面的小节描述 // 任务id,服务端在下发数据的时候,需要生成一个唯一的uuid, // 用于标识一个任务 "t_id": "任务id", // 表示发给哪个微服务,这里是服务的标识,即服务名称 "to": "", // 命令执行的超时时间,单位为秒 "t": 10, // 实际内容 "m": "$bytes", } ``` 下面介绍几种下发类型: ### 参数下发的结构 对于参数下发,下发内容中的m为一个`map[string]interface{}`结构,用于向某个微服务发送参数,具体参数内容由微服务的参数配置提供。 ### 采集项下发的结构 采集项下发时,下发内容中的m为一个`[]map[string]interface{}`结构的列表,每一个条目是一个采集项内容,具体采集向内容由微服务的采集项配置提供。 ### 微服务下发的结构 在微服务下发中,`to`字段会被忽略,可以填写空字符串,而m字段为json化之后的数据,json化之前结构如下: ```json { "f": "微服务名", "v": "微服务版本" "k": "微服务下载的token" "md5": "微服务的md5值,用于验证下载完整性" // ms表示是微服务,config表示配置文件,self表示efka的新版本 "t": "ms|config|self" // o代表oldversion,老版本,如果t为ms,且o不为空字符串, // 则表示要升级微服务版本,老版本的内容会被删除和替换。 "o": "old-version" } ``` ## ping结构 ping结构会上传到topic为"host/upstream/$uuid", 结构体由msgpack格式编码, ping结构每隔20秒发送一次, 其中params的数据格式为: base64:encode(aes("加密")) ```json { "method": "ping", "params": { // 硬件信息,目前有剩余内存,剩余磁盘和cpu负载 // 剩余内存,单位为mb "memory": { "total": 1024, // 内存数,mb "used": "$int" // 剩余内存数 }, "disk": { "total": 1024, // 硬盘容量GB "used": "$int" // 剩余硬盘内容GB }, "cpu_load": $float, // 浮点数 "cpu_temperature": $float // 稳定信息 "cpu_core": $int, "boot_time": 2000, // 启动时间 "efka_version": "1.0.0", // 客户端版本 "kernel_arch": "arm64", // 客户端硬件架构 "province": "", // 所在省 "city": "", // 所在市 "adcode": 100, // 所在城市的编号 "ips": [ "ip地址1", "ip地址2" ], // 接口信息 "interfaces": [ { "status": 0|1, "接口状态,0离线,1在线" "name": "接口名称", "desc": "接口描述", } ] } } ``` ## 命令下发步骤上报结构 在任务下发之后,设备端会根据命令到哪个环节,进行步骤上报,上报的消息写入到topic为`host/upstream/$uuid`,上报结构如下: ```json { "method": "feedback_step", "params": { "task_id": "任务的task id", // sc为step code,具体地: // 0代表该任务开始了,服务端创建该任务之后,是这个代码 // 1代表任务被分发了,服务端向nats(mqtt)发送消息之后,是这个代码 // 2代表任务被设备端接收到了 // 3代表该任务已经被发送给微服务进行处理了 // 4代表该任务已经被微服务收到了,微服务正在处理 // 5代表任务已经完成,微服务已经处理完成。 "code": $int } } ``` 有了这个步骤,最后任务超时等情况,就可以知道任务卡在哪里。 ## 命令下发结果上报结构 任务在微服务处理完成之后,设备端会向服务端反馈任务执行的结果。该结果会写入`host/upstream/$uuid`,上报的结构如下: ```json { "method": "feedback_result", "params": { "task_id": "任务id", // unix nano类型 "time": $int, // 返回的结果码,0代表成功,其他代表出错 "code": $int, "reason": "任务执行的结果", "error": "错误消息,当c为非0时,这个字段会表示出错消息", // 返回任务类型,1表示任务是微服务下发,0代表是命令下发 "type": 0 | 1, } } ``` ## inform结构 inform用于客户端主动通知服务端本地事情的发生,比如,自身的某个微服务下线了,就会发送一个inform信息给服务端。 topic: host/upstream/$uuid inform信息会发送给``, 结构如下: ```json { "method": "inform", "params": { "at": $int64, // 微服务信息 "services": [{ "scene_id": $int "场景的编号", "name": "微服务名称", "version": "微服务版本", "version_copy": "微服务副本", // 微服务是否在线,0表示离线,1表示在线 "status": 0|1 }] } } ```