# C200K [TOC] # 1. 前言 ## 1.1 背景 方便客户基于C200K基站设备使用 通信基站数据接收及解析。 ## 1.2 编写目的 此说明书是为了说明协议内容,开发流程,方便基于此进行二次开发。 ## 1.3 读者对象 1. 相关研发人员 2. 方案制定人员 3. 决策人员 # 2. 开发流程 C200K基站,采用MQTT协议进行开发,基站接入MQTT服务器,应用从MQTT服务器下发指令和接收数据 整个交互图如下: ![](img/mqtt_1.png) ## 2.1 MQTT服务器准备 MQTT服务器要求: 厂家使用EMQX 进行测试 能够正常收发MQTT数据。 服务器的性能参数由业务决定。 域名: 公网域名或ip或者基站所处局域网ip。 MQTT服务器配置结果检查 使用MQTT.FX客户端,配置域名,端口,用户名和密码信息,然后连接,如果提示连接成功,则表示MQTT服务器可用。基站clientid 是基站编号。 ![](img/mqtt_2.png) ## 2.2 基站配置 通过USB连接基站,打开基站服务器配置软件。 ![](img/mqtt_3.png) 如果基站没有连接电脑,工具显示检测基站中,这时候读写无效。 连接正常后,按照各个信息进行写入。 设置基站服务器域名(ip)、端口,用户信息、密码信息,写入基站。 如果MQTT服务器没有启用用户密码验证,用户密码信息可以随机设置。 拔掉USB,连接网线,接入网络,查看网络是否连接正常。(基站上面的灯是否蓝色闪烁) 如果选用POE供电,请使用标准的POE(48V)供电 ![](img/mqtt_7.png) 基站说明 ![](img/mqtt_4.png) 服务器IP设置软件下载 ## 2.3 应用开发 整体开发流程图如下: ![](img/mqtt_5.png) # 3. 功能清单 ## 3.1 支持的功能清单 | **功能** | **说明** | | --- | --- | | 授时 | 给基站校时 | | 基站上下线 | 基站上下线信息 | | 键盘上下线 | 键盘上下线信息 | | 基站信息设置与读取 | 基站信息设置与读取 | | 基站信息上报 | 基站信息上报 | | 基站错误信息上报 | 基站错误信息上报 | | 键盘上线 | 键盘上线 | | 读取白名单 | 读取白名单 | | 添加白名单 | 添加白名单 | | 删除白名单 | 删除白名单 | | 清空白名单 | 清空白名单 | | 基站复位 | 基站复位 | | 签到 | 键盘签到 | | 单题 | 下发一题练习 | | 抢答 | 抢答 | | 多级题号多题 | 多级题号多题 | | 基站升级 | 基站固件升级 | | 键盘升级 | 键盘固件升级 | | 文本下发 | 文本下发 | | 学生卡信息上报 | 学生卡信息上报 | | 下发生避字 | 下发生避字 | ## 3.2 协议简介 ### 3.2.1 MQTT 关于MQTT说明,请参考 https://mqtt.org/ ${clinetId} clinetId 是基站编号,为替换字符串。 #### 3.2.1.1 应用程序发布的主题 | 主题 | 功能 | | --- | --- | | /client/${clientId}/operate | 基站的设置,查询等指令 | | /lesson/class/${clinetId}/receive | 接收答题的指令 | #### 3.2.1.2 应用程序订阅的主题 | 主题 | 功能 | | --- | --- | | /client/${clientId}/online | 基站上线通知。 | | /client/${clientId}/offline | 基站下线通知。 | | /client/${clientId}/card/online | 答题器上线通知。 | | /client/${client}/operate | 对基站进行设置或查询信息 | | /client/${clientId}/report | 基站主动上报状态,或者对设置指令的回应。 | | /lesson/class/${clinetId}/receive | 基站对答题的设置 | | /lesson/class/${clinetId}/send | 基站对答题的反馈 | | /client/${clientId}/error | 基站错误信息上报 | ### 3.2.2 协议格式 ![](img/mqtt_6.png) 协议采用json格式,数据中字段见属性介绍。 ### 3.2.3 指令属性介绍 | 名称 | 类型 | 说明 | | --- | --- | --- | | i | number | 消息指令类型(instruction),取值范围: 1-9999。
i = 3 添加白名单
i = 4 删除白名单
i = 5 清除白名单
i = 11 停止
i = 12开启答题
i = 13 接收答题数据
i = 14 键盘上线
i = 17 基站升级
i = 18 升级报告
i = 19 授时
i = 20 设置基站信息
i = 21基站信息上报
i = 22基站错误信息上报
i = 28 键盘升级
i = 29 键盘升级报告
I= 30 读取白名单
I= 31 白名单上传
i = 40 基站上线
i = 41 基站下线
i = 60 文本下发
i = 61 文本下发返回| | m | object | 答题消息的内容(message) 对象或数组
数组对象,一般与分包结合,分包原则是发送的json数据不要超过1k,如果超过1k,基站将无法接收。 大数据将采用分包发送,每包的数据不超过1k| | c | string | 答题器的序号(card) | | t | number | 时间戳(10位timestamp) 单位秒 需要先校时 | | qm | number | 模式(question model)取值范围:
0:停止
1:单题
7:签到
14:多级编号答题 | | qt | number | 题型(question type),取值范围:
0:字母单选
1:字母多选
2:数字题
3:判断题
5:抢答
7: 自判题判断题 | | qs | number | 该题型数量(question sum),单题为1,多题则为实际数量。 | | qn | number | 题目序号(question No),题目顺序号 | | dn | string | display no 显示编号,键盘上显示的题目编号| | a | string | 答案(answer)
示例:字母单选:A
字母多选:A|B|C | | en | string | 练习(考试)编号(exercise No)每次考试或者练习的唯一编号,用于确定提交的答案属于那次练习。 | | on | number | 单选和多选题的选项数量(option no) | | cid | string | 客户端ID(client Id) | | cm | string | 客户端型号(client Model) | | cv | string | 客户端版本(client Version)基站软件版本号 | | f | number | 信道频点(frequency) 基站频点取值:1-80 | | content | string | 题干内容(选项信息都包含在此),中文信息用hex进行传输。内容前面需要 hex:详见后面示例 | | sn | string | sn 序列号 | | et | number | 错误类型et =1 基站频点冲突 | | em | string | 错误消息em = "3" 配合et使用,如et=1 em=3 表示3号频点冲突 | | success | number | 0 成功 -1 错误/失败 -2 基站忙 | ### 3.2.4 分包示例 如果要下发第二行文本,一次业务需要下发50个键盘。 正常组包,基站将会接收失败: ```json { "i": 60, "en": "6", "cmd": 10, "total": 1, "current": 1, "qs": 50, "m": [{ "sn": "2126833671", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833672", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833673", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833674", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833675", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833676", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833677", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833678", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833679", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833680", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833681", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833682", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833683", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833684", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833685", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833686", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833687", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833688", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833689", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833690", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833691", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833692", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833693", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833694", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833695", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833696", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833697", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833698", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833699", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833700", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833701", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833702", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833703", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833704", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833705", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833706", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833707", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833708", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833709", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833710", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833711", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833712", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833713", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833714", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833715", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833716", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833717", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833718", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833719", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833720", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }] } ``` 该数据大小超过了1k(实际4091字节),基站会接收失败。 要采取分包进行分送,每包大小不要超过1k(1024字节)
现在对数据进行分包,通过计算每包发送10个键盘数据。分为5包。
下面是5包的每一包数据:
第一包: ```json { "i": 60, "en": "6", "cmd": 10, "total": 5, "current": 1, "qs": 50, "m": [{ "sn": "2126833671", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833672", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833673", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833674", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833675", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833676", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833677", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833678", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833679", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833680", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }] } ``` 5包,total 要赋值5
当前是第一包 current 赋值1
总数有50个,qs赋值50
数据包大小892字节,基站能接收。

第二包: ```json { "i": 60, "en": "6", "cmd": 10, "total": 5, "current": 2, "qs": 50, "m": [{ "sn": "2126833681", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833682", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833683", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833684", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833685", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833686", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833687", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833688", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833689", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833690", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }] } ``` 5包,total 要赋值5
当前是第二包 current 赋值2
总数有50个,qs赋值50
数据包大小892字节,基站能接收。
第三包: ```json { "i": 60, "en": "6", "cmd": 10, "total": 5, "current": 3, "qs": 50, "m": [{ "sn": "2126833691", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833692", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833693", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833694", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833695", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833696", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833697", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833698", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833699", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833700", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }] } ``` 5包,total 要赋值5
当前是第三包 current 赋值3
总数有50个,qs赋值50
数据包大小892字节,基站能接收。
第四包: ```json { "i": 60, "en": "6", "cmd": 10, "total": 5, "current": 4, "qs": 50, "m": [{ "sn": "2126833701", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833702", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833703", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833704", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833705", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833706", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833707", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833708", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833709", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833710", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }] } ``` 5包,total 要赋值5
当前是第四包 current 赋值4
总数有50个,qs赋值50
数据包大小892字节,基站能接收。
第五包: ```json { "i": 60, "en": "6", "cmd": 10, "total": 5, "current": 5, "qs": 60, "m": [{ "sn": "2126833711", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833712", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833713", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833714", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833715", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833716", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833717", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833718", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833719", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }, { "sn": "2126833720", "text": "hex:cae4c8ebd1a7bac5bdf8d0d0b0f3b6a8" }] } ``` 5包,total 要赋值5
当前是第五包 current 赋值5
总数有50个,qs赋值50
数据包大小892字节,基站能接收。
每包的数据计算,少于1k即可。 ### 3.2.5 生避字检测方法 下发文本姓名时,需要先检测下发的姓名内容
下发的内容当中,如果有些字属于生避字,需要在业务进行前,先把生避字对应的字码下发给到基站。
目前键盘最多存储10个生避字。
字码表文件:字码表下载 判断字符的GB2312编码是否是生避字,方法: 字符的GB2312编码: ```java byte[] inputs = input.getBytes("GB2312"); // 原文内容 ``` byte数组转int型 ```java public static int bytes2ToInt(byte[] b, int offset) { int x = 0; if (b.length - offset >= 2) { x = (int) (((b[offset ] & 0xff) << 8) | ((b[offset + 1] & 0xff) << 0)); } return x; } ``` 判断这个字符是否是生避字 ```java public static boolean isGB2312Font(int code) { int GB_H = (code >> 8) & 0xFF; int GB_L = code & 0xFF; if (GB_H < 0xA1 || GB_H > 0xF7) { return true; } else if (GB_L < 0xA1 || GB_L > 0xFE) { return true; } return false; } ``` 获取这个生避字的编码:
获取字码表的方法是该字符的GB2312的16进制数,然后根据该16进制数据从字码文件中定位该字符的字码
例如计算出“綩”的GB2312得到的byte,转换byte字节为整数48719。
然后在字码表文件code.dat文件偏移48719*24位置,然后读取24个字节,为该字的字码(每个字的字码为24个字节)
下发给基站 ``` byte[] inputs = "綩".getBytes("GB2312"); // 原文内容 int code = bytes2ToInt(inputs,0); byte[] result = readFileContent(code,24); String hex = bytesToHexString(inputs) + bytesToHexString(result); public byte[] readFileContent(int start, int len) { byte[] buffer = new byte[len]; try { InputStream inputStream = new FileInputStream(new File("path/code.dat")); inputStream.skip(start*len); inputStream.read(buffer, 0, len); inputStream.close(); } catch (Exception e) { e.printStackTrace(); } return buffer; } public static String bytesToHexString(byte[] src) { StringBuilder stringBuilder = new StringBuilder(""); if (src == null || src.length <= 0) { return stringBuilder.toString(); } for (int i = 0; i < src.length; i++) { int v = src[i] & 0xFF; String hv = Integer.toHexString(v); if (hv.length() < 2) { stringBuilder.append(0); } stringBuilder.append(hv); stringBuilder.append(""); } return stringBuilder.toString(); } ``` 最后下发的json数据 ```json { "i": 60, "cmd": 70, "current": 1, "total": 1, "qs": 1, "en": "17", "m": [{ "text": "hex:be4fdcb3c880669af203f292f6000e0006000806010007080e00", "sn": "2126833671" }] } ``` ## 3.3 功能说明 ### 授时 对客户端授时,发送指令即可,基站会完成校时。
主题 : /client/${client}/operate ``` { "i": 19, "t": 1634802943 } ``` | 名称 | 备注 | | --- | --- | | i = 19 | 授时 | | t | 当前时间轴秒 | ### 基站管理 #### 基站上线 由基站发给应用程序,应用需要订阅主题
主题 : /client/${clientId}/online
指令内容: ``` { "i": 40 } ``` | 名称 | 备注 | | --- | --- | | i = 40 | 基站上线 | #### 基站下线 由服务器发给应用程序,应用需要订阅主题
主题:/client/${clientId}/offline
指令内容: ```json { "i": 41 } ``` | 名称 | 备注 | | --- | --- | | i = 41 | 基站下线 | #### 基站信息设置与读取 应用程序发送给基站
主题: /client/${client}/operate
指令内容: 设置 ```json { "i": 20, "m": { "f": 1, "mqtt_ip": "edunew.sunvotecloud.cn", "mqtt_port": 1883, "mqtt_username": "admin", "mqtt_password": "1223456789" } } ``` 读取 ```json { "i": 20, "m": { } } ``` | 名称 | 备注 | | --- | --- | | i= 20 |基站信息设置与读取 | | f |基站频点 频点范围1-80| | mqtt_ip | 连接服务器的IP| | mqtt_port | 连接mqtt服务器的端口 | | mqtt_username | 连接mqtt服务器的用户名 | | mqtt_password | 连接mqtt服务器的密码 | | 注意 | 如果没有该字段则不设置该值,保持原值下发对有需要修改的值设置完成后,基站信息会自动上报一次 | #### 基站信息上报 基站发送给应用程序
主题: /client/${client}/report
指令内容: ```json { "i": 21, "cId": "2024010282", "m": { "f": 1, "mqtt_ip": "edunew.sunvotecloud.cn", "mqtt_port": 1883, "mqtt_username": "admin", "mqtt_password": "1223456789" } } ``` | 名称 | 备注 | | --- | --- | | i= 21 |基站信息上报 | | cId |客户端ID | | f |基站频点 | | mqtt_ip | 连接服务器的IP | | mqtt_port | 连接mqtt服务器的端口 | | mqtt_username | 连接mqtt服务器的用户名 | | mqtt_password | 连接mqtt服务器的密码 | #### 基站错误信息上报 基站发送给应用程序主题
5S 报一次
主题:/client/${client}/error
指令内容: ```json { "i": 22, "m": { "et": 1, "em": "3" } } ``` | 名称 | 备注 | | --- | --- | | i = 22 | 基站错误信息上报 | | et |错误类型 et = 1 :基站频点冲突 | | em |错误消息 em=3 :3号频点示例 基站频点3冲突 | ### 键盘管理 #### 键盘上线 由基站发给应用程序,应用需要订阅主题
主题: /client/${clientId}/card/online
指令内容: ```json { "i": 14, "m": [{ "c": "2126833671", "t": 1626016299 }] } ``` | 名称 | 备注 | | --- | --- | | i = 14 |键盘上线 | | c |键盘sn号 | | t |下线时间 时间戳单位秒 需要先校时| #### 读取白名单 应用程序发送给基站
主题: /lesson/class/${clinetId}/receive
指令内容: ```json { "i": 30 } ``` | 名称 | 备注 | | --- | --- | | i = 30 |读取白名单 | 基站发给应用程序主题
主题: /lesson/class/${clinetId}/receive
指令内容: ```json { "i": 31, "total": 1, "current": 1, "m": ["2126932551"] } ``` | 名称 | 备注 | | --- | --- | | i = 31 |白名单上传 | | total |一共上传的数据包数,这里分包原则参考指令属性m介绍说明及分包示例| | current |当前第几包 ,这里分包原则参考指令属性m介绍说明及分包示例| | m |键盘序列号列表 | #### 添加白名单 应用程序发送给基站
主题: /lesson/class/${clinetId}/receive
指令内容: ```json { "i": 3, "m": ["2126833703", "2126833671"] } ``` | 名称 | 备注 | | --- | --- | | i = 3 |添加白名单m内包含白名单键盘按编号 | | m |键盘序列号列表 | #### 删除白名单 应用程序发送给基站
主题: /lesson/class/${clinetId}/receive
指令内容: ```json { "i": 4, "m": ["2126833703", "2126833671"] } ``` | 名称 | 备注 | | --- | --- | | i = 4 |删除白名单m内包含白名单键盘按编号 | | m |键盘序列号列表 | #### 清空白名单 应用程序发送给基站
主题: /lesson/class/${clinetId}/receive
指令内容: ```json { "i": 5 } ``` | 名称 | 备注 | | --- | --- | | i = 5 |清空白名单 | ### 签到 #### 开启签到 由应用程序发给基站,应用按照主题发送
主题: /lesson/class/${clinetId}/receive
指令内容: ```json { "i": 12, "en": "6", "qm": 7, "m": { "tp": "pin", "content": "hex:cae4c8ebd5cbbac5cdeab3c9b0f3b6a8" } } ``` | 名称 | 备注 | | --- | --- | | i = 12 |下发答题 | | en |序号 | | qm = 7 |签到 | | tp |签到功能 目前支持传入固定值:"pin" 为pin码签到 | | content |签到标题,hex: 为内容的GB2312编码的hex内容 | #### 接收签到信息 由基站发给应用程序,应用订阅主题
主题: /lesson/class/${clinetId}/send
指令内容: ```json { "i": 13, "en": "6", "qm": 7, "c": "2126833671", "m": [{ "a": "6542", "t": 1626016599 }] } ``` | 名称 | 备注 | | --- | --- | | i = 13 | 签到数据 | | t |提交答案时间 时间戳单位秒 需要先校时| | a | 签到信息(签到码) | #### 停止签到 由应用程序发给基站,应用按照发送
主题:/lesson/class/${clinetId}/receive
指令内容: ```json { "i": 11, "en": "6" } ``` | 名称 | 备注 | | --- | --- | | i = 11 | 停止签到 | | en |停止对序号6的练习 | ### 单题 #### 开启答题 由应用程序发给基站,应用按照主题发送
主题: /lesson/class/${clinetId}/receive
指令内容: ```json { "i": 12, "en": "1", "qm": 1, "m": { "qt": 0, "on": 4, "content": "hex:d7d4b6a8d2e5b1eacce2" } } ``` | 名称 | 备注 | | --- | --- | | i = 12 |下发答题 | | en |序号 | | qm = 1 |单题模式 示例为单题模式的字母单选 | | qt |字母单选 | | on |选项数 | | content |题目主干内容,hex: 为内容的GB2312编码的hex内容 | ```java System.out.println(new String(hex2Bytes(bytesToHexString("李四".getBytes("GB2312"))),"GB2312")); public static String bytesToHexString(byte[] src) { StringBuilder stringBuilder = new StringBuilder(""); if (src == null || src.length <= 0) { return ""; } for (int i = 0; i < src.length; i++) { int v = src[i] & 0xFF; String hv = Integer.toHexString(v); if (hv.length() < 2) { stringBuilder.append(0); } stringBuilder.append(hv); stringBuilder.append(""); } return stringBuilder.toString(); } public static byte[] hex2Bytes(String hex){ if(hex.length() % 2 == 0){ byte[] ret = new byte[hex.length() / 2]; for(int i = 0 ; i< hex.length() / 2 ; i++){ ret[i] = (byte)Integer.parseInt(hex.substring(2*i,2*i+2),16); } return ret; } return null; } ``` #### 接收答题数据 由基站发给应用程序,应用订阅主题
主题: /lesson/class/${clinetId}/send

指令内容: ```json { "i": 13, "en": "1", "qm": 1, "c": "2126833671", "m": [{ "qn": 1, "a": "A", "t": 1626016299 }] } ``` | 名称 | 备注 | | --- | --- | | i = 13 | 答题数据 | | qn |上报答题数据,单题题号固定为1 | | t |提交答案时间 时间戳单位秒 需要先校时| | a | 答题数据 | #### 停止答题 由应用程序发给基站,应用按照发送
主题:/lesson/class/${clinetId}/receive
指令内容: ```json { "i": 11, "en": "1" } ``` | 名称 | 备注 | | --- | --- | | i = 11 | 停止答题 | | en |停止对序号1的练习 | ### 抢答 #### 开启答题 由应用程序发给基站,应用按照发送
主题: /lesson/class/${clinetId}/receive
指令内容: ```json { "i": 12, "en": "2", "qm": 4 } ``` | 名称 | 备注 | | --- | --- | | i = 12 | 开启答题 | | en |序号2的练习 | | qm | qm =4 抢答题 | #### 接收答题数据 由基站发给应用程序,应用订阅
主题: /lesson/class/${clinetId}/send
指令内容: ```json { "i": 13, "en": "2", "qm": 4, "m": [{ "c": "2126833671", "t": 1626016299 }] } ``` | 名称 | 备注 | | --- | --- | | i = 13 | 接收答题数据 | | en |序号2的练习 | | qm | qm =4 抢答题 | | c | 键盘编号 | | t |提交答案时间 时间戳单位秒 需要先校时| #### 停止答题 由应用程序发给基站,应用按照主题发送
主题:/lesson/class/${clinetId}/receive
指令内容: ```json { "i": 11, "en": "2" } ``` | 名称 | 备注 | | --- | --- | | i = 11 | 停止答题 | | en |停止对序号2的练习 | ### 多题题号多题 #### 开启答题 由应用程序发给基站,应用按照主题发送
主题: /lesson/class/${clinetId}/receive
指令内容: ```json { "i": 12, "en": "3", "qm": 14, "total": 1, "current": 1, "qs": 5, "m": [{ "qt": 0, "on": 4, "dn": "1" }, { "qt": 1, "on": 4, "dn": "2-1" }, { "qt": 2, "dn": "2-2" }, { "qt": 3, "dn": "2-2-1" }, { "qt": 7, "dn": "3" }] } ``` | 名称 | 备注 | | --- | --- | | i = 12 | 开启答题 | | en |12341练习 | | qm = 14 | 多题多级编号模式 | | current |当前第几包数据,这里分包原则参考指令属性m介绍说明及分包示例 | | total |一共几包数据 ,这里分包原则参考指令属性m介绍说明及分包示例| | qs | 题目总数 | | qt | 0:字母单选
1: 字母多选
2: 数字题
3: 判断题
7: 自判题| | on | 选项数量 | | dn | 显示编号,题目最多3级数字编号,多级数字编号中间必须以“-”隔开,每级最大数200 | #### 接收答题数据 由基站发给应用程序,应用订阅主题
主题: /lesson/class/${clinetId}/send
指令内容: ```json { "i": 13, "en": "3", "qm": 14, "c": "2126833671", "m": [{ "qn": 1, "a": "A", "t": 1626016299 }] } ``` | 名称 | 备注 | | --- | --- | | i = 13 | 接收答题数据 | | en |12341练习 | | qm = 14 | 多题多级编号模式 | | c | 键盘编号 | | qn |顺序题号,不为显示编号,是上述题目的顺序号 | | t |提交答案时间 时间戳单位秒 需要先校时 | | a | 答题数据 | #### 停止答题 由应用程序发给基站,应用按照主题发送
主题:/lesson/class/${clinetId}/receive
指令内容: ```json { "i": 11, "en": "3" } ``` | 名称 | 备注 | | --- | --- | | i = 11 | 停止答题 | | en |停止对序号12341的练习 | ### 基站升级 #### 开始升级
主题: /client/${client}/operate
指令内容: ```json { "i": 17, "m": { "url": "http://120.78.57.84:8888/file/C200K-V200.0.21.bin", "version": "200.0.21", "CRC": 2382669270 } } ``` | 名称 | 备注 | | --- | --- | | i = 17 | 基站升级 | | en |序号5 | | url |固件包地址,基站能访问到的路径 | | version |新固件的版本 | | CRC |固件文件的CRC值 | #### 升级进度报告
主题: /client/${client}/report
指令内容: ```json { "i": 18, "m": { "cId": "3411111156", "percent": 0, "status": 0 } } ``` | 名称 | 备注 | | --- | --- | | i = 18 | 升级进度报告 | | cId |基站编号 | | percent |升级百分比 | | status |升级状态信息。
0:未开始
1:升级中
2:升级成功
4:固件版本号错误
5:不在升级名单内
6:升级指令错误
\>127:升级中的错误码。 | ### 键盘升级 #### 开始升级
主题: /client/${client}/operate
指令内容: ```json { "i": 28, "m": { "url": "http://120.78.57.84:8888/file/S6_2.4G_2.1.2.50_GB2312_CGC_add_head.bin", "version": "2.1.2.50", "CRC": 10605263 } } ``` ### 文本下发 #### 文本下发
主题: /lesson/class/${clinetId}/receive
指令内容: ```json { "i": 60, "en": "6", "cmd": 1, "total": 1, "current": 1, "qs": 2, "m": [{ "sn": "2126833671", "text": "hex:d5c5c8fd" },{ "sn": "2126833672", "text": "hex:c0eecbc4" }] } ``` | 名称 | 备注 | | --- | --- | | i = 60 | 文本下发 | | sn |键盘列表 | | current |当前第几包 ,这里分包原则参考指令属性m介绍说明及分包示例| | total |一共多少包,这里分包原则参考指令属性m介绍说明及分包示例 | | qs |键盘总数 如果是多包,是多包总和 | | cmd |下发的命令
1: 姓名下发
10: 第二行文本
70: 生避字,生避字规则:内容为GB2312的hex编码,先是生避字的hex编码,紧接着是生避字的字符编码,生避字的字符编码为24个字节。一个生避字是26个字节52个字符(hex编码) 一包最多发3个生避字,如果超过3个生避字,需要多包发送
85: 自判题标题
87: 清除 | | text |标题信息,以hex:开头添加的内容 内容为GB2312的hex编码 | ```java System.out.println(new String(hex2Bytes(bytesToHexString("李四".getBytes("GB2312"))),"GB2312")); public static String bytesToHexString(byte[] src) { StringBuilder stringBuilder = new StringBuilder(""); if (src == null || src.length <= 0) { return ""; } for (int i = 0; i < src.length; i++) { int v = src[i] & 0xFF; String hv = Integer.toHexString(v); if (hv.length() < 2) { stringBuilder.append(0); } stringBuilder.append(hv); stringBuilder.append(""); } return stringBuilder.toString(); } public static byte[] hex2Bytes(String hex){ if(hex.length() % 2 == 0){ byte[] ret = new byte[hex.length() / 2]; for(int i = 0 ; i< hex.length() / 2 ; i++){ ret[i] = (byte)Integer.parseInt(hex.substring(2*i,2*i+2),16); } return ret; } return null; } ``` #### 文本下发返回
主题: /lesson/class/${clinetId}/send
指令内容: ```json { "i": 61, "cmd": 1, "m": [{ "sn": "2126833671", "success": 0 }] } ``` | 名称 | 备注 | | --- | --- | | i = 61 | 文本下发返回 | | sn |键盘号 | | cmd |下发的命令
1: 姓名下发
10: 第二行文本
70: 生避字,生避字规则(参考:生避字检测方法):内容为GB2312的hex编码,先是生避字的hex编码,紧接着是生避字的字符编码,生避字的字符编码为24个字节。一个生避字是26个字节52个字符(hex编码)
85:自判题标题
87:清除 | | success | 是否成功。
0 表示成功
-1 失败
-2 基站忙,应用程序需要等待一段时间再发送 | 注意:连续文本下发,需要等待上一条命令返回(成功或失败)后,再发送下一条指令。 ### 学生卡信息上报 卡初始化组网的时候会上报一次
主题:/client/${client}/report
指令内容: ```json { "i":32, "m":[ { "c":"1873373905", "cv":"0.2.2", "hw":"67.0", "t":1638865040 } ] } ``` | 名称 | 备注 | | --- | --- | | i = 32 | 学生卡信息上报 | | c |键盘编号 | | cv |固件版本号 | | hw |硬件型号 | | t | 时间戳 时间戳单位秒 需要先校时| ### 基站复位 应用程序发送给基站
主题: /lesson/class/${clinetId}/receive
指令内容: ```json { "i": 62 } ``` | 名称 | 备注 | | --- | --- | | i = 62 |基站复位 |