-----------------------------对接类(包含测试main方法)----------------------------------
协议开发 主要是对接厦门名望的led/三级诱导屏的语音和显示
import com.tnar.xiamen.model.LedText;
import com.tnar.xiamen.util.UdpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
/**
* @author gaotianyu
* @date Created in 2021-03-02 14:51
*/
public class MingWangHongKa {
private final static Logger log = LoggerFactory.getLogger(UdpClient.class);
private static final char[] hexArray = "0123456789ABCDEF".toCharArray();
//test-main方法
public static void main(String[] args) {
//line代表想显示到第几行 kuan代表宽度多少 文字超过要设置移动
List<LedText> texts = Arrays.asList( new LedText(1, "第一行测试左移", 1),
new LedText(2, "第二行", 1),
new LedText(3, "第三行", 1));
try {
for (LedText ledText : texts) {
byte[] by=verticalLedTextToByte(ledText,12);
System.out.println(toHex(by));
DirectionalDatagramPacket packet = DirectionalDatagramPacket.getInstance("192.168.1.185",8800, Integer.MAX_VALUE);
UdpClient.sendAndReceive(packet.newPacket(by));
}
} catch (Exception e) {
log.info("异常:"+e);
}
}
/**
* 调用语音接口
* @param text 播放的文字
* @param ip 对应led的ip
*/
public static void speakLed(String text,String ip) {
try {
byte[] by = getVoiceBytes(text);
DirectionalDatagramPacket packet = DirectionalDatagramPacket.getInstance(ip,8800,Integer.MAX_VALUE);
UdpClient.sendAndReceive(packet.newPacket(by));
} catch (Exception e) {
log.info("异常:"+e);
}
}
/**
* 调用显示接口
* @param texts 显示的文字
* @param ip 对应led/三级诱导屏幕ip
* @param kuan 屏幕总宽度 宽度=横点阵数总和除以8 常态一小块为 横32 竖16点阵数 宽度就是32/8=4
*/
public static void showLed(List<LedText> texts, String ip,int kuan){
try {
for (LedText ledText : texts) {
byte[] by=verticalLedTextToByte(ledText,kuan);
System.out.println(toHex(by));
DirectionalDatagramPacket packet = DirectionalDatagramPacket.getInstance(ip,8800, Integer.MAX_VALUE);
UdpClient.sendAndReceive(packet.newPacket(by));
}
} catch (Exception e) {
log.info("异常:"+e);
}
}
/**
* 红卡(红色控制卡)语音
* @param text
* @return
*/
private static byte[] getVoiceBytes(String text) {
//输出流最终输出 要转byte[]数组
ByteArrayOutputStream bos = new ByteArrayOutputStream();
//手动写入到一个底层输出流
DataOutputStream dos = new DataOutputStream(bos);
byte[] message =buildPlayMessageByteWithOptions(text);
//播放信息长度
int messageLength=message.length;
//语音长度
int yuyin= 2 + 16 + messageLength;
//数据长度
int shuju= 1 + 2 + yuyin + 2;
//总长度
int zong= 4 + 4 + 1 + 4 + 4 + shuju + 2;
byte[] head = {(byte) 0xFE, (byte) 0x5C, (byte) 0x4B, (byte) 0x89};
try {
//头------------4
dos.write(head);
// 总长度--------4
dos.write(intTo4ByteLE(zong));
// 命令-语音播放-----------1
dos.writeByte(0x68);
// 232-2 的 2------------4
dos.write(intTo4ByteLE(2));
//数据长度--------------4
dos.write(intTo4ByteLE(shuju));
//数据协议固定头
dos.writeByte(0xFD);
//语音长度
dos.write(intTo2ByteLE(yuyin));
//语音文本固定头
dos.writeByte(0x01);
dos.writeByte(0x00);
// 语音设置
dos.writeByte(0x5b);
dos.writeByte(0x76);
dos.writeByte(0x31); //音量
dos.writeByte(0x5d);
dos.writeByte(0x5b);
dos.writeByte(0x73);
dos.writeByte(0x35); //语速
dos.writeByte(0x5d);
dos.writeByte(0x5b);
dos.writeByte(0x74);
dos.writeByte(0x35); //语调
dos.writeByte(0x5d);
dos.writeByte(0x5b);
dos.writeByte(0x6d);
dos.writeByte(0x33); //声音性别控制(男or女)
dos.writeByte(0x5d);
//语音内容
dos.write(message);
//预留
dos.writeByte(0x00);
dos.writeByte(0x00);
//结束
dos.writeByte(0xFF);
dos.writeByte(0xFF);
}catch (IOException e) {
log.info("客户端异常:{}"+e);
}finally {
try{
if (dos != null){
dos.close();
}
if (bos != null){
bos.close();
}
}catch (IOException e) {
log.info("客户端finally异常:{}"+e);
}
}
return bos.toByteArray();
}
/**
* 蓝卡(蓝色控制卡)显示
* @param text
* @param line
* @param color
* @param fontSize
* @param kuan
* @return
*/
private static byte[] getShowBytes(String text, int line, byte color, byte fontSize,int kuan) {
//输出流最终输出 要转byte[]数组
ByteArrayOutputStream bos = new ByteArrayOutputStream();
//手动写入到一个底层输出流
DataOutputStream dos = new DataOutputStream(bos);
//显示的数据
byte[] message =buildPlayMessageByteWithOptions(text);
// 文本内容长度
// 显示数据+固定10位字节
int textLength = message.length + 10;
// 数据长度
// 25为窗口长度 4为素材属性长度 23为素材属性 4为文本内容长度 textLength为文本内容
int contentLength = 25 + 4 + 23 + 4 + textLength;
// 总长度
// 17为头部长度 contentLength为数据长度 2为结束符
int totalLength = 17 + contentLength + 2;
//包头4字节
byte[] head = {(byte) 0xFE, (byte) 0x5C, (byte) 0x4B, (byte) 0x89};
try {
//-----头部长度开始------
//头------------4字节
dos.write(head);
//总长度--------4字节
dos.write(intTo4ByteLE(totalLength));
// 命令-文本播放-----------1字节
dos.writeByte(0x31);
// 发送编号--------------4字节
dos.write(new byte[] {(byte) 0x92, 0x79, (byte) 0x95, 0x72});
//数据长度------------4字节
dos.write(intTo4ByteLE(contentLength));
//-----头部长度结束------
//-----数据长度开始------
//-----窗口长度开始------
//窗口编号-----9字节
dos.write(new byte[] {0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30});
dos.writeByte((byte) (0x30 + line));
// 固定分隔符----1字节
dos.writeByte((byte) 0x2C);
//显示方式---1字节
dos.writeByte(putShowStyle(message,kuan));
//速度1-8 值越大速度越慢----1字节
dos.writeByte(0x01);
//停留 0-255秒 0不停留-----1字节
dos.writeByte(0x00);
// 开始时间-----6字节
dos.write(new byte[] {0x30, 0x31, 0x30, 0x31, 0x30, 0x31});
// 结束时间-----6字节
dos.write(new byte[] {0x39, 0x39, 0x31, 0x32, 0x33, 0x31});
//-----窗口长度结束------
//文本属性长度
dos.write(intTo4ByteLE(19));
//-------文本属性长度开始-------
// 第 7 个 0x01 所有素材立即更新 0x02 本素材立即更新,其他素材显示不变 0x03 本素材不立即更新,按正常显示进行。
// 第 9 个 0x01 为 单基色, 0x02 为双基色
dos.writeByte(0x55);
dos.writeByte((byte)0xAA);
dos.writeByte(0x00);
dos.writeByte(0x00);
dos.writeByte(0x37);
dos.writeByte(0x01); //红卡的话填 0x32
dos.writeByte(0x01);//第 7 个 //红卡的话填 0x32
dos.writeByte(0x31);
dos.writeByte(0x01);//第 9 个 //红卡的话填 0x32 0x31 为 单基色, 0x32 为双基色
dos.writeByte(0x31);
dos.writeByte(0x00);
dos.writeByte(0x00);
dos.writeByte(0x08);//单字长度=屏幕宽/8
dos.writeByte(0x00);
dos.writeByte(0x08);//单字高度
dos.writeByte(0x00);
dos.writeByte(color);//颜色
dos.writeByte(fontSize);//字号
dos.writeByte(0x00);
//----文本属性长度结束-----------
//文本内容长度
dos.write(intTo4ByteLE(textLength));
//-----文本内容长度开始------
// 文本长度
dos.write(message);
// 文本固定结尾------10字节
dos.write(new byte[] {(byte) 0xFF, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00});
//-----文本内容长度结束------
//-----数据长度结束------
//结束符号-------2字节
dos.writeByte(0xFF);
dos.writeByte(0xFF);
}catch (Exception e) {
log.info("客户端异常:{}"+e);
}finally {
try{
if (dos != null){
dos.close();
}
if (bos != null){
bos.close();
}
}catch (IOException e) {
log.info("客户端finally异常:{}"+e);
}
}
return bos.toByteArray();
}
//判断文字是否移动
private static byte putShowStyle(byte[] showStyle,int kuan){
if(showStyle.length<=kuan){
// 显示效果 没有效果立即显示
return (byte) 0x09;
}else{
// 显示文字超过屏幕宽度时显示左移
return (byte) 0x01;
}
}
//颜色字体转换
private static byte[] verticalLedTextToByte(LedText ledText,int kuan) {
int font=16; //16位点阵 //0x01宋体 0x02楷体 0x03黑体 0x04隶书
int size=1;
byte fontSize=(byte)(font+size);
byte color=0x01; //设置默认为红色
switch (ledText.getColor()) {
case 1://红色
color=0x01;
break;
case 2://绿色
color=0x02;
break;
case 3://黄色
color=0x03;
break;
default:
break;
}
return getShowBytes(ledText.getText(), ledText.getLine(), color,fontSize,kuan);
}
//------下面方法都是进制转化
public static String toHex(byte[] bytes) {
return toHex(bytes, bytes.length);
}
public static String toHex(byte[] bytes, int len) {
char[] hexChars = new char[len * 2];
for (int j = 0; j < len; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
private static byte[] buildPlayMessageByteWithOptions(String text) {
return text.getBytes(Charset.forName("GBK"));
}
private static byte[] intTo4ByteLE(int yourInt){
byte[]bytes=new byte[4];
bytes[0]=(byte) (yourInt&0xff);
bytes[1]=(byte) ((yourInt>>8)&0xff);
bytes[2]=(byte) ((yourInt>>16)&0xff);
bytes[3]=(byte) ((yourInt>>24)&0xff);
return bytes;
}
private static byte[] intTo2ByteLE(int yourInt){
byte[]bytes=new byte[2];
bytes[0]=(byte) ((yourInt>>8)&0xff);
bytes[1]=(byte) (yourInt&0xff);
return bytes;
}
}
----------------------------输出实体类---------------------
/**
* @author gaotianyu
* @date Created in 2021-03-02 14:50
*/
public class LedText {
public LedText(int line, String text, int color) {
this.line = line;
this.text = text;
this.color = color;
}
public LedText(int line, String text) {
this.line = line;
this.text = text;
}
public LedText() {
}
/**
*
*/
private int line;
/**
*
*/
private String text;
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
/**
*
*/
private int color;
public int getLine() {
return line;
}
public void setLine(int line) {
this.line = line;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
@Override
public String toString() {
return "LedText{" +
"line=" + line +
", text='" + text + '\'' +
", color=" + color +
'}';
}
}
--------------------------------------UDP发送类----------------------------------------
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.concurrent.TimeUnit;
/**
* @author gaotianyu
* @date Created in 2021-03-02 14:44
*/
//名望UDP
public class UdpClient {
private final static Logger log = LoggerFactory.getLogger(UdpClient.class);
private volatile static UdpClient instance;
private DatagramSocket socket;
private UdpClient() {
try {
//创建一个流套接字并将其连接到主机上
socket = new DatagramSocket();
} catch (SocketException e) {
System.out.println("construct udp socket error: "+ e.getMessage());
}
}
private static DatagramSocket getInstance() {
if (instance == null) {
synchronized (UdpClient.class) {
if (instance == null) {
instance = new UdpClient();
}
}
}
return instance.socket;
}
public static void send(DatagramPacket packet, long delay) {
try {
getInstance().send(packet);
if (delay > 0) {
TimeUnit.MILLISECONDS.sleep(delay);
}
} catch (IOException | InterruptedException e) {
log.info("异常:"+e);
}
}
public static DatagramPacket sendAndReceive(DatagramPacket packet) {
try {
DatagramSocket datagramSocket = getInstance();
//发送数据
datagramSocket.send(packet);
datagramSocket.setSoTimeout(1000);
datagramSocket.receive(packet);
} catch (SocketTimeoutException e) {
log.info("异常:"+e);
} catch (IOException e) {
log.info("异常:"+e);
}
return packet;
}
}
------------------------------发送初始化------------------------
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* @author gaotianyu
* @date Created in 2021-03-02 14:59
*/
public class DirectionalDatagramPacket {
private final static Logger log = LoggerFactory.getLogger(DirectionalDatagramPacket.class);
private InetAddress address;
private int port;
private int max_bytes = 0;
public static DirectionalDatagramPacket getInstance(String ip, int port, int max_byte){
DirectionalDatagramPacket ret = new DirectionalDatagramPacket();
ret.init(ip, port, max_byte);
return ret;
}
public DatagramPacket newPacket(byte[] bytes){
if (address == null) {
log.info("address为null");
return null;
}
if (this.max_bytes > 0 && bytes.length > this.max_bytes){
log.info("byte数组超过了最大限制");
return null;
}
return new DatagramPacket(bytes ,bytes.length , address , port);
}
private void init(String ip, int port, int max_byte) {
this.max_bytes = max_byte;
this.port = port;
try {
this.address = InetAddress.getByName(ip);
} catch (UnknownHostException e) {
log.info("异常:"+e);
}
}
private DirectionalDatagramPacket() {
super();
}
}