开发一个控制硬件的安卓app(2)——指令生成与状态解析

偷懒了好久,没有更新,挖坑不填不太好呀。

上次说到总体的需求,按理来说应该由上至下分析一遍,把程序功能理清楚,再决定使用什么软件结构来完成程序的设计。为了说明软件结构时不显得太空,先从具体实现开始写吧。

1  通讯协议介绍

如何用安卓app来控制设备呢?首先就要提及通讯协议这个概念了。我所使用的这套硬件设备有着一套自己的”密码本”,只要我将指令以符合”密码本”的格式发送给设备,设备就会按照给出的指令行动。这里的”密码本”就是上面提到的通讯协议。不过不同设备的具体指令信息或多或少有所不同,大家参考一下就好了。下面给出这一套设备的通讯协议格式,请注意指令使用16进制描述。

表1 通信协议基本格式

具体指令信息(共15位)

D0 ~ D8

0x2E

T1

T2

T3

0x0D 0x0A

位置

说明

D0 ~ D8

为信息位

0x2E

小数点,起分隔作用

T1

发送方地址值,发送数据的站点地址,即源地址,指明由谁发出的数据

T2

接收方地址值,接受数据的站点地址,即目的地址,指明数据是发给谁

T3

为校验位,简单的数据校验,计算方法如下:D0...D8,T1,T2共11个数字的和,取其个位数,十六进制范围在0x30~0x39之间(数字0~9)

0x0D 0x0A

为结束标志标识位,用回车换行表示命令结束

为了识别指令是从哪发到哪的,指令中使用T1与T2来标记发送方和接收方,地址定义如下:

表2 通讯协议中的地址

主站

工控机站点

0X30

从站

堆垛机

0X31

输送线

0X32

AGV小车

0X33

分拣线

0X34

电子标签

0X35

加工

0X36

机器人1

0X37

机器人2

0X38

AGV小车2

0X39

比如说你想控制小车的动作,你的指令中T1为0X30,T2为0X33。而小车给你返回的状态信息中的T1为0X33,T2为0X30。

2  具体指令介绍

虽然看着上面的命令很复杂,但是自动分拣系统的指令非常简单,只有4个控制指令和4个返回状态,下面给出自动分拣系统的通讯协议,讲一讲怎么看懂这个通讯协议。

主站-->分拣线命令

T1=0x30,T2=0x34,D0、D1定义如下(其它位为0):

表3 分拣线命令

D0取值

功能操作

说明  D1~D8取值

1

0x31

启动

D1={0,1,2},其中:

0表示不动作

1表示分拣口1

2表示分拣口2

2

0x36

停止

D1~D8全为0

看到陌生的东西不要觉得是天书,这个表格的表述其实十分明确。对于分拣线的启动指令,我们一位一位看下来,也就15位。下面的进制转换是按ascii码进行的。

D0:按表3的说法D0取0X31(16进制),也就是"1"(ascii码);

D1:按表3的说法D0取"0"(ascii码),也就是0X30(16进制);

D2-D8:看表3前面一句话,D0、D1定义如下(其它位为0),所以这几位都是0X30(16进制),也就是"0"(ascii码);

T0:按表1描述,固定为0X2E(16进制),对应为"."(ascii码);

T1:按表1和2,指令从上位机发送,所以该位为0x30(16进制),对应"0"(ascii码);

T2:和上面相同,值为0x34(16进制),对应"4"(ascii码);

T3:这是校验位,把前面除了T0的相加取个位,简单算一下就是1+0+0+0+0+0+0+0+0+0+4=5,那么就是"5"(ascii码),也就是0X35(16进制);

最后两位:按表1描述这是固定值0x0D 0x0A(16进制),也就是"\r\n"(ascii码),就是回车换行啦。
很好,这样一条指令就出来了。

16进制表示0X31 0X30 0X30 0X30 0X30 0X30 0X30 0X30 0X30 0X2E 0X30 0X34 0X35 0X0D 0X0A
字符串表示"100000000.045\r\n"
对于本设备来说,两种表示都可以,不过ascii码不是所有16进制数都能表示,所以编程时最好使用16进制来操作指令。
大家可以用其它指令来练习一下,是不是看懂了就很简单呢?

具体实现起来就是这样的:

表4 具体分拣线指令

操作

指令

启动

0X31 0X30 0X30 0X30 0X30 0X30 0X30 0X30 0X30 0X2E 0X30 0X34 0X35 0X0D 0X0A

或”100000000.045\r\n”

向分拣口1运送

0X31 0X31 0X30 0X30 0X30 0X30 0X30 0X30 0X30 0X2E 0X30 0X34 0X36 0X0D 0X0A

或”110000000.046\r\n”

向分拣口2运送

0X31 0X32 0X30 0X30 0X30 0X30 0X30 0X30 0X30 0X2E 0X30 0X34 0X37 0X0D 0X0A

或”120000000.047\r\n”

停止

0X36 0X30 0X30 0X30 0X30 0X30 0X30 0X30 0X30 0X2E 0X30 0X34 0X30 0X0D 0X0A

或”600000000.040\r\n”

分拣系统状态反馈数据

T1=0x34,T2=0x30,D0定义如下(其它位为0):

表5 分拣线状态说明

D0取值

状态说明

1

0x31

货物分向分拣口1

2

0x32

货物分向分拣口2

3

0x36

分拣系统已停止运行

4

0x38

分拣系统空闲,完成一次分拣自动变为空闲

这边和上面指令的分析差不多,只是地址交换了一下。状态反馈就是下位机把当前自己的状态返回给上位机,便于上位机进行协调。

3  指令生成与状态解析的Java实现

下面在eclipse中实现一下这个指令的生成与状态的解析

public class AutoSortLine {

	// 用于存放15位指令
	protected byte[] cmd = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x30, 0x34, 0x00, 0x0D, 0x0A };
	protected boolean isBusy = false;

	// 构造方法初始化
	public AutoSortLine() {
		for (int i = 0; i < 9; i++) {
			cmd[i] = 0x30;
		}
	}

	// 启动
	public byte[] start() {
		cmd[0] = 0x31;
		cmd[1] = 0x30;
		countCheck();
		return cmd;
	}

	// 停止
	public byte[] stop() {
		cmd[0] = 0x36;
		cmd[1] = 0x30;
		countCheck();
		return cmd;
	}

	// 1口出
	public byte[] out1() {
		cmd[0] = 0x31;
		cmd[1] = 0x31;
		countCheck();
		return cmd;
	}

	// 2口出
	public byte[] out2() {
		cmd[0] = 0x31;
		cmd[1] = 0x32;
		countCheck();
		return cmd;
	}

	/**
	 * 翻译通信协议
	 *
	 * @param msg
	 *            收到的状态信息
	 * @return 返回应显示的状态信息
	 */
	public String receive(String msg) {
		switch (msg.replaceAll(" ", "")) {
		case "100000000.405\r\n": // 注意\r\n
			isBusy = true;
			return "货物分向分拣口1";
		case "200000000.406\r\n":
			return "货物分向分拣口2";
		case "600000000.400\r\n":
			return "分拣系统已停止运行";
		case "800000000.402\r\n":
			isBusy = false;
			return "分拣系统空闲";
		default:
			isBusy = false;
			break;
		}
		return "";
	}

	// 查询设备是否繁忙
	public boolean isBusy() {
		return isBusy;
	}

	// 计算校验位
	public void countCheck() {
		byte check = 0;
		for (int i = 0; i < 12; i++) {
			if (i != 9) {
				check = (byte) (check + cmd[i] - 0x30);
			}
		}
		cmd[12] = (byte) ((check % 10) + 0x30);
	}

	// 用来测试指令生成和状态解析的效果
	public static void main(String[] args) {
		AutoSortLine asl = new AutoSortLine();
		System.out.println("指令生成,请注意每个指令结束有额外的换行");
		System.out.println("strat code is " + new String(asl.start()).equals("100000000.045\r\n"));
		System.out.println("start:" + new String(asl.start()));
		System.out.println("stop code is " + new String(asl.stop()).equals("600000000.040\r\n"));
		System.out.println("stop:" + new String(asl.stop()));
		System.out.println("out1 code is " + new String(asl.out1()).equals("110000000.046\r\n"));
		System.out.println("out1:" + new String(asl.out1()));
		System.out.println("out2 code is " + new String(asl.out2()).equals("120000000.047\r\n"));
		System.out.println("out2:" + new String(asl.out2()));
		System.out.println("状态解析,请注意每个状态结束有额外的换行");
		System.out.println(asl.receive("100000000.405\r\n"));
		System.out.println(asl.receive("200000000.406\r\n"));
		System.out.println(asl.receive("600000000.400\r\n"));
		System.out.println(asl.receive("800000000.402\r\n"));
	}

}

运行后输出

指令生成,请注意每个指令结束有额外的换行
strat code is true
start:100000000.045

stop code is true
stop:600000000.040

out1 code is true
out1:110000000.046

out2 code is true
out2:120000000.047

状态解析,请注意每个状态结束有额外的换行
货物分向分拣口1
货物分向分拣口2
分拣系统已停止运行
分拣系统空闲
这就完成了指令生成与状态解析的Java实现。


阅读更多

没有更多推荐了,返回首页