package com.sunvote.cmd.app; import com.sunvote.cmd.BaseCmd; import com.sunvote.cmd.ICmd; import com.sunvote.cmd.push.PushBaseCmd; import java.io.ByteArrayOutputStream; import java.util.Arrays; /** * Created by Elvis on 2017/8/29. * Email:Eluis@psunsky.com * 版权所有:长沙中天电子设计开发有限公司 * Description:平板模块键盘投票功能模块 * * 6.1 概述 下载多包类主要用于下载一批数据。例如项目名称表、评议规则表等。 我们统一使用《广播式下载方式》来下载多包(即数据表),具体原理参见《表决系统通讯协议-应用文档-原理》。 6.2 进入和退出下载状态 通知表决器进入下载模式的好处: 1、在重新下载未成功的表决器时候,仅通知未成功的进入下载状态,其他成功的表决器就可以不处理下载数据; 2、能立即知道是否在线,表决器可以准备擦除FLASH,V4.52对于支持文件下载的就按文件名称创建文件; 3、退出时,表决器能知道下载完成。 字节 标识符 描述 1 DOWNCMD 0x40 下载多包类指令 2-3 KEYID 表决器编号,2字节,高位在前 一般要指定表决器编号使指定键盘进入或退出下载状态 但0x0000广播时候,键盘也执行 FF01到FFFE时候,是用序列号指定键盘 V4.74 低位字节01-FE,表示下载次序,由SDK管理,新下载变化一次, 键盘收到的时候,记录下,同时,此次序也出现在下载数据包中,这样键盘可判断是否是我可以下载的数据,避免上次下载状态没正常退出而错误接受数据 4 DOWNCMD 1 进入或退出下载状态 5 DOWNTYPE 多包类型 6 DOWNID 数据包标识码 7 DCMD 模式 1进入下载 0退出下载 8-24 FILENAME 在DOWNTYPE=40下载文件模式时候,是下载文件名称,16字符 其他模式参数无意义 SN 序列号模式时候,6字节键盘序列号,用于指定键盘 暂不支持文件下载 表决器回应结果状态: 字节 标识符 描述 1 DOWNCMD 0xC0 下载多包类指令应答 2-3 KEYID 表决器编号,2字节,高位在前 不是0000也不是FFFF,是要询问的表决器的编号 FFFF 表示用SN号代表键盘 4 DOWNCMD 1回应下载状态 5 DOWNTYPE 多包类型 6 DOWNID 数据包标识码 7 DCMDACK 现在模式 1已经进入下载模式 0已经退出下载模式 8-24 SN 使用SN号模式时候,键盘的SN,6字节 否则参数无意义 6.3 广播式下载数据包和查询状态 具体原理参见《表决系统通讯协议-应用文档-原理》。 下载数据包指令结构如下: 字节 标识符 描述 1 DOWNCMD 0x40 下载多包类指令 2-3 KEYID 表决器编号,2字节,高位在前 0xFFFF的时候是部分表决器下载,是常用模式,进入下载模式的表决器才处理数据 0x0000时候是广播下载,所有表决器都接收和处理 其他值是指定下载,编号和KEYID相同的才处理数据 FF01到FFFE时候,是序列号模式 V4.74 低位字节和进入下载模式时候的低位字节相同,用于键盘判断是否是当前下载数据 4 DOWNCMD 2 广播式下载具体数据 5 DOWNTYPE 多包类型 6 DOWNID 数据包标识码 文件下载模式时候是最高位地址 V4.52 由于非文件下载模式PACKH加上PACKL,只能下载65536字节,文件下载模式为支持大文件,用DOWNID作为高位地址,可以下载16384K字节 7 PACKH 数据段编号,0-255 8 PACKL 数据片编号, 0-15 9-24 PACKDATA 16字节的数据 备注: 1、 投票器先判断PACKH是否发生变化,发生变化就表示新的16片数据下载开始了,要把标志16片段下载成功状态的OKBITS全置1; 2、 然后计算地址,把PACKDATA16字节数据写到指定位置,对于非文件下载,地址=(PACKH*16+PACKL)*16,对于文件下载,地址=((DOWNID*256+PACKH)*16+PACKL)*16; 3、 然后把OKBITS中对应PACKL的比特位置0表示已经对应片段下载成功,用于应答下面的下载状态询问指令 询问下载成功状态指令结构: 字节 标识符 描述 1 DOWNCMD 0x40 下载多包类指令 2-3 KEYID 表决器编号,2字节,高位在前 使用键盘编号模式时候,不是0000也不是FFFF,是要询问的表决器的编号 FFFF,使用SN号询问键盘 4 DOWNCMD 3 询问广播式下载的执行状态 5 DOWNTYPE 多包类型 6 DOWNID 数据包标识码 7 PACKH 数据段编号 8-24 SN 指定键盘的SN号,6字节 参数无意义 表决器回应下载成功状态: 字节 标识符 描述 1 DOWNCMD 0xC0 下载多包类指令应答 2-3 KEYID 表决器编号,2字节,高位在前 不是0000也不是FFFF,是要询问的表决器的编号 FFFF,使用SN号回答 4 DOWNCMD 3 回应广播式下载的执行状态 5 DOWNTYPE 多包类型 6 DOWNID 数据包标识码 7 PACKH 数据段编号 8-9 OKBITS_L OKBITS_H 下载成功状态,共16Bit,代表0-15号数据片,Bit=0表示下载成功 注意是低位字节在前,例如OKBITS_L=3,OKBITS_H=128时候,表示第1、2、16个数据片没下载成功,即0、1号和15号数据片不成功 10-24 SN 指定键盘的SN号,6字节 不是SN模式时参数无意义 6.4 数据包类型分类 通过DOWNTYPE和DOWNID两个字节的组合,可以决定下载数据包的类型。 下面列出目前使用的几种类型。具体数据包的格式和含义,请参见《数据表规范-政务商务》。 DOWNTYPE值 DOWNID值 数据包类型 1 1 固定编号项目名称表 1 2 随机名称编号表 1 3 二维评测指标名称表 2 1 评议规则表 2 2 评议规则说明信息表 3 1 评分规则表 3 2 评分规则说明信息表 10 变化 即时信息 11 变化 短消息 12 页号 屏幕点阵显示信息 12 1或2 股东信息 20 1 空闲液晶信息 20 2 开机信息 30 1 汉字自造字字库,16x16点阵 31 1 多国语言资源包(未实现) 40 高位地址 指定文件名称(可含目录)下载文件 50 xPad的透传多包信息 6.4.1 关于即时信息和短信的特殊说明 即时信息定义为马上显示在屏幕上的信息,不保存,看完即丢。 短信定义为保存的,可查看,即时显示或提醒。 下载数据包指令结构不变,但DOWNID和PACKH含义不同: 字节 标识符 描述 1 DOWNCMD 0x40 下载多包类指令 2-3 KEYID 表决器编号,2字节,高位在前 0x0000时候是广播发信息,所有表决器都接收和处理,不需进入下载模式 0xFFFF的时候是对多个表决器发信息,已经进入下载模式的才处理 其他值是指定下载,编号和KEYID相同的才接收信息 4 DOWNCMD 2 广播式下载具体数据 5 DOWNTYPE 固定为10即时信息或11短信 50是xPad透传多包信息,一般是字符串信息 6 DOWNID 发一次信息就变化一次,表决器据此可知道是新的信息来了 7 PACKH 数据片总数,0表示1,F表示最多16片,也就是所短信息最多256字符,但一般限定64字符以内 8 PACKL 0-15,当前发送的数据片的编号 9-24 PACKDATA 16字节的数据 对于短信接收,表决器只要判断DOWNTYPE为10或11,然后DOWNID和以前的不同,就知道是新短信过来,并根据PACKH知道信息的长度,建立新的下载状态位。 然后根据PACKH判断所有片段是否都接收完毕,可以自动显示。 对于广播给所有表决器的信息,表决器不需单个单个进入下载模式,由SDK控制各片段信息多广播几次,表决器自己判断接收完整就显示。 对应指定单个表决器发信息,为提高速度,不对表决器执行进入下载模式,但要查询下载状态,确保信息完整下载,并且不执行退出下载模式。 对应指定多个表决器发信息,就必须进入下载模式,然后下载,然后查询,为提高下载速度,对表决器不执行退出下载模式。 所以,因为不执行退出下载状态,要注意的是,表决器要对短信模式下的进入下载状态要单独处理,避免和指定多个的表决器下载项目名称表等其他操作搞混。 6.4.2 桌牌点阵显示信息数据格式 桌牌点阵显示信息一般是按规则排布的点阵亮灭二进制字节数据。例如96x32点阵的显示屏,实际的数据是96x32/8=384字节。为保证以后点阵屏幕变更,在实际点阵数据前加16字节的格式说明。 字节 标识符 描述 1 TYPE 点阵格式 1 96x32点阵,(排列方式?) 2-16 根据点阵格式有不同含义,暂时为空 17-… BITMAP 显示点阵数据,根据不同格式有不同的长度 这样,如果格式为1,实际要传输的数据是16+384=400字节。 6.4.3 股东信息 特定型号需要显示股东姓名等信息。 请参考6.4.1节短信,下载方法一样,就是国外应用需要考虑unicode编码,所以DOWNID来标志。 字节 标识符 描述 1 DOWNCMD 0x40 下载多包类指令 2-3 KEYID 表决器编号,2字节,高位在前 0x0000时候是广播发信息,所有表决器都接收和处理,不需进入下载模式 0xFFFF的时候是对多个表决器发信息,已经进入下载模式的才处理 其他值是指定下载,编号和KEYID相同的才接收信息 FF01到FFFE时候,是用序列号指定键盘 V4.77 (采用SN号模式下载股东信息时候,键盘先进入下载状态,参见6.6节序列号下载多包方法,和6.2节进入和退出下载状态) 4 DOWNCMD 2 广播式下载具体数据 5 DOWNTYPE 12 股东信息 6 DOWNID 0 GB2312编码 1 UTF8编码 2 Unicode编码 7 PACKH 数据片总数,0表示1,F表示最多16片,也就是所短信息最多256字符,但一般限定64字符以内 8 PACKL 0-15,当前发送的数据片的编号 9-24 PACKDATA 16字节的数据 6.5 简单下载模式(不使用) 简单下载方式只是作为解决方案提出,因为效率比广播式低,没有采用。 下载数据包指令结构如下: 字节 标识符 描述 1 DOWNCMD 0x40 下载多包类指令 2-3 KEYID 表决器编号,2字节,高位在前 必须指定一个具体的表决器编号 4 DOWNCMD 4 简单下载模式 5 DOWNTYPE 多包类型 6 DOWNID 数据包标识码 7 PACKH 数据段编号 8 PACKL 0-7,数据片编号 9-24 PACKDATA 16字节的数据 表决器回应下载成功状态: 字节 标识符 描述 1 DOWNCMD 0xB0 下载多包类指令应答 2-3 KEYID 表决器编号,2字节,高位在前 不是0000也不是FFFF,是要询问的表决器的编号 4 DOWNCMD 4 回应下载状态 5 DOWNTYPE 多包类型 6 DOWNID 数据包标识码 7 PACKH 数据段编号 8 PACKL 0-7,数据片编号 9 STATUS 1 成功 0 不成功 10-24 参数无意义 6.6 序列号下载多包 键盘使用序列号模式时,也分广播、指定多个、指定单个键盘下载。 指定多个,修改了6.2节指定键盘进入下载状态和退出下载状态,可用SN号指定,同样,6.3节的询问下载结果,修改为同样支持SN号。而下载数据,仍然沿用原来的ID=FFFF,在下载模式的才接受。 指定单个,包含到指定多个里面,这样,即使单个发短信,也要先进入下载模式。 广播,则沿用ID=0000的原来模式。 然后,发现一个问题,如果上次键盘没能正常退出下载模式,就有可能错误接受数据,包括原来使用键盘编号的,都有这个可能。所以,进入和退出下载模式,广播数据时,原来的FFFF改为FF01到FFFE,低字节01-FE表示次序,SDK管理,下载一次改变一次,这样键盘就能判断数据是不是这次的,同时如果不是,键盘改自己次序为0。 * */ public class MutiPkgDownCmd extends BaseCmd { /*** * 下载数据包指令结构如下: 字节 标识符 描述 1 DOWNCMD 0x40 下载多包类指令 2-3 KEYID 表决器编号,2字节,高位在前 0xFFFF的时候是部分表决器下载,是常用模式,进入下载模式的表决器才处理数据 0x0000时候是广播下载,所有表决器都接收和处理 其他值是指定下载,编号和KEYID相同的才处理数据 FF01到FFFE时候,是序列号模式 V4.74 低位字节和进入下载模式时候的低位字节相同,用于键盘判断是否是当前下载数据 4 DOWNCMD 2 广播式下载具体数据 5 DOWNTYPE 多包类型 6 DOWNID 数据包标识码 文件下载模式时候是最高位地址 V4.52 由于非文件下载模式PACKH加上PACKL,只能下载65536字节,文件下载模式为支持大文件,用DOWNID作为高位地址,可以下载16384K字节 7 PACKH 数据段编号,0-255 8 PACKL 数据片编号, 0-15 9-24 PACKDATA 16字节的数据 * @return */ public static final byte DOWNCMD = 0x40 ; private byte cmd = DOWNCMD; private byte[] keyId = new byte[2]; private byte downType ; private byte downid ; private byte packh; private byte packl; private byte[] packdata = new byte[16]; /** * 1 进入或退出下载状态 * 2 广播式下载具体数据 * 3 询问广播式下载的执行状态 */ private byte cmd1 ; @Override public byte[] toBytes() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); try{ outputStream.write(getCmd()); outputStream.write(getKeyId()); outputStream.write(getCmd1()); outputStream.write(getDownType()); outputStream.write(getDownid()); outputStream.write(getPackh()); outputStream.write(getPackl()); outputStream.write(getPackdata()); return outputStream.toByteArray(); }catch (Exception e){} return new byte[0]; } @Override public ICmd parseCmd(byte[] source, int start) { if(source.length > start){ setCmd(source[start]); } if(source.length > start + 2){ setKeyId(Arrays.copyOfRange(source,start+2,start+4)); } if(source.length > start + 4){ setCmd1(source[start+4]); } if(source.length > start + 5){ setDownType(source[start+5]); } if(source.length > start + 6){ setDownid(source[start+6]); } if(source.length > start + 7){ setPackh(source[start+7]); } if(source.length > start + 8){ setPackl(source[start+8]); } if(source.length > start + 24){ setPackdata(Arrays.copyOfRange(source,start+9,start+24)); } return this; } public static MutiPkgDownCmd parseRequest(byte[] bytes, int length){ MutiPkgDownCmd mutiPkgDownCmd = new MutiPkgDownCmd(); mutiPkgDownCmd.parseCmd(bytes); return mutiPkgDownCmd; } public byte getCmd() { return cmd; } public void setCmd(byte cmd) { this.cmd = cmd; } public byte[] getKeyId() { return keyId; } public void setKeyId(byte[] keyId) { this.keyId = keyId; } public byte getDownType() { return downType; } public void setDownType(byte downType) { this.downType = downType; } public byte getDownid() { return downid; } public void setDownid(byte downid) { this.downid = downid; } public byte getPackh() { return packh; } public void setPackh(byte packh) { this.packh = packh; } public byte getPackl() { return packl; } public void setPackl(byte packl) { this.packl = packl; } public byte[] getPackdata() { return packdata; } public void setPackdata(byte[] packdata) { this.packdata = packdata; } public byte getCmd1() { return cmd1; } public void setCmd1(byte cmd1) { this.cmd1 = cmd1; } }