跳转至

RF接口 - 版本1

RF接口消息格式

RF接口 是 RC 和 服务设备之间的 通讯接口。

image


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