RF接口 - 版本1
RF接口消息格式
RF接口 是 RC 和 服务设备之间的 通讯接口。
RC 和 服务设备之间 是TCP 长连接。RC 是服务端,设备是客户端
生产环境 TCP上有TLS传输层加密, 测试环境可以不用TLS。
每个消息以 字节 04
(ASCII码 EOT
end of transmission)作为结束符,也就是消息边界
消息体内容是JSON格式的编码。
目前消息内容字符都是ascii范围之内的。
如果消息中有中文字符,编码时,采用unicode escape, 防止消息中出现 04字节,搞乱边界
消息格式示例如下,为消息头加消息体,以 字符$
作为分隔符
RF1|device-auth-req|0|RC1665579494.551XPR${
"device_sn" : "aaaaaaaa"
"cur_time" : "202212031335",
"auth_code" : base64( SHA256(sharedsecrete + currentdatetime) ),
}
消息头
消息头存放消息类型,以 $
结束
消息头中内容以 |
分隔,依次为 协议版本、消息类型、是否重发消息、消息编号
协议版本
当前协议版本为1, 表示为 RF01
消息类型
描述当前消息的类型,比如
设备认证请求: device-auth-req
设备认证响应: device-auth-anw
心跳消息请求: heart-beat-req
心跳消息响应: heart-beat-anw
获取设备信息请求: get-dev-info-req
获取设备信息响应: get-dev-info-anw
在后面业务流程介绍中,会有各个阶段涉及到消息的类型说明
是否重发消息
表示该消息是首次发送还是重发的消息
0 为正常消息, 1 为重发消息
消息编号
用一个无符号整数,作为消息的编号,用来标记消息。
响应消息必须和请求消息使用同一编号
RC 请求消息的编号前缀为 RC 设备 请求消息的编号前缀为 DV
消息编号要保证唯一
消息的编号方式推荐格式: DEV + 当前时间戳unix时间(到微秒), 比如,DEV1670243387.279147
业务消息如果响应超时,必须重发,重发消息的 编号 和前面的消息一致,并且重发消息头标记。
当接收方发现是重发消息时,应该检验消息体内的业务序列号,确定是否已经处理过。
消息体
消息头后面是消息体,是json的对象格式
连接和认证
设备、RC 互相认证
设备启动后,和RC创建tcp连接后, 第一个消息必须是认证消息
该消息中携带 当前时间(是 UTC
时间,不是本地时间),认证码,
认证码是
- 共享密钥+当前时间(年月日时分秒,比如20060102150405)
- 进行 SHA-256 哈希值计算的结果字节串
- 再转化为 base64字符串
用伪代码描述就是: base64( SHA256(sharedsecrete + currentdatetime) )
# 认证请求
RF01|device-auth-req|0|DEV1670243387.279147${
"device_sn" : "aaaaaaaa",
"cur_time" : "20221203133555",
"auth_code" : "2JlNex2ljX6brBngJt1JWo+uxsN/1AiLb9vKhsXd10Y="
}
服务端接收到后, 首先判断当前时间是否和本地当前时间差别不超过2秒,
如果超过了, 返回时间戳错误
如果是, 也使用同样的算法(使用本地保存的该设备的共享密钥)产生 认证码, 看看是否一致
如果一致则认证通过
并且以 设备上报的当前时间的倒序字符串,使用同样机制创建 认证码
用伪代码描述就是: base64( SHA256(sharedsecrete + reverse(currentdatetime)) )
然后,把验证码 包含在响应中,返回给设备。
# 认证响应
RF01|device-auth-anw|0|DEV1670243387.279147$ {
"ret" : 0,
"auth_code" : base64( SHA256(sharedsecrete + reverse
(currentdatetime)) )
}
设备收到后,首先应该校验返回响应消息中的 ret 字段值是否为 0 ,为0, 表示服务端认为设备是合法的,
然后,同样的方法也校验 RC的验证码, 如果不通过,断开连接
双向校验成功
心跳
# 心跳请求
RF01|heart-beat-req|0|DEV1670245064.844079${
"cur_time" : "20221205125744",
}
# 心跳响应
RF01|heart-beat-anw|0|DEV1670245064.844079${
"cur_time" : "20221205125744",
}
设备双向认证成功后,设备每空闲(无任何消息)10分钟左右,发送一个心跳消息给RC
里面的 cur_time是当前时间:年月日时分秒
RC 收到后,回复心跳响应消息 给设备
业务流程
服务开始阶段
-
用户微信支付宝扫码打开设备对应的操作页面,选定充电桩充电口
手机端要求先获取设备信息, 请求发送给 RC,
RC 经过必要的处理后,发送请求给
获取设备信息请求
消息 设备,消息格式如下RF01|get-dev-info-req|0|1670243752650046${ "device_sn" : "aaaaaaaa" }
设备返回给RC的
获取设备信息响应
消息,格式如下RF01|get-dev-info-anw|0|1670243752650046${ "ret": 0, "deviceHealth": 0, // 0: 正常 , 1:异常, 2: 未知 "devicePorts" : [ {"no": 1 ,"state": "free"}, {"no": 2 ,"state": "using"}, {"no": 3 ,"state": "free"}, {"no": 4 ,"state": "free"}, {"no": 5 ,"state": "free"}, {"no": 6, "state": "free"} ], }
-
然后,用户选择服务端口(比如充电口、洗车位),
通过RC发送
开始使用设备服务请求
消息 给对应设备,比如RF01|user-start-svc-req|0|1670243895569155${ "device_sn" : "aaaaaaaa" "port_no" : 4 # 充电口号码 }
-
设备收到通知后,
如果确定服务可行, 回复
开始使用设备服务响应
给RC,响应消息 格式如下
RF01|user-start-svc-anw|0|1670243895569155{ "ret" : 0, # 为0表示成功, "desc": "charging start", "devicePorts" : [ {"no": 1 ,"state":"free"}, {"no": 2 ,"state":"free"}, {"no": 3 ,"state":"free"}, {"no": 4 ,"state":"using"}, {"no": 5 ,"state":"free"}, ], }
响应码 ret 不为0表示失败 ,
devicePorts 表明 当前各端口的充电情况
如果服务不可行,比如 未插入电源、 设备损坏,当前充电口失灵、已经被其它用户抢先使用
回复拒绝服务响应,格式如下
RF01|user-start-svc-anw|0|1670243895569155{
"ret" : 1, # 不为0表示失败
"desc" : "port damaged", # 描述失败原因
"devicePorts" : [
{"no": 1 ,"state":"free"},
{"no": 2 ,"state":"free"},
{"no": 3 ,"state":"free"},
{"no": 4 ,"state":"free"},
{"no": 5 ,"state":"free"},
],
}
服务进行阶段
目前只有洗车机设备,在服务进行阶段有业务消息交互
用户按下洗车机上的3种按钮之一:水洗、泡沫、吸尘
分别触发不同类型的服务消息
设备上报 使用 服务操作请求
消息给RC,格式如下
RF01|dev-start-op-req|0|DEV1670244720.3887978${
"device_sn" : "cccccccc",
"port_no" : 4, # 洗车位号码
"svc_type" : "water" # 使用服务类型:water: 水洗、foam: 泡沫、clean: 吸尘
}
RC 进行相应处理(比如计费)并回复 服务操作响应
消息给 设备
RF01|dev-start-op-anw|0|DEV1670244720.3887978${
"ret":0
}
设备在某项洗车操作进行中,也可以发 暂停服务操作请求
消息
RF01|dev-stop-op-req|0|DEV1670244804.6238596${
"device_sn" : "cccccccc",
"port_no" : 4
}
RC 进行相应处理 并回复 暂停服务操作响应
消息给 设备
RF01|dev-stop-op-anw|0|DEV1670244804.6238596${
"ret":0
}
服务结束阶段
-
如果手机侧出现服务结束事件
就是用户手机上 点击停止服务
手机前端发送请求给RC,
RC进行必要处理,然后发送
用户结束服务请求
消息 给对应设备,比如RF01|user-stop-svc-req|0|1670244561566376${ "device_sn" : "aaaaaaaa", "port_no" : 4 # 充电口号码 }
-
设备收到通知后,进行相应处理
比如充电站对应充电口进行断电,洗车机停止服务
并回复
结束服务响应
消息给RC,格式如下RF01|user-stop-svc-anw|0|1670244561566376${ "ret" : 0, # 为0表示成功 "desc" : "user stop charging", # 描述 "devicePorts": [ {"no": 1, "state": "free"}, {"no": 2, "state": "free"}, {"no": 3, "state": "free"}, {"no": 4, "state": "free"}, {"no": 5, "state": "free"}, {"no": 6, "state": "free"} ]} }
也有可能有些异常情况,比如不能停止停止服务
回复响应如下
user-stop-svc-anw|0|RC1665579494.551XPR${ "ret" : 2, # 不为0表示失败 "desc" : "" # 描述 }
-
如果设备侧出现服务结束事件
比如:充电站充满电,或者用户充电过程中拔出电源插头
再比如: 洗车机设备感知停靠车开走
设备上报
设备结束服务请求
消息给RC,格式如下RF01|dev-stop-svc-req|0|DEV1670245384.5449173${ "device_sn": "aaaaaaaa", "port_no": 1, "desc": "拔出插头" }
RC 进行相应处理,比如 进行计费、扣费处理产生账单等
并且返回
设备结束服务响应
消息RF01|dev-stop-svc-anw|0|DEV1670245384.5449173${ "ret":0 }
系统数据
目前RC系统中有如下 设备,可以使用
充电设备
设备编号:aaaaaaaa
共享密钥: hypih74vidyig771hsce6utu4v5tn4rl
充电设备
设备编号:bbbbbbbb
共享密钥:yfymn3vyoxxiamxagu4btsek6qezfprt
洗车设备
设备编号:cccccccc
共享密钥:a4pqk4dcxnjvmoj81no9n6rn6hcujs8i