/OneNET-CoAP-Shell

Constrained Application Protocol (CoAP) Interactive Shell

Primary LanguageJavaApache License 2.0Apache-2.0

CoAP Shell for OneNET

这是一个改编自 CoAP Shell 项目的命令行工具,可以用于测试 OneNET Studio 平台的 CoAP 设备接入。

OneNET 官方提供了一个 Studio 模拟器,在模拟器中可以采用 CoAP 协议接入设备,但那个模拟器太自动化了,替用户完成了大量工作,体会不到 CoAP 交互的过程。本项目比起官方模拟器来,能使用户更深入地了解 CoAP 接入的细节。你可以把本项目看成是 CoAP 版的 POSTMAN。

编译

直接使用编译好的 compiled-jar\coap-shell-1.1.2-SNAPSHOT.jar 也行,可以跳过编译环节

克隆本项目:

git clone https://github.com/CQCET-IOT/OneNET-CoAP-Shell.git

使用 Maven package 打包之后,会自动下载完成所有依赖库。此时会有编译错误,提示找不到合适的方法,这是因为我修改了 californium-core-2.6.3.jar 中的 CoapClient.java 文件,添加了一个重载的 post(String, int, int, Token) 函数。

将项目中的 modified-jar\californium-core-2.6.3.jar 拷贝到 Maven 仓库中,替换自动下载的 californium-core-2.6.3.jar

比如我的依赖库会下载到 F:\ProgramEnv\mvnRepo\org\eclipse\californium\californium-core\2.6.3\californium-core-2.6.3.jar,其中:

  • 仓库路径 F:\ProgramEnv\mvnRepo 是在 Maven 配置文件 settings.xml 中配置的
  • 库路径 org\eclipse\californium\californium-core\2.6.3 是 Maven 按照库的 groupId, artifactId 和 version 三个字段创建的

替换完成之后,关闭 IEDA,重新打开项目,再次 package 打包,错误提示会消失,也能够编译打包成功。

使用

编译得到 coap-shell-1.1.2-SNAPSHOT.jar,在 cmd 窗口中使用下述命令将其启动起来:

java -jar coap-shell-1.1.2-SNAPSHOT.jar

运行后,会打开 CoAP Shell 窗口,这是一个命令行工具,其 Banner 如下:

  _____     ___   ___     ______       ____
 / ___/__  / _ | / _ \   / __/ /  ___ / / /
/ /__/ _ \/ __ |/ ___/  _\ \/ _ \/ -_) / /
\___/\___/_/ |_/_/     /___/_//_/\__/_/_/
CoAP Shell (v1.1.2-SNAPSHOT)
For assistance hit TAB or type "help".

server-unknown:>

首先需要连接到 OneNET 平台,通过下面指令进行连接:

connect coap://183.230.102.116:5683

连接成功后,就可以向服务器发送 GET/POST/PUT/DELETE 指令了。具体的指令参看 OneNET 官方文档

按文档要求,先进行设备登录:

post /$sys/XXXXXXXXXX/COAP-DEVICE/login --payload '{"lt":600,"st":"version=2018-10-31&res=products/XXXXXXXXXX/devices/COAP-DEVICE&sign=Lbdn9HNoqLBwajUYhVjsVMl4crY%3D&et=1645322317&method=sha1"}' --format 'application/json'

其中,登录操作需要使用 POST 指令,在命令行中就是 post 命令;URI 的格式为 $sys/{pid}/{device-name}/login,其中 piddevice-name 部分需要使用自己的 CoAP 设备进行替换。命令中为了安全起见,特意用 XXXXXXXXXX 来代替我的 pidpayload 部分是登录时需要提交给 OneNET 平台的 JSON 数据,其中的 st 字段需要使用 Token 计算器自行计算,Token 计算器可以从 这里下载

总之,这一步的命令行需要自行构建完毕,才能正确登录。登录成功以后会返回 Token 信息,如下:

----------------------------- Response -----------------------------
POST coap://183.230.102.116:5683/$sys/XXXXXXXXXX/COAP-DEVICE/login
MID: 38459, Type: ACK, Token: F82F6F4391D92C59, RTT: 20ms
Options: {"Location-Path":"$sys"}
Status : 201-Created, Payload: 8B
............................. Payload ..............................
69FA73187498C923
--------------------------------------------------------------------

此处 Payload 中返回的就是 Token,需要在后续命令中携带,比如上传设备属性数据时,就采用 --token 参数进行携带。但在继续执行指令之前,还需要确保 CoAP 设备的物模型中,拥有一个名为 Brand,类型为 string 的属性,下面的例子就是在更新 Brand 属性的数据。

post /$sys/XXXXXXXXXX/COAP-DEVICE/thing/property/post --payload '{"id": "53725","version": "1.0","params": {"Brand": {"value": "this is a string3"}}}' --format 'application/json' --accept 'application/json' --token '69FA73187498C923'
----------------------------- Response -----------------------------
POST coap://183.230.102.116:5683/$sys/XXXXXXXXXX/COAP-DEVICE/thing/property/post
MID: 38462, Type: ACK, Token: 69FA73187498C923, RTT: 82ms
Options: {}
Status : 205-Reset Content, Payload: 0B
............................. Payload ..............................
--------------------------------------------------------------------

可以看到,这个 POST 指令执行成功。此时前往 OneNET Studio 就可以看到刚刚上传的字符串了。

改造过程(供开发者参考)

CoAP Shell 原项目在不修改任何代码的情况下,是可以连接到 OneNET Studio 平台的,而且可以完成设备登录,平台上也会显示设备在线。

但是原项目将登录后服务器返回的 Token 按照 UTF-8 输出,显示为乱码(未能以十六进制打印),也不能手动设置报文的 Token。要想实现向 OneNET Studio 上报数据,就必须对其进行改造。

打印十六进制的 Token 比较简单,在 CoAP Shell 上修改就可以完成;手动设置报文的 Token,则必须修改 CoAP Shell 所依赖的 californium 库。

打印十六进制Token

src/main/java/io/datalake/coap/coapshell/util/PrintUtils.java 中增加下述函数,用于将 Token 按照十六进制打印输出:

/**
 * 字节流类型的payload,转化为十六进制进行显示
 * @param text
 * @return
 */
private static String prettyStream(byte[] text) {
	try {
		Token token = new Token(text);
		return token.getAsString();
	}
	catch (Exception e) {
		return text.toString();
	}
}

并且在 prettyPayload() 函数中增加一个 else if 判断,并且调用上面的 prettyStream()

public static String prettyPayload(Response r) {
	...
	else if (r.getOptions().toString().contains("$sys")) { /* OneNET登录返回此种类型的ACK,payload中为后续通信的Token */
		return cyan(prettyStream(r.getPayload()));
	}
	return r.getPayloadString();
}

这里修改完成以后,CoAP Shell 打印的 Token 就可以正常显示出来了。前面已经看到过 Token 输出的效果。

手动设置报文Token

CoAP Shell 依赖于 californium 2.6.3,因此要下载 californium 2.6.3 版本的代码。

查看代码发现,californium 重载了多种 post() 方法,但无一例外都使用了类似下面的方法:

public CoapResponse post(String payload, int format, int accept) throws ConnectorException, IOException {
    Request request = this.newPost();
    request.setPayload(payload);
    request.getOptions().setContentFormat(format);
    request.getOptions().setAccept(accept);
    this.assignClientUriIfEmpty(request);
    return this.synchronous(request);
}

这些 post() 方法中,都是首先初始化一个 Request 对象,然后设置 payload 和 option,但都没有设置 Token,所以需要重载一个新的 post() 方法才行,将 Token 作为参数传进去,在里面设置 Token 然后发送出去。

修改 org/eclipse/californium/core/CoapClient.java,在其中加入重载的 post(String, int, int, Token) 函数,该函数支持携带 Token 作为参数,修改后的 CoapClient.java 放到了 这里,L602-L623 就是重载的 post() 函数,内容如下:

/**
 * Sends a POST request with the specified payload, the specified content
 * format and the specified Accept option and blocks until the response is
 * available.
 *
 * @param payload the payload
 * @param format the Content-Format
 * @param accept the Accept option
 * @param token token returned by OneNET
 * @return the CoAP response
 * @throws ConnectorException if an issue specific to the connector occurred
 * @throws IOException if any other issue (not specific to the connector) occurred
 */
public CoapResponse post(String payload, int format, int accept, Token token) throws ConnectorException, IOException {
	Request request = newPost();
	request.setToken(token);
	request.setPayload(payload);
	request.getOptions().setContentFormat(format);
	request.getOptions().setAccept(accept);
	assignClientUriIfEmpty(request);
	return synchronous(request);
}

对 californium 库的修改,只需要添加上面的重载函数就完成了。不过重新编译这个库有点小麻烦。

起初设想是重新完整编译 californium 2.6.3 版本的代码,重载后重新编译得到 jar 包,再手动安装到 Maven 仓库中。但后来发现,2.6.3 版本的代码并没有严格对应 POM 中依赖那个 2.6.3 版本库,它编译出来是 2.6.0 版本的 jar 包。

也不想多花时间验证其他版本了。所以我只能进行局部替换,把 2.6.3 源码编译出来的 CoapClient.class 手动替换到 Maven 自动下载的 californium-core-2.6.3.jar 库中。理论上这样做可以了。但是 jar 包对 class 文件做了 RSA 校验,因此还需要把 META-INF 目录下的 *.RSA, *.SF 文件删除掉,强制程序在执行时不进行校验。这就是本项目 modified-jar\californium-core-2.6.3.jar 的由来。

依赖库增加了设置 Token 功能,回过来,CoAP Shell 中就需要调用这个功能。

修改 CoAP Shell 的 src/main/java/io/datalake/coap/coapshell/command/CoapShellCommands.java 文件中 POST 命令的逻辑,增加一个 Token 参数,并且调用前面重载的 post() 函数。

public String post(
        ...
		@ShellOption(defaultValue = ShellOption.NULL, help = "OneNET Token") String token) throws IOException, ConnectorException {

        ...

		} else {
		    CoapResponse response;
		    if (token == null) {
				response = coapClient.post(payloadContent, coapContentType(format), coapContentType(accept));
			} else {
				Token realToken = new Token(StringUtil.hex2ByteArray(token));
				response = coapClient.post(payloadContent, coapContentType(format), coapContentType(accept), realToken);
			}

			result.append(PrintUtils.prettyPrint(response, requestInfo("POST", baseUri + path, async)));
		}
	}
	
	...
}

最后在执行上报属性的指令时,增加一个 --token 参数,携带上登录返回的十六进制 Token 值,就可以啦!