新增了 和 科聪控制器通讯
This commit is contained in:
parent
aa56926258
commit
ece8013fec
@ -12,6 +12,18 @@ dependencies {
|
|||||||
api group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '2.3.9'
|
api group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '2.3.9'
|
||||||
|
|
||||||
implementation group: 'org.semver4j', name: 'semver4j', version: '5.4.0'
|
implementation group: 'org.semver4j', name: 'semver4j', version: '5.4.0'
|
||||||
|
|
||||||
|
// https://mvnrepository.com/artifact/com.github.ben-manes.caffeine/caffeine
|
||||||
|
implementation group: 'com.github.ben-manes.caffeine', name: 'caffeine', version: '3.1.8'
|
||||||
|
|
||||||
|
// https://mvnrepository.com/artifact/io.netty/netty-all
|
||||||
|
implementation group: 'io.netty', name: 'netty-all', version: '4.1.110.Final'
|
||||||
|
// https://mvnrepository.com/artifact/org.springframework.retry/spring-retry
|
||||||
|
implementation group: 'org.springframework.retry', name: 'spring-retry', version: '2.0.5'
|
||||||
|
// https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
|
||||||
|
implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.5.16'
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
processResources.doLast {
|
processResources.doLast {
|
||||||
|
@ -0,0 +1,143 @@
|
|||||||
|
package org.opentcs.kc.common;
|
||||||
|
|
||||||
|
|
||||||
|
import com.github.benmanes.caffeine.cache.Cache;
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
public class CaffeineUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存的最大容量
|
||||||
|
*/
|
||||||
|
private static final int MAXIMUM_SIZE = 1000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存项的写入后过期时间
|
||||||
|
*/
|
||||||
|
private static final int EXPIRE_AFTER_WRITE_DURATION = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过期时间单位(分钟)
|
||||||
|
*/
|
||||||
|
private static final TimeUnit EXPIRE_AFTER_WRITE_TIMEUNIT = TimeUnit.MINUTES;
|
||||||
|
|
||||||
|
private static Cache<String, Object> cache;
|
||||||
|
private static Cache<String, AtomicInteger> cacheUUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化Caffeine缓存配置
|
||||||
|
*/
|
||||||
|
static {
|
||||||
|
//todo https://github.com/ben-manes/caffeine/wiki 研究一下,然后升级一下版本
|
||||||
|
cache = Caffeine.newBuilder()
|
||||||
|
.maximumSize(MAXIMUM_SIZE)
|
||||||
|
.expireAfterWrite(EXPIRE_AFTER_WRITE_DURATION, EXPIRE_AFTER_WRITE_TIMEUNIT)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
cacheUUID = Caffeine.newBuilder()
|
||||||
|
.maximumSize(10)
|
||||||
|
.expireAfterWrite(Long.MAX_VALUE, EXPIRE_AFTER_WRITE_TIMEUNIT)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
byte[] a = getUUID();
|
||||||
|
byte[] b = getUUID();
|
||||||
|
byte[] d = ByteUtils.intToBytes(300);
|
||||||
|
byte[] c = ByteUtils.intToBytes(32000);
|
||||||
|
//todo int 转两个字节长度
|
||||||
|
System.out.println(Arrays.asList(getUUID()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static synchronized byte[] getUUID() {
|
||||||
|
AtomicInteger uuid = cacheUUID.getIfPresent("UUID");
|
||||||
|
if(uuid == null){
|
||||||
|
//transationId 从1 开始,0留给心跳变量,这样就固定报文了
|
||||||
|
cacheUUID.put("UUID",new AtomicInteger(1));
|
||||||
|
return ByteUtils.intToBytes(1);
|
||||||
|
}else {
|
||||||
|
if(uuid.get() >= 32000){
|
||||||
|
cacheUUID.put("UUID",new AtomicInteger(1));
|
||||||
|
return ByteUtils.intToBytes(1);
|
||||||
|
}else {
|
||||||
|
return ByteUtils.intToBytes(uuid.incrementAndGet());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized byte[] getUUIDAGV() {
|
||||||
|
AtomicInteger agvuuid = cacheUUID.getIfPresent("AGVUUID");
|
||||||
|
if(agvuuid == null){
|
||||||
|
//transationId 从1 开始,0留给心跳变量,这样就固定报文了
|
||||||
|
cacheUUID.put("AGVUUID",new AtomicInteger(1));
|
||||||
|
return ByteUtils.intToBytesS(1);
|
||||||
|
}else {
|
||||||
|
if(agvuuid.get() >= 32000){
|
||||||
|
cacheUUID.put("AGVUUID",new AtomicInteger(1));
|
||||||
|
return ByteUtils.intToBytesS(1);
|
||||||
|
}else {
|
||||||
|
return ByteUtils.intToBytesS(agvuuid.incrementAndGet());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取缓存值
|
||||||
|
*
|
||||||
|
* @param key 缓存键
|
||||||
|
* @return 缓存值
|
||||||
|
*/
|
||||||
|
public static Object get(String key) {
|
||||||
|
return cache.getIfPresent(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置缓存值
|
||||||
|
*
|
||||||
|
* @param key 缓存键
|
||||||
|
* @param value 缓存值
|
||||||
|
*/
|
||||||
|
public static void put(String key, Object value) {
|
||||||
|
cache.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除缓存项
|
||||||
|
*
|
||||||
|
* @param key 缓存键
|
||||||
|
*/
|
||||||
|
public static void remove(String key) {
|
||||||
|
cache.invalidate(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空缓存
|
||||||
|
*/
|
||||||
|
public static void clear() {
|
||||||
|
cache.invalidateAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取缓存中的所有值
|
||||||
|
*
|
||||||
|
* @return 缓存中的所有值集合
|
||||||
|
*/
|
||||||
|
public static Collection<Object> getAllValues() {
|
||||||
|
return cache.asMap().values();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空缓存中的所有值
|
||||||
|
*/
|
||||||
|
public static void removeAllValues() {
|
||||||
|
cache.invalidateAll();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package org.opentcs.kc.common;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author tanyaowu
|
||||||
|
* 2017年3月30日 下午7:05:54
|
||||||
|
*/
|
||||||
|
public interface Const {
|
||||||
|
/**
|
||||||
|
* 服务器地址
|
||||||
|
*/
|
||||||
|
public static final String SERVER = "127.0.0.1";
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
String s = "123, 34, 104, 101, 97, 100, 101, 114, 34, 58, 123, 34, 116, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 73, 100, 34, 58, 34, 50, 48, 50, 52, 48, 49, 48, 50, 49, 53, 52, 48, 50, 55, 95, 51, 100, 56, 49, 99, 34, 44, 34, 109, 101, 115, 115, 97, 103, 101, 84, 121, 112, 101, 34, 58, 51, 44, 34, 109, 101, 115, 115, 97, 103, 101, 78, 97, 109, 101, 34, 58, 34, 72, 101, 97, 114, 116, 66, 101, 97, 116, 34, 44, 34, 115, 101, 110, 100, 84, 105, 109, 101, 115, 116, 97, 109, 112, 34, 58, 34, 50, 48, 50, 52, 45, 48, 49, 45, 48, 50, 32, 49, 53, 58, 52, 48, 58, 50, 55, 34, 125, 44, 34, 98, 111, 100, 121, 34, 58, 34, 49, 34, 44, 34, 114, 101, 116, 117, 114, 110, 115, 34, 58, 110, 117, 108, 108, 125";
|
||||||
|
String[] split = s.split(",");
|
||||||
|
System.out.println(split.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听端口
|
||||||
|
*/
|
||||||
|
public static final int PORT = 6789;
|
||||||
|
//public static final int PORT = 9000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 心跳超时时间
|
||||||
|
*/
|
||||||
|
public static final int TIMEOUT = 5000;
|
||||||
|
|
||||||
|
|
||||||
|
public static final int NEED_REPLY_TYPE = 1;
|
||||||
|
public static final int NO_REPLY_TYPE = 2;
|
||||||
|
public static final int HEARTBEAT_TYPE = 3;
|
||||||
|
|
||||||
|
|
||||||
|
public static final Integer REQUEST_TYPE = 1;
|
||||||
|
public static final Integer RESPONSE_TYPE = 2;
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package org.opentcs.kc.common;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: "通用的数据传输底层类"
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2022/10/18 16:22
|
||||||
|
*/
|
||||||
|
public class Package {
|
||||||
|
|
||||||
|
private byte[] body;
|
||||||
|
private String transationId;
|
||||||
|
|
||||||
|
|
||||||
|
public Package(byte[] body, String transationId) {
|
||||||
|
this.body = body;
|
||||||
|
this.transationId = transationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getBody() {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBody(byte[] body) {
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTransationId() {
|
||||||
|
return transationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTransationId(String transationId) {
|
||||||
|
this.transationId = transationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Package{" +
|
||||||
|
"body=" + Arrays.toString(body) +
|
||||||
|
", transationId='" + transationId + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 S7connector members (github.com/s7connector)
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.opentcs.kc.common;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Class S7Exception is an exception related to S7 Communication
|
||||||
|
*/
|
||||||
|
public final class ParseDataException extends RuntimeException {
|
||||||
|
|
||||||
|
/** The Constant serialVersionUID. */
|
||||||
|
private static final long serialVersionUID = -4761415733559374116L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new s7 exception.
|
||||||
|
*/
|
||||||
|
public ParseDataException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new s7 exception.
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
* the message
|
||||||
|
*/
|
||||||
|
public ParseDataException(final String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new s7 exception.
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
* the message
|
||||||
|
* @param cause
|
||||||
|
* the cause
|
||||||
|
*/
|
||||||
|
public ParseDataException(final String message, final Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new s7 exception.
|
||||||
|
*
|
||||||
|
* @param cause
|
||||||
|
* the cause
|
||||||
|
*/
|
||||||
|
public ParseDataException(final Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package org.opentcs.kc.common;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: "通用的数据传输底层类"
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2022/10/18 16:22
|
||||||
|
*/
|
||||||
|
public class RcvPackage {
|
||||||
|
|
||||||
|
private boolean isOk;
|
||||||
|
private Object value;
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
public RcvPackage(boolean isOk, Object value, String content) {
|
||||||
|
this.isOk = isOk;
|
||||||
|
this.value = value;
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
public RcvPackage() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOk() {
|
||||||
|
return isOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(Object value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "RcvPackage{" +
|
||||||
|
"isOk=" + isOk +
|
||||||
|
", value=" + value +
|
||||||
|
", content='" + content + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
package org.opentcs.kc.common;
|
||||||
|
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2022/10/24 16:27
|
||||||
|
*/
|
||||||
|
public class Utils {
|
||||||
|
/**
|
||||||
|
* java生成随机数字15位数
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getTransationId() {
|
||||||
|
String val = String.valueOf(System.currentTimeMillis());
|
||||||
|
Random random = new Random();
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
val += String.valueOf(random.nextInt(10));
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定长度的随机数
|
||||||
|
* (获取指定长度uuid)
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getUUID(int len)
|
||||||
|
{
|
||||||
|
if(0 >= len)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String uuid = getUUID();
|
||||||
|
StringBuffer str = new StringBuffer();
|
||||||
|
|
||||||
|
for (int i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
str.append(uuid.charAt(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return str.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*32位默认长度的uuid
|
||||||
|
* (获取32位uuid)
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getUUID()
|
||||||
|
{
|
||||||
|
return UUID.randomUUID().toString().replace("-", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
type:
|
||||||
|
1.返回的是这种格式:2021-08-16 15:00:05
|
||||||
|
2.返回的是这种格式:20210816150021
|
||||||
|
*/
|
||||||
|
public static String getNowDate(Integer type){
|
||||||
|
if(type==1){
|
||||||
|
return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||||
|
}else {
|
||||||
|
return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,349 @@
|
|||||||
|
package org.opentcs.kc.common.byteutils;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2022/4/1 17:32
|
||||||
|
*/
|
||||||
|
//对数字和字节进行转换。 假设数据存储是以大端模式存储的:
|
||||||
|
// byte: 字节类型 占8位二进制 00000000
|
||||||
|
// char: 字符类型 占2个字节 16位二进制 byte[0] byte[1]
|
||||||
|
// int : 整数类型 占4个字节 32位二进制 byte[0] byte[1] byte[2] byte[3]
|
||||||
|
// long: 长整数类型 占8个字节 64位二进制 byte[0] byte[1] byte[2] byte[3] byte[4] byte[5]
|
||||||
|
// long: 长整数类型 占8个字节 64位二进制 byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] byte[6] byte[7]
|
||||||
|
// float: 浮点数(小数) 占4个字节 32位二进制 byte[0] byte[1] byte[2] byte[3]
|
||||||
|
// double: 双精度浮点数(小数) 占8个字节 64位二进制 byte[0] byte[1] byte[2] byte[3] byte[4]byte[5] byte[6] byte[7]
|
||||||
|
|
||||||
|
public class ByteUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* int转byte
|
||||||
|
*
|
||||||
|
* @param intValue int值
|
||||||
|
* @return byte值
|
||||||
|
*/
|
||||||
|
public static byte intToByte(int intValue) {
|
||||||
|
return (byte) intValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte intTo1Byte(int intValue) {
|
||||||
|
ByteBuffer byteBuffer = ByteBuffer.allocate(4);
|
||||||
|
byteBuffer.putInt(intValue);
|
||||||
|
byte[] byteArray = byteBuffer.array();
|
||||||
|
return byteArray[3]; // 取低字节
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* byte转无符号int
|
||||||
|
*
|
||||||
|
* @param byteValue byte值
|
||||||
|
* @return 无符号int值
|
||||||
|
* @since 3.2.0
|
||||||
|
*/
|
||||||
|
public static int byteToUnsignedInt(byte byteValue) {
|
||||||
|
// Java 总是把 byte 当做有符处理;我们可以通过将其和 0xFF 进行二进制与得到它的无符值
|
||||||
|
return byteValue & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 字符串转成16个字节的byte[] ,不足16个字节前面填充0
|
||||||
|
* */
|
||||||
|
public static byte[] stringTo16Byte(String input) {
|
||||||
|
byte[] originalBytes = input.getBytes(StandardCharsets.UTF_8);
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(16);
|
||||||
|
buffer.put(originalBytes);
|
||||||
|
buffer.put(new byte[16 - originalBytes.length]);
|
||||||
|
return buffer.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* byte数组转short<br>
|
||||||
|
* 默认以小端序转换
|
||||||
|
*
|
||||||
|
* @param bytes byte数组
|
||||||
|
* @return short值
|
||||||
|
*/
|
||||||
|
public static short bytesToShort(byte[] bytes) {
|
||||||
|
return bytesToShort(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* byte数组转short<br>
|
||||||
|
* 自定义端序
|
||||||
|
*
|
||||||
|
* @param bytes byte数组
|
||||||
|
* @param byteOrder 端序
|
||||||
|
* @return short值
|
||||||
|
*/
|
||||||
|
public static short bytesToShort(byte[] bytes, ByteOrder byteOrder) {
|
||||||
|
if (ByteOrder.LITTLE_ENDIAN == byteOrder) {
|
||||||
|
//小端模式,数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中
|
||||||
|
return (short) (bytes[0] & 0xff | (bytes[1] & 0xff) << Byte.SIZE);
|
||||||
|
} else {
|
||||||
|
return (short) (bytes[1] & 0xff | (bytes[0] & 0xff) << Byte.SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* short转byte数组<br>
|
||||||
|
* 默认以小端序转换
|
||||||
|
*
|
||||||
|
* @param shortValue short值
|
||||||
|
* @return byte数组
|
||||||
|
*/
|
||||||
|
public static byte[] shortToBytes(short shortValue) {
|
||||||
|
return shortToBytes(shortValue, ByteOrder.LITTLE_ENDIAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* short转byte数组<br>
|
||||||
|
* 自定义端序
|
||||||
|
*
|
||||||
|
* @param shortValue short值
|
||||||
|
* @param byteOrder 端序
|
||||||
|
* @return byte数组
|
||||||
|
*/
|
||||||
|
public static byte[] shortToBytes(short shortValue, ByteOrder byteOrder) {
|
||||||
|
byte[] b = new byte[Short.BYTES];
|
||||||
|
if (ByteOrder.LITTLE_ENDIAN == byteOrder) {
|
||||||
|
b[0] = (byte) (shortValue & 0xff);
|
||||||
|
b[1] = (byte) ((shortValue >> Byte.SIZE) & 0xff);
|
||||||
|
} else {
|
||||||
|
b[1] = (byte) (shortValue & 0xff);
|
||||||
|
b[0] = (byte) ((shortValue >> Byte.SIZE) & 0xff);
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* byte[]转int值<br>
|
||||||
|
* 默认以小端序转换
|
||||||
|
*
|
||||||
|
* @param bytes byte数组
|
||||||
|
* @return int值
|
||||||
|
*/
|
||||||
|
public static int bytesToInt(byte[] bytes) {
|
||||||
|
return bytesToInt(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* byte[]转int值<br>
|
||||||
|
* 自定义端序
|
||||||
|
*
|
||||||
|
* @param bytes byte数组
|
||||||
|
* @param byteOrder 端序
|
||||||
|
* @return int值
|
||||||
|
*/
|
||||||
|
public static int bytesToInt(byte[] bytes, ByteOrder byteOrder) {
|
||||||
|
if (ByteOrder.LITTLE_ENDIAN == byteOrder) {
|
||||||
|
return bytes[0] & 0xFF | //
|
||||||
|
(bytes[1] & 0xFF) << 8 | //
|
||||||
|
(bytes[2] & 0xFF) << 16 | //
|
||||||
|
(bytes[3] & 0xFF) << 24; //
|
||||||
|
} else {
|
||||||
|
return bytes[3] & 0xFF | //
|
||||||
|
(bytes[2] & 0xFF) << 8 | //
|
||||||
|
(bytes[1] & 0xFF) << 16 | //
|
||||||
|
(bytes[0] & 0xFF) << 24; //
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* int转byte数组<br>
|
||||||
|
* 默认以小端序转换
|
||||||
|
*
|
||||||
|
* @param intValue int值
|
||||||
|
* @return byte数组
|
||||||
|
*/
|
||||||
|
public static byte[] intToBytes(int intValue) {
|
||||||
|
return intToBytes(intValue, ByteOrder.LITTLE_ENDIAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* int转byte数组<br>
|
||||||
|
* 自定义端序
|
||||||
|
*
|
||||||
|
* @param intValue int值
|
||||||
|
* @param byteOrder 端序
|
||||||
|
* @return byte数组
|
||||||
|
*/
|
||||||
|
public static byte[] intToBytes(int intValue, ByteOrder byteOrder) {
|
||||||
|
|
||||||
|
if (ByteOrder.LITTLE_ENDIAN == byteOrder) {
|
||||||
|
return new byte[]{ //
|
||||||
|
(byte) (intValue & 0xFF), //
|
||||||
|
(byte) ((intValue >> 8) & 0xFF), //
|
||||||
|
(byte) ((intValue >> 16) & 0xFF), //
|
||||||
|
(byte) ((intValue >> 24) & 0xFF) //
|
||||||
|
};
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return new byte[]{ //
|
||||||
|
(byte) ((intValue >> 24) & 0xFF), //
|
||||||
|
(byte) ((intValue >> 16) & 0xFF), //
|
||||||
|
(byte) ((intValue >> 8) & 0xFF), //
|
||||||
|
(byte) (intValue & 0xFF) //
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* long转byte数组<br>
|
||||||
|
* 默认以小端序转换<br>
|
||||||
|
* from: https://stackoverflow.com/questions/4485128/how-do-i-convert-long-to-byte-and-back-in-java
|
||||||
|
*
|
||||||
|
* @param longValue long值
|
||||||
|
* @return byte数组
|
||||||
|
*/
|
||||||
|
public static byte[] longToBytes(long longValue) {
|
||||||
|
return longToBytes(longValue, ByteOrder.LITTLE_ENDIAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* long转byte数组<br>
|
||||||
|
* 自定义端序<br>
|
||||||
|
* from: https://stackoverflow.com/questions/4485128/how-do-i-convert-long-to-byte-and-back-in-java
|
||||||
|
*
|
||||||
|
* @param longValue long值
|
||||||
|
* @param byteOrder 端序
|
||||||
|
* @return byte数组
|
||||||
|
*/
|
||||||
|
public static byte[] longToBytes(long longValue, ByteOrder byteOrder) {
|
||||||
|
byte[] result = new byte[Long.BYTES];
|
||||||
|
if (ByteOrder.LITTLE_ENDIAN == byteOrder) {
|
||||||
|
for (int i = 0; i < result.length; i++) {
|
||||||
|
result[i] = (byte) (longValue & 0xFF);
|
||||||
|
longValue >>= Byte.SIZE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = (result.length - 1); i >= 0; i--) {
|
||||||
|
result[i] = (byte) (longValue & 0xFF);
|
||||||
|
longValue >>= Byte.SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* byte数组转long<br>
|
||||||
|
* 默认以小端序转换<br>
|
||||||
|
* from: https://stackoverflow.com/questions/4485128/how-do-i-convert-long-to-byte-and-back-in-java
|
||||||
|
*
|
||||||
|
* @param bytes byte数组
|
||||||
|
* @return long值
|
||||||
|
*/
|
||||||
|
public static long bytesToLong(byte[] bytes) {
|
||||||
|
return bytesToLong(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* byte数组转long<br>
|
||||||
|
* 自定义端序<br>
|
||||||
|
* from: https://stackoverflow.com/questions/4485128/how-do-i-convert-long-to-byte-and-back-in-java
|
||||||
|
*
|
||||||
|
* @param bytes byte数组
|
||||||
|
* @param byteOrder 端序
|
||||||
|
* @return long值
|
||||||
|
*/
|
||||||
|
public static long bytesToLong(byte[] bytes, ByteOrder byteOrder) {
|
||||||
|
long values = 0;
|
||||||
|
if (ByteOrder.LITTLE_ENDIAN == byteOrder) {
|
||||||
|
for (int i = (Long.BYTES - 1); i >= 0; i--) {
|
||||||
|
values <<= Byte.SIZE;
|
||||||
|
values |= (bytes[i] & 0xff);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < Long.BYTES; i++) {
|
||||||
|
values <<= Byte.SIZE;
|
||||||
|
values |= (bytes[i] & 0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* double转byte数组<br>
|
||||||
|
* 默认以小端序转换<br>
|
||||||
|
*
|
||||||
|
* @param doubleValue double值
|
||||||
|
* @return byte数组
|
||||||
|
*/
|
||||||
|
public static byte[] doubleToBytes(double doubleValue) {
|
||||||
|
return doubleToBytes(doubleValue, ByteOrder.LITTLE_ENDIAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* double转byte数组<br>
|
||||||
|
* 自定义端序<br>
|
||||||
|
* from: https://stackoverflow.com/questions/4485128/how-do-i-convert-long-to-byte-and-back-in-java
|
||||||
|
*
|
||||||
|
* @param doubleValue double值
|
||||||
|
* @param byteOrder 端序
|
||||||
|
* @return byte数组
|
||||||
|
*/
|
||||||
|
public static byte[] doubleToBytes(double doubleValue, ByteOrder byteOrder) {
|
||||||
|
return longToBytes(Double.doubleToLongBits(doubleValue), byteOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* byte数组转Double<br>
|
||||||
|
* 默认以小端序转换<br>
|
||||||
|
*
|
||||||
|
* @param bytes byte数组
|
||||||
|
* @return long值
|
||||||
|
*/
|
||||||
|
public static double bytesToDouble(byte[] bytes) {
|
||||||
|
return bytesToDouble(bytes, ByteOrder.LITTLE_ENDIAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* byte数组转double<br>
|
||||||
|
* 自定义端序<br>
|
||||||
|
*
|
||||||
|
* @param bytes byte数组
|
||||||
|
* @param byteOrder 端序
|
||||||
|
* @return long值
|
||||||
|
*/
|
||||||
|
public static double bytesToDouble(byte[] bytes, ByteOrder byteOrder) {
|
||||||
|
return Double.longBitsToDouble(bytesToLong(bytes, byteOrder));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将{@link Number}转换为
|
||||||
|
*
|
||||||
|
* @param number 数字
|
||||||
|
* @return bytes
|
||||||
|
*/
|
||||||
|
public static byte[] numberToBytes(Number number) {
|
||||||
|
return numberToBytes(number, ByteOrder.LITTLE_ENDIAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将{@link Number}转换为
|
||||||
|
*
|
||||||
|
* @param number 数字
|
||||||
|
* @param byteOrder 端序
|
||||||
|
* @return bytes
|
||||||
|
*/
|
||||||
|
public static byte[] numberToBytes(Number number, ByteOrder byteOrder) {
|
||||||
|
if (number instanceof Double) {
|
||||||
|
return doubleToBytes((Double) number, byteOrder);
|
||||||
|
} else if (number instanceof Long) {
|
||||||
|
return longToBytes((Long) number, byteOrder);
|
||||||
|
} else if (number instanceof Integer) {
|
||||||
|
return intToBytes((Integer) number, byteOrder);
|
||||||
|
} else if (number instanceof Short) {
|
||||||
|
return shortToBytes((Short) number, byteOrder);
|
||||||
|
} else {
|
||||||
|
return doubleToBytes(number.doubleValue(), byteOrder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,21 @@
|
|||||||
|
package org.opentcs.kc.common.byteutils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2022/12/5 11:11
|
||||||
|
*/
|
||||||
|
public class CommonFunctions {
|
||||||
|
/**
|
||||||
|
* a 整除 b ,如果有余数+1
|
||||||
|
* */
|
||||||
|
public static Integer exactDivision(Integer a,Integer b) {
|
||||||
|
|
||||||
|
int c = a/b;
|
||||||
|
|
||||||
|
if(a%b!=0){
|
||||||
|
c = a/b+1;
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package org.opentcs.kc.common.byteutils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2024-05-08 11:02
|
||||||
|
*/
|
||||||
|
public class StringArrayStruc {
|
||||||
|
private String[] content;
|
||||||
|
private Integer strSize;
|
||||||
|
|
||||||
|
public StringArrayStruc(String[] content, Integer strSize) {
|
||||||
|
this.content = content;
|
||||||
|
this.strSize = strSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContent(String[] content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getStrSize() {
|
||||||
|
return strSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStrSize(Integer strSize) {
|
||||||
|
this.strSize = strSize;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package org.opentcs.kc.common.enmuc;
|
||||||
|
|
||||||
|
public enum ModbusFC {
|
||||||
|
//心跳变量(这个可以要求电控同事加一个,不和业务关联,只用于通讯)
|
||||||
|
MB_HOLD_REG((byte) 0x03, (byte) 0x10),
|
||||||
|
Q_OUT((byte) 0x01, (byte) 0x05),
|
||||||
|
I_IN((byte) 0x02, null),
|
||||||
|
IW_IN((byte) 0x04, null),
|
||||||
|
;
|
||||||
|
|
||||||
|
private Byte fread;
|
||||||
|
private Byte fwrite;
|
||||||
|
|
||||||
|
ModbusFC(Byte fread, Byte fwrite){
|
||||||
|
this.fread = fread;
|
||||||
|
this.fwrite = fwrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Byte getFread() {
|
||||||
|
return fread;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFread(Byte fread) {
|
||||||
|
this.fread = fread;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Byte getFwrite() {
|
||||||
|
return fwrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFwrite(Byte fwrite) {
|
||||||
|
this.fwrite = fwrite;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package org.opentcs.kc.syn;
|
||||||
|
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: 蔡翔
|
||||||
|
* @Date: 2019/11/6 14:11
|
||||||
|
* @Version 1.0
|
||||||
|
*
|
||||||
|
* (重点)
|
||||||
|
* AsyncFuture 这个类就是票据, 刚拿到这个票据是没有信息的,当done == true 的时候,这个票据 上就自动有信息了
|
||||||
|
* 这个结果类设计的比较神奇
|
||||||
|
*/
|
||||||
|
public class AsyncFuture<Object> implements Future<Object> {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(AsyncFuture.class);
|
||||||
|
private volatile boolean done = false;
|
||||||
|
private Object oldRequest;
|
||||||
|
private Object result;
|
||||||
|
|
||||||
|
public AsyncFuture(Object oldRequest) {
|
||||||
|
this.oldRequest = oldRequest;
|
||||||
|
}
|
||||||
|
public AsyncFuture() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void done(Object result){
|
||||||
|
synchronized (this){
|
||||||
|
this.result = result;
|
||||||
|
this.done = true;
|
||||||
|
//注意这里的notifyAll只能唤醒 本锁的所有 下的所有 wait(),这里的锁就是 AsyncFuture这个类
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get(Long timeout) throws Exception {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get(Long timeout, String transationId) throws Exception {
|
||||||
|
synchronized (this){
|
||||||
|
//其实有 synchronize就相当于有一个阻塞队列,当有线程执行了wait 方法,就会把执行wait的这个线程给加入wait<Thread> 队列,
|
||||||
|
//当有线程执行notify方法的时候,就会往这个队列中取出一个或者多个Thread,取出来以后就能执行后续代码了
|
||||||
|
// System.out.println("get");
|
||||||
|
|
||||||
|
// 当线程执行wait()时,会把当前的锁释放,然后让出CPU,进入等待状态。 wait()会立刻释放synchronized(obj)中的obj锁,以便其他线程可以执行obj.notify()
|
||||||
|
// * 当线程执行notify()/notifyAll()方法时,会唤醒一个处于等待状态该对象锁的线程,然后继续往下执行,直到执行完退出对象锁锁住的区域(synchronized修饰的代码块)后再释放锁
|
||||||
|
this.wait(timeout);
|
||||||
|
if(!done){
|
||||||
|
//logger.error("T3 timeout , request information: "+oldRequest.toString());
|
||||||
|
SendedList.remove(transationId);
|
||||||
|
throw new Exception("T3 timeout , request information: "+oldRequest.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
//因为上面的代码是加锁的,所以这里的代码也是加锁的。
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
14
opentcs-common/src/main/java/org/opentcs/kc/syn/Future.java
Normal file
14
opentcs-common/src/main/java/org/opentcs/kc/syn/Future.java
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package org.opentcs.kc.syn;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: 蔡翔
|
||||||
|
* @Date: 2019/11/6 13:49
|
||||||
|
* @Version 1.0
|
||||||
|
*/
|
||||||
|
public interface Future<MQMessage> {
|
||||||
|
//别人调用我的时候,我先给他们返回一个结果,
|
||||||
|
MQMessage get(Long timeout) throws Exception;
|
||||||
|
|
||||||
|
MQMessage get(Long timeout, String transationId) throws Exception;
|
||||||
|
}
|
41
opentcs-common/src/main/java/org/opentcs/kc/syn/Main.java
Normal file
41
opentcs-common/src/main/java/org/opentcs/kc/syn/Main.java
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package org.opentcs.kc.syn;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2021/8/17 14:35
|
||||||
|
*/
|
||||||
|
public class Main {
|
||||||
|
|
||||||
|
// public static void main(String[] args) throws Exception {
|
||||||
|
//
|
||||||
|
// new Thread(()->{
|
||||||
|
// TResponse TResponse = new TResponse();
|
||||||
|
//
|
||||||
|
// TResponse.setHeader(new Header("123",1));
|
||||||
|
// TResponse.setBody("request-body-123");
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// AsyncFuture<TResponse> add = SendedList.add(TResponse.getHeader().getTransationId(), TResponse);
|
||||||
|
// try {
|
||||||
|
// TResponse TResponseResponse = add.get(3000L);
|
||||||
|
// System.out.println(TResponseResponse.getReturns());
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
// }).start();
|
||||||
|
//
|
||||||
|
// Thread.sleep(1000);
|
||||||
|
//
|
||||||
|
// new Thread(()->{
|
||||||
|
// TResponse TResponse = new TResponse();
|
||||||
|
// TResponse.setReturns(1);
|
||||||
|
// TResponse.setBody("response-body-123");
|
||||||
|
// TResponse.setHeader(new Header("123",2));
|
||||||
|
// SendedList.set(TResponse.getHeader().getTransationId() , TResponse);
|
||||||
|
// }).start();
|
||||||
|
// }
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package org.opentcs.kc.syn;
|
||||||
|
|
||||||
|
|
||||||
|
import org.opentcs.kc.common.CaffeineUtil;
|
||||||
|
import org.opentcs.kc.common.Package;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: "MES端 发送远程指令列表"
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2021/8/17 14:14
|
||||||
|
*/
|
||||||
|
public class SendedList {
|
||||||
|
// private static HashMap<String, AsyncFuture<MBPackage>> list = new HashMap<>();
|
||||||
|
// public static synchronized AsyncFuture<MQMessage> get(String transitionId){
|
||||||
|
// return list.get(transitionId);
|
||||||
|
// }
|
||||||
|
// public static synchronized void put(String transitionId,AsyncFuture<MQMessage> asyncFuture){
|
||||||
|
// list.put(transitionId,asyncFuture);
|
||||||
|
// }
|
||||||
|
|
||||||
|
public static synchronized AsyncFuture<Package> add(String transitionId, Package PackageRequest) {
|
||||||
|
AsyncFuture<Package> objectAsyncFuture = new AsyncFuture<>(PackageRequest);
|
||||||
|
//list.put(transitionId,objectAsyncFuture);
|
||||||
|
CaffeineUtil.put(transitionId,objectAsyncFuture);
|
||||||
|
return objectAsyncFuture;
|
||||||
|
}
|
||||||
|
|
||||||
|
//如果超时了,那么让外部把这个key 给清除掉,防止 list 过大
|
||||||
|
public static synchronized void remove(String transitionId) {
|
||||||
|
//list.remove(transitionId);
|
||||||
|
CaffeineUtil.remove(transitionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static synchronized Boolean set(String transitionId, Package message) {
|
||||||
|
//AsyncFuture<MBPackage> mqMessageAsyncFuture = list.get(transitionId);
|
||||||
|
AsyncFuture<Package> mqMessageAsyncFuture = (AsyncFuture<Package>)CaffeineUtil.get(transitionId);
|
||||||
|
if(mqMessageAsyncFuture == null){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mqMessageAsyncFuture.done(message);
|
||||||
|
//清除 ,防止这个hashMap过大。
|
||||||
|
CaffeineUtil.remove(transitionId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package org.opentcs.kc.udp.GB;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2024/12/15 10:53
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 作者:DarkKIng
|
||||||
|
* 类说明:日志信息,用String数组代替
|
||||||
|
*/
|
||||||
|
public class LogConst {
|
||||||
|
public final static int MONITOR_SIDE_PORT = 9998;
|
||||||
|
private static final String[] LOG_INFOS = {
|
||||||
|
"晨光微好,暖在夕阳。幽幽的巷子里,有着岁月酝酿的酒,愈久愈淳。一笔墨香,一盏明灯,记千帆过浪,数不尽的悲欢离合,待那水莲花开。",
|
||||||
|
"未来无期,静在安好。一剪寒梅,纷扰了岁月,抚平了伤痕。摆动的双桨,拨动了心的潭水。陌上花开,落一地秋霜,红枫染了红尘,便许了你十里红装。",
|
||||||
|
"离别的风,风干了月的泪。夜里的美",
|
||||||
|
"是梦的呢喃低语,挥走一片彩云,段落成珠。拂袖离去,乘鹤而来,古道西风瘦马。斑驳的树影中,眉目如画的眼,轻语告别了往事如烟。",
|
||||||
|
"无言的殇,几世沧桑,几生悲凉。一起剪了西窗烛,听了夜来风吹雨。昨日的叹息,今日的迷离,执一伞,存了一世一笔的温情。一曲长歌,唱尽了一世繁华,一世缘……",
|
||||||
|
"一世恋书,那便十里花开。一生凄凉,那便霜花十里。" ,
|
||||||
|
"一抹浓烟,便翻页书,展颜一笑,是时间带来遥远的梦。细数树的年轮,感受昨日惆怅,留一半清醒,梦一半叶落。在指尖流过的沙,海边浪花一朵朵,不相遇,才有不约而同。",
|
||||||
|
"这世俗,太多牵挂留在心间,一点朱砂泪,一曲相诗歌。岁月朦胧,梦醒了人生,风雨相容,演绎了一段风情。雪亦梦,雨亦梦,万张红纸从天洒来。惊动了山,撼动了天。" +
|
||||||
|
"一纸情愁,一指烟凉。一相思,一思量,水漫岸头,我们都有着自己不同的三生故事。迎一夜秋风,送一世暖阳,一切冰雪里的花开,是我一生的柔情。" +
|
||||||
|
"记忆中的短笛,有着清风须来的气息,那时我们面向大海,海风在耳边述说着大海边缘的温暖故事。安好一轮冷月,静好了一残红日,这便是我的语言,我的情丝。" +
|
||||||
|
"一漫山水,一段情,留在了岁月,拭去了风,晴雨清风,倒是暖阳拂绿草。" +
|
||||||
|
"这便,晨光微好,花开静好……"};
|
||||||
|
|
||||||
|
private final static Random r = new Random();
|
||||||
|
public static String getLogInfo(){
|
||||||
|
return LOG_INFOS[r.nextInt(LOG_INFOS.length-1)];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
package org.opentcs.kc.udp.GB;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2024/12/15 10:55
|
||||||
|
*/
|
||||||
|
|
||||||
|
import io.netty.bootstrap.Bootstrap;
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelOption;
|
||||||
|
import io.netty.channel.EventLoopGroup;
|
||||||
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
|
import io.netty.channel.socket.nio.NioDatagramChannel;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 作者:DarkKIng
|
||||||
|
* 类说明:日志的广播端 ( Client 端 )
|
||||||
|
*/
|
||||||
|
public class LogEventBroadcaster {
|
||||||
|
private final EventLoopGroup group;
|
||||||
|
private final Bootstrap bootstrap;
|
||||||
|
|
||||||
|
public LogEventBroadcaster(InetSocketAddress remoteAddress) {
|
||||||
|
group = new NioEventLoopGroup();
|
||||||
|
bootstrap = new Bootstrap();
|
||||||
|
//引导该 NioDatagramChannel(无连接的)
|
||||||
|
bootstrap.group(group).channel(NioDatagramChannel.class)
|
||||||
|
//todo 设置 SO_BROADCAST 套接字选项(option so_broadcast差异,这里和单播 是有差异的)
|
||||||
|
.option(ChannelOption.SO_BROADCAST, true)
|
||||||
|
.handler(new LogEventEncoder(remoteAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() throws Exception {
|
||||||
|
//绑定 Channel
|
||||||
|
Channel ch = bootstrap.bind(0).sync().channel();
|
||||||
|
long count = 0;
|
||||||
|
//启动主处理循环,模拟日志发送
|
||||||
|
for (;;) {
|
||||||
|
ch.writeAndFlush(new LogMsg(null, ++count,
|
||||||
|
LogConst.getLogInfo()));
|
||||||
|
try {
|
||||||
|
//休眠 2 秒,如果被中断,则退出循环;
|
||||||
|
Thread.sleep(2000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.interrupted();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
group.shutdownGracefully();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
|
||||||
|
//创建并启动一个新的 UdpQuestionSide 的实例
|
||||||
|
LogEventBroadcaster broadcaster = new LogEventBroadcaster(
|
||||||
|
//表明本应用发送的报文并没有一个确定的目的地,也就是进行广播
|
||||||
|
//就是往这个网络下 所有主机发送数据,往这些主机的 LogConst.MONITOR_SIDE_PORT端口 发送数据
|
||||||
|
//todo 这里的设置和 单播是有差异的
|
||||||
|
new InetSocketAddress("255.255.255.255",
|
||||||
|
LogConst.MONITOR_SIDE_PORT));
|
||||||
|
try {
|
||||||
|
System.out.println("广播服务启动");
|
||||||
|
broadcaster.run();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
broadcaster.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package org.opentcs.kc.udp.GB;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2024/12/15 10:56
|
||||||
|
*/
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.socket.DatagramPacket;
|
||||||
|
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||||
|
import io.netty.util.CharsetUtil;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 作者:DarkKIng
|
||||||
|
* 类说明:解码,将DatagramPacket解码为实际的日志实体类
|
||||||
|
*/
|
||||||
|
public class LogEventDecoder extends MessageToMessageDecoder<DatagramPacket> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void decode(ChannelHandlerContext ctx,
|
||||||
|
DatagramPacket datagramPacket, List<Object> out)
|
||||||
|
throws Exception {
|
||||||
|
//获取对 DatagramPacket 中的数据(ByteBuf)的引用
|
||||||
|
ByteBuf data = datagramPacket.content();
|
||||||
|
long time = new Date().getTime();
|
||||||
|
|
||||||
|
System.out.println(time+" 接受到发送的消息:");
|
||||||
|
//获得消息的id
|
||||||
|
long msgId = data.readLong();
|
||||||
|
//获得分隔符SEPARATOR
|
||||||
|
byte sepa = data.readByte();
|
||||||
|
//获取读索引的当前位置,就是分隔符的索引+1
|
||||||
|
int idx = data.readerIndex();
|
||||||
|
//提取日志消息,从读索引开始,到最后为日志的信息
|
||||||
|
String sendMsg = data.slice(idx ,
|
||||||
|
data.readableBytes()).toString(CharsetUtil.UTF_8);
|
||||||
|
//构建一个新的 LogMsg 对象,并且将它添加到(已经解码的消息的)列表中
|
||||||
|
LogMsg event = new LogMsg(datagramPacket.sender(),
|
||||||
|
msgId, sendMsg);
|
||||||
|
//作为本handler的处理结果,交给后面的handler进行处理
|
||||||
|
out.add(event);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package org.opentcs.kc.udp.GB;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2024/12/15 10:55
|
||||||
|
*/
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.socket.DatagramPacket;
|
||||||
|
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||||
|
import io.netty.util.CharsetUtil;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 作者:DarkKIng
|
||||||
|
* 类说明:编码,将实际的日志实体类编码为DatagramPacket
|
||||||
|
*/
|
||||||
|
public class LogEventEncoder extends MessageToMessageEncoder<LogMsg> {
|
||||||
|
private final InetSocketAddress remoteAddress;
|
||||||
|
|
||||||
|
//LogEventEncoder 创建了即将被发送到指定的 InetSocketAddress
|
||||||
|
// 的 DatagramPacket 消息
|
||||||
|
public LogEventEncoder(InetSocketAddress remoteAddress) {
|
||||||
|
this.remoteAddress = remoteAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void encode(ChannelHandlerContext channelHandlerContext,
|
||||||
|
LogMsg logMsg, List<Object> out) throws Exception {
|
||||||
|
byte[] msg = logMsg.getMsg().getBytes(CharsetUtil.UTF_8);
|
||||||
|
//容量的计算:两个long型+消息的内容+分割符
|
||||||
|
ByteBuf buf = channelHandlerContext.alloc()
|
||||||
|
.buffer(8*2 + msg.length + 1);
|
||||||
|
//将发送时间写入到 ByteBuf中
|
||||||
|
buf.writeLong(logMsg.getTime());
|
||||||
|
//将消息id写入到 ByteBuf中
|
||||||
|
buf.writeLong(logMsg.getMsgId());
|
||||||
|
//添加一个 SEPARATOR
|
||||||
|
buf.writeByte(LogMsg.SEPARATOR);
|
||||||
|
//将日志消息写入 ByteBuf中
|
||||||
|
buf.writeBytes(msg);
|
||||||
|
//将一个拥有数据和目的地地址的新 DatagramPacket 添加到出站的消息列表中
|
||||||
|
out.add(new DatagramPacket(buf, remoteAddress));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package org.opentcs.kc.udp.GB;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2024/12/15 10:57
|
||||||
|
*/
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.SimpleChannelInboundHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 作者:DarkKIng
|
||||||
|
* 类说明:日志的业务处理类,实际的业务处理,接受日志信息
|
||||||
|
*/
|
||||||
|
public class LogEventHandler
|
||||||
|
extends SimpleChannelInboundHandler<LogMsg> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exceptionCaught(ChannelHandlerContext ctx,
|
||||||
|
Throwable cause) throws Exception {
|
||||||
|
//当异常发生时,打印栈跟踪信息,并关闭对应的 Channel
|
||||||
|
cause.printStackTrace();
|
||||||
|
ctx.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelRead0(ChannelHandlerContext ctx,
|
||||||
|
LogMsg event) throws Exception {
|
||||||
|
//创建 StringBuilder,并且构建输出的字符串
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append(event.getTime());
|
||||||
|
builder.append(" [");
|
||||||
|
builder.append(event.getSource().toString());
|
||||||
|
builder.append("] :[");
|
||||||
|
builder.append(event.getMsgId());
|
||||||
|
builder.append("] :");
|
||||||
|
builder.append(event.getMsg());
|
||||||
|
//打印 LogMsg 的数据
|
||||||
|
System.out.println(builder.toString());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
package org.opentcs.kc.udp.GB;
|
||||||
|
|
||||||
|
import io.netty.bootstrap.Bootstrap;
|
||||||
|
import io.netty.channel.*;
|
||||||
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
|
import io.netty.channel.socket.nio.NioDatagramChannel;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang ( Server 端,接受消息 )
|
||||||
|
* @DATE: 2024/12/15 10:57
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class LogEventMonitor {
|
||||||
|
private final EventLoopGroup group;
|
||||||
|
private final Bootstrap bootstrap;
|
||||||
|
|
||||||
|
public LogEventMonitor(InetSocketAddress address) {
|
||||||
|
group = new NioEventLoopGroup();
|
||||||
|
bootstrap = new Bootstrap();
|
||||||
|
//引导该 NioDatagramChannel
|
||||||
|
bootstrap.group(group)
|
||||||
|
.channel(NioDatagramChannel.class)
|
||||||
|
//设置套接字选项 SO_BROADCAST
|
||||||
|
.option(ChannelOption.SO_BROADCAST, true)
|
||||||
|
//允许重用
|
||||||
|
.option(ChannelOption.SO_REUSEADDR,true)
|
||||||
|
.handler( new ChannelInitializer<Channel>() {
|
||||||
|
@Override
|
||||||
|
protected void initChannel(Channel channel)
|
||||||
|
throws Exception {
|
||||||
|
ChannelPipeline pipeline = channel.pipeline();
|
||||||
|
pipeline.addLast(new LogEventDecoder());
|
||||||
|
pipeline.addLast(new LogEventHandler());
|
||||||
|
}
|
||||||
|
} )
|
||||||
|
.localAddress(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Channel bind() {
|
||||||
|
//绑定 Channel。注意,DatagramChannel 是无连接的
|
||||||
|
return bootstrap.bind().syncUninterruptibly().channel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
group.shutdownGracefully();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
//构造一个新的 UdpAnswerSide并指明监听端口
|
||||||
|
LogEventMonitor monitor = new LogEventMonitor(
|
||||||
|
new InetSocketAddress(LogConst.MONITOR_SIDE_PORT));
|
||||||
|
try {
|
||||||
|
//绑定本地监听端口
|
||||||
|
Channel channel = monitor.bind();
|
||||||
|
System.out.println("UdpAnswerSide running");
|
||||||
|
channel.closeFuture().sync();
|
||||||
|
} finally {
|
||||||
|
monitor.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
package org.opentcs.kc.udp.GB;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2024/12/15 10:54
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 作者:DarkKIng
|
||||||
|
* 类说明:日志实体类
|
||||||
|
*/
|
||||||
|
public final class LogMsg {
|
||||||
|
public static final byte SEPARATOR = (byte) ':';
|
||||||
|
/*源的 InetSocketAddress*/
|
||||||
|
private final InetSocketAddress source;
|
||||||
|
/*消息内容*/
|
||||||
|
private final String msg;
|
||||||
|
/*消息id*/
|
||||||
|
private final long msgId;
|
||||||
|
/*消息发送的时间*/
|
||||||
|
private final long time;
|
||||||
|
|
||||||
|
//用于传入消息的构造函数
|
||||||
|
public LogMsg(String msg) {
|
||||||
|
this(null, msg,-1,System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
//用于传出消息的构造函数
|
||||||
|
public LogMsg(InetSocketAddress source, long msgId,
|
||||||
|
String msg) {
|
||||||
|
this(source,msg,msgId,System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogMsg(InetSocketAddress source, String msg, long msgId, long time) {
|
||||||
|
this.source = source;
|
||||||
|
this.msg = msg;
|
||||||
|
this.msgId = msgId;
|
||||||
|
this.time = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
//返回发送 LogMsg 的源的 InetSocketAddress
|
||||||
|
public InetSocketAddress getSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
//返回消息内容
|
||||||
|
public String getMsg() {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
//返回消息id
|
||||||
|
public long getMsgId() {
|
||||||
|
return msgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
//返回消息中的时间
|
||||||
|
public long getTime() {
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,407 @@
|
|||||||
|
package org.opentcs.kc.udp;
|
||||||
|
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
//import org.opentcs.kc.udp.agv.param.AgvEvent;
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
import org.opentcs.kc.udp.agv.param.AgvEvent;
|
||||||
|
import org.opentcs.kc.udp.agv.param.AgvEventConstant;
|
||||||
|
import org.opentcs.kc.udp.agv.param.function.b1.SubscribeInfo;
|
||||||
|
import org.opentcs.kc.udp.agv.param.function.b1.SubscribeParam;
|
||||||
|
import org.opentcs.kc.udp.agv.param.function.navigation.*;
|
||||||
|
import org.opentcs.kc.udp.agv.param.function.read.ReadParam;
|
||||||
|
import org.opentcs.kc.udp.agv.param.function.read.ReadStrValue;
|
||||||
|
import org.opentcs.kc.udp.agv.param.function.read.ReadValueMember;
|
||||||
|
import org.opentcs.kc.udp.agv.param.function.write.WriteParam;
|
||||||
|
import org.opentcs.kc.udp.agv.param.function.write.WriteStrValue;
|
||||||
|
import org.opentcs.kc.udp.agv.param.function.write.WriteValueMember;
|
||||||
|
import org.opentcs.kc.udp.agv.param.function.x14.RobotSetPosition;
|
||||||
|
import org.opentcs.kc.udp.agv.param.rsp.RcvEventPackage;
|
||||||
|
import org.opentcs.kc.udp.io.UDPClient;
|
||||||
|
import org.opentcs.kc.udp.agv.param.AgvEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* AGV启动:
|
||||||
|
* 0xAF(查询机器人状态) 👌
|
||||||
|
* 0xB1(下发订阅信令) 👌
|
||||||
|
* 初始化:
|
||||||
|
* 0x03(切换手自动) 👌
|
||||||
|
* 0x14(手动定位) 👌
|
||||||
|
* 0x17(查询机器人运行状态) 👌
|
||||||
|
* 0x1F(确认初始位置) 👌
|
||||||
|
* 运行:
|
||||||
|
* 0xAE(导航控制导航点控制) 👌
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/1/17 16:25
|
||||||
|
*/
|
||||||
|
public class KCCommandDemo {
|
||||||
|
public static void main(String [] args) throws Exception{
|
||||||
|
// {
|
||||||
|
// //0xAF(查询机器人状态)
|
||||||
|
// AgvEvent agvEvent = queryStatus();
|
||||||
|
// printInfo(agvEvent);
|
||||||
|
// RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
|
||||||
|
// if(rcv.isOk()){
|
||||||
|
// QueryRobotStatusRsp queryRobotStatusRsp = new QueryRobotStatusRsp(rcv.getDataBytes());
|
||||||
|
// System.out.println();
|
||||||
|
// System.out.println("received transationId : "+ "isok:"+rcv.isOk());
|
||||||
|
// for (byte b:rcv.getValue()){
|
||||||
|
// System.out.print(byteToHex(b)+" ");
|
||||||
|
// }
|
||||||
|
// }else {
|
||||||
|
// System.out.println();
|
||||||
|
// System.out.println("received transationId : "+ "isok:"+rcv.isOk());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// //0xB0(查询载货状态)
|
||||||
|
// AgvEvent agvEvent = checkCargoStatus();
|
||||||
|
// printInfo(agvEvent);
|
||||||
|
// RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
|
||||||
|
// if(rcv.isOk()){
|
||||||
|
// QueryCargoStatusRsp queryCargoStatusRsp = new QueryCargoStatusRsp(rcv.getDataBytes());
|
||||||
|
// System.out.println();
|
||||||
|
// System.out.println("received transationId : "+ "isok:"+rcv.isOk());
|
||||||
|
// for (byte b:rcv.getValue()){
|
||||||
|
// System.out.print(byteToHex(b)+" ");
|
||||||
|
// }
|
||||||
|
// }else {
|
||||||
|
// System.out.println();
|
||||||
|
// System.out.println("received transationId : "+ "isok:"+rcv.isOk());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// //0xB1(订阅信息)
|
||||||
|
// AgvEvent agvEvent = issueSubscribe();
|
||||||
|
// printInfo(agvEvent);
|
||||||
|
// //todo 订阅参数构建完毕 去写 回调部分
|
||||||
|
// RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
|
||||||
|
// if(rcv.isOk()){
|
||||||
|
// System.out.println();
|
||||||
|
// System.out.println("received transationId : "+ "isok:"+rcv.isOk());
|
||||||
|
// SubscribeRsp subscribeRsp = new SubscribeRsp(rcv.getDataBytes());
|
||||||
|
// if(subscribeRsp.isOk()){
|
||||||
|
// //...
|
||||||
|
// }else {
|
||||||
|
// //...
|
||||||
|
// }
|
||||||
|
// }else {
|
||||||
|
// System.out.println();
|
||||||
|
// System.out.println("received transationId : "+ "isok:"+rcv.isOk());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// //0x02(read操作)
|
||||||
|
// AgvEvent agvEvent = readValue();
|
||||||
|
// printInfo(agvEvent);
|
||||||
|
// RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
|
||||||
|
// if(rcv.isOk()){
|
||||||
|
// System.out.println();
|
||||||
|
// System.out.println("received transationId : "+ "isok:"+rcv.isOk());
|
||||||
|
// printInfo(rcv);
|
||||||
|
// ReadRsp readRsp = new ReadRsp(rcv.getDataBytes());
|
||||||
|
// if(readRsp.isOk()){
|
||||||
|
// //get and parse value
|
||||||
|
// System.out.println("read ok");
|
||||||
|
// }else {
|
||||||
|
// System.out.println("read failed");
|
||||||
|
// }
|
||||||
|
// }else {
|
||||||
|
// System.out.println();
|
||||||
|
// System.out.println("received transationId : "+ "isok:"+rcv.isOk());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// //0x03(切换手自动)
|
||||||
|
// AgvEvent agvEvent = writeValue();
|
||||||
|
// printInfo(agvEvent);
|
||||||
|
// RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
|
||||||
|
// if(rcv.isOk()){
|
||||||
|
// System.out.println();
|
||||||
|
// System.out.println("received "+ "isok:"+rcv.isOk()+" dataBytes:");
|
||||||
|
// printInfo(rcv);
|
||||||
|
// if(rcv.isOk()){
|
||||||
|
// //get and parse value
|
||||||
|
// System.out.println("write ok");
|
||||||
|
// }else {
|
||||||
|
// System.out.println("write failed");
|
||||||
|
// }
|
||||||
|
// }else {
|
||||||
|
// System.out.println();
|
||||||
|
// System.out.println("received transationId : "+ "isok:"+rcv.isOk());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// {
|
||||||
|
// //0x14(手动定位)
|
||||||
|
// AgvEvent agvEvent = manualLocation();
|
||||||
|
// printInfo(agvEvent);
|
||||||
|
// RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
|
||||||
|
// if(rcv.isOk()){
|
||||||
|
// System.out.println();
|
||||||
|
// System.out.println("received "+ "isok:"+rcv.isOk()+" dataBytes:");
|
||||||
|
// printInfo(rcv);
|
||||||
|
// if(rcv.isOk()){
|
||||||
|
// //get and parse value
|
||||||
|
// System.out.println("0x14 ok");
|
||||||
|
// }else {
|
||||||
|
// System.out.println("0x14 failed");
|
||||||
|
// }
|
||||||
|
// }else {
|
||||||
|
// System.out.println();
|
||||||
|
// System.out.println("received transationId : "+ "isok:"+rcv.isOk());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// {
|
||||||
|
// //0x17(查询机器人运行状态)
|
||||||
|
// AgvEvent agvEvent = queryRobotRunStatus();
|
||||||
|
// printInfo(agvEvent);
|
||||||
|
// RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
|
||||||
|
// if(rcv.isOk()){
|
||||||
|
// //QueryCargoStatusRsp queryCargoStatusRsp = new QueryCargoStatusRsp(rcv.getDataBytes());
|
||||||
|
// QueryRobotRunStatusRsp queryRobotRunStatusRsp = new QueryRobotRunStatusRsp(rcv.getDataBytes());
|
||||||
|
// System.out.println(queryRobotRunStatusRsp.toString());
|
||||||
|
// System.out.println();
|
||||||
|
// System.out.println("received transationId : "+ "isok:"+rcv.isOk());
|
||||||
|
// for (byte b:rcv.getValue()){
|
||||||
|
// System.out.print(byteToHex(b)+" ");
|
||||||
|
// }
|
||||||
|
// }else {
|
||||||
|
// System.out.println();
|
||||||
|
// System.out.println("received transationId : "+ "isok:"+rcv.isOk());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
{
|
||||||
|
//0x1F(确认初始位置)
|
||||||
|
AgvEvent agvEvent = confirmInitialPosition();
|
||||||
|
printInfo(agvEvent);
|
||||||
|
RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
|
||||||
|
if(rcv.isOk()){
|
||||||
|
System.out.println("0x1F ok");
|
||||||
|
}else {
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("0x1F fail");
|
||||||
|
System.out.println("received transationId : "+ "isok:"+rcv.isOk());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// {
|
||||||
|
// //0xAE(导航控制导航点控制)
|
||||||
|
// AgvEvent agvEvent = navigationControl();
|
||||||
|
// printInfo(agvEvent);
|
||||||
|
// RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
|
||||||
|
// if(rcv.isOk()){
|
||||||
|
// System.out.println("0xAE ok");
|
||||||
|
// }else {
|
||||||
|
// System.out.println();
|
||||||
|
// System.out.println("0xAE fail");
|
||||||
|
// System.out.println("received transationId : "+ "isok:"+rcv.isOk());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String byteToHex(byte b) {
|
||||||
|
// 将byte转换为无符号整数
|
||||||
|
int unsignedByte = b & 0xFF;
|
||||||
|
// 使用Integer.toHexString方法转换为十六进制字符串
|
||||||
|
String hexString = Integer.toHexString(unsignedByte);
|
||||||
|
// 如果字符串长度为1,需要在前面补0
|
||||||
|
if (hexString.length() == 1) {
|
||||||
|
return "0" + hexString;
|
||||||
|
}
|
||||||
|
return hexString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void printInfo(AgvEvent agvEvent){
|
||||||
|
System.out.println("sended transationId : "+agvEvent.getTransationIdString());
|
||||||
|
for (byte b:agvEvent.toBytes().getBody()){
|
||||||
|
System.out.print(byteToHex(b)+" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void printInfo(RcvEventPackage rcv){
|
||||||
|
|
||||||
|
for (byte b: rcv.getDataBytes()){
|
||||||
|
System.out.print(byteToHex(b)+" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* decs: read操作
|
||||||
|
* 指令:0x02
|
||||||
|
* author: caixiang
|
||||||
|
* date: 2025/1/17 16:25
|
||||||
|
* */
|
||||||
|
public static AgvEvent readValue() {
|
||||||
|
AgvEvent agvEvent = new AgvEvent(AgvEventConstant.CommandCode_READ);
|
||||||
|
List<ReadValueMember> readValueMemberList = new ArrayList<>();
|
||||||
|
ReadValueMember readValueMember1 = new ReadValueMember(Short.valueOf("0"),Short.valueOf("1"));
|
||||||
|
readValueMemberList.add(readValueMember1);
|
||||||
|
|
||||||
|
List<ReadStrValue> readStrValueList = new ArrayList<>();
|
||||||
|
ReadStrValue readStrValue = new ReadStrValue("Battry_SOC", 1, readValueMemberList);
|
||||||
|
readStrValueList.add(readStrValue);
|
||||||
|
|
||||||
|
ReadParam readParam = new ReadParam(agvEvent.getTransationId(),readStrValueList );
|
||||||
|
agvEvent.setBody(readParam.toBytes());
|
||||||
|
|
||||||
|
return agvEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* decs: write操作
|
||||||
|
* 指令:0x03
|
||||||
|
* author: caixiang
|
||||||
|
* date: 2025/1/17 16:25
|
||||||
|
* */
|
||||||
|
public static AgvEvent writeValue() {
|
||||||
|
AgvEvent agvEvent = new AgvEvent(AgvEventConstant.CommandCode_WRITE);
|
||||||
|
|
||||||
|
List<WriteValueMember> valueMemberList = new ArrayList<>();
|
||||||
|
WriteValueMember valueMember1 = new WriteValueMember(Short.valueOf("0"),Short.valueOf("4"), ByteUtils.uintToBytes(3, ByteOrder.LITTLE_ENDIAN));
|
||||||
|
valueMemberList.add(valueMember1);
|
||||||
|
|
||||||
|
List<WriteStrValue> strValueList = new ArrayList<>();
|
||||||
|
WriteStrValue strValue = new WriteStrValue("TestRW", 1, valueMemberList);
|
||||||
|
strValueList.add(strValue);
|
||||||
|
|
||||||
|
WriteParam param = new WriteParam(1,strValueList );
|
||||||
|
agvEvent.setBody(param.toBytes());
|
||||||
|
|
||||||
|
return agvEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* decs: 查询机器人状态
|
||||||
|
* 指令:0xAF
|
||||||
|
* author: caixiang
|
||||||
|
* date: 2025/1/17 16:25
|
||||||
|
* */
|
||||||
|
public static AgvEvent queryStatus() {
|
||||||
|
AgvEvent agvEvent = new AgvEvent(AgvEventConstant.CommandCode_QUERY_ROBOT_STATUS);
|
||||||
|
return agvEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* decs: 导航控制
|
||||||
|
* 指令:0xAE
|
||||||
|
* author: caixiang
|
||||||
|
* date: 2025/1/17 16:25
|
||||||
|
* */
|
||||||
|
public static AgvEvent navigationControl() {
|
||||||
|
AgvEvent agvEvent = new AgvEvent(AgvEventConstant.CommandCode_QUERY_ROBOT_STATUS);
|
||||||
|
//TODO 构建
|
||||||
|
Integer orderId = 1;
|
||||||
|
|
||||||
|
//构建point
|
||||||
|
Action[] pointActions1 = new Action[]{
|
||||||
|
new Action(ActionSet.stop0x01, (byte) 0x00, 1, ActionSet.stop0x01_paramsize, ActionSet.stop0x01(orderId, (byte) 0x01))
|
||||||
|
//,new Action()... 每一个point 可以绑定一个或者多个 action
|
||||||
|
};
|
||||||
|
Action[] pointActions2 = new Action[]{
|
||||||
|
new Action(ActionSet.stop0x01, (byte) 0x00, 1, ActionSet.stop0x01_paramsize, ActionSet.stop0x01(orderId, (byte) 0x01))
|
||||||
|
};
|
||||||
|
Action[] pointActions3 = new Action[]{
|
||||||
|
new Action(ActionSet.stop0x01, (byte) 0x00, 1, ActionSet.stop0x01_paramsize, ActionSet.stop0x01(orderId, (byte) 0x01))
|
||||||
|
};
|
||||||
|
Point[] points = new Point[]{
|
||||||
|
new Point(0, 1, 1f, (byte)0x00, ByteUtils.usintTo1Byte(pointActions1.length),pointActions1),
|
||||||
|
new Point(2, 2, 1f, (byte)0x00, ByteUtils.usintTo1Byte(pointActions2.length),pointActions2),
|
||||||
|
new Point(4, 3, 1f, (byte)0x00, ByteUtils.usintTo1Byte(pointActions3.length),pointActions3)
|
||||||
|
};
|
||||||
|
|
||||||
|
//构建path
|
||||||
|
Action[] pathActions1 = new Action[]{
|
||||||
|
new Action(ActionSet.stop0x01, (byte) 0x00, 1, ActionSet.stop0x01_paramsize, ActionSet.stop0x01(orderId, (byte) 0x01))
|
||||||
|
//,new Action()... 每一个path 可以绑定一个或者多个 action
|
||||||
|
};
|
||||||
|
Action[] pathActions2 = new Action[]{
|
||||||
|
new Action(ActionSet.stop0x01, (byte) 0x00, 1, ActionSet.stop0x01_paramsize, ActionSet.stop0x01(orderId, (byte) 0x01))
|
||||||
|
};
|
||||||
|
Path[] paths = new Path[]{
|
||||||
|
new Path(1,1,1f,(byte)0x00,(byte)0x00,ByteUtils.usintTo1Byte(pathActions1.length),5f,1f,pathActions1) ,
|
||||||
|
new Path(3,2,1f,(byte)0x00,(byte)0x00,ByteUtils.usintTo1Byte(pathActions2.length),5f,1f,pathActions2) ,
|
||||||
|
};
|
||||||
|
NavigationParam navigationParam = new NavigationParam(1,1,ByteUtils.usintTo1Byte(points.length),ByteUtils.usintTo1Byte(points.length-1),points,paths);
|
||||||
|
agvEvent.setBody(navigationParam.toBytes());
|
||||||
|
|
||||||
|
return agvEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* decs: 确认机器人位置
|
||||||
|
* 指令:0x1F
|
||||||
|
* author: caixiang
|
||||||
|
* date: 2025/1/17 16:25
|
||||||
|
* */
|
||||||
|
public static AgvEvent confirmInitialPosition() {
|
||||||
|
AgvEvent agvEvent = new AgvEvent(AgvEventConstant.CommandCode_CONFIRM_ROBOT_POSITION);
|
||||||
|
return agvEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* decs: 查询机器人运行状态
|
||||||
|
* 指令:0x17
|
||||||
|
* author: caixiang
|
||||||
|
* date: 2025/1/17 16:25
|
||||||
|
* */
|
||||||
|
public static AgvEvent queryRobotRunStatus() {
|
||||||
|
AgvEvent agvEvent = new AgvEvent(AgvEventConstant.CommandCode_QUERY_ROBOT_RUN_STATUS);
|
||||||
|
return agvEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AgvEvent manualLocation() {
|
||||||
|
AgvEvent agvEvent = new AgvEvent(AgvEventConstant.CommandCode_ROBOT_SET_POSITION);
|
||||||
|
RobotSetPosition robotSetPosition = new RobotSetPosition(11, 11, 11);
|
||||||
|
byte[] bytes = robotSetPosition.toBytes();
|
||||||
|
agvEvent.setBody(bytes);
|
||||||
|
return agvEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* decs: 查询载货状态
|
||||||
|
* 指令:0xB0
|
||||||
|
* author: caixiang
|
||||||
|
* date: 2025/1/17 16:25
|
||||||
|
* */
|
||||||
|
public static AgvEvent checkCargoStatus() {
|
||||||
|
AgvEvent agvEvent = new AgvEvent(AgvEventConstant.CommandCode_QUERY_CARRY_STATUS);
|
||||||
|
return agvEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* decs: 下发订阅信息
|
||||||
|
* 指令:0xB1
|
||||||
|
* author: caixiang
|
||||||
|
* date: 2025/1/17 16:25
|
||||||
|
* */
|
||||||
|
public static AgvEvent issueSubscribe() {
|
||||||
|
List<SubscribeInfo> subscribeInfoList = new ArrayList<>();
|
||||||
|
SubscribeInfo subscribeInfo = new SubscribeInfo(new byte[]{(byte)0xaf,(byte)0x00}, (short) 100,1000);
|
||||||
|
//SubscribeInfo subscribeInfo = new SubscribeInfo(new byte[]{(byte)0xb0,(byte)0x00}, (short) 100,1000);
|
||||||
|
subscribeInfoList.add(subscribeInfo);
|
||||||
|
SubscribeParam subscribeParam = new SubscribeParam(subscribeInfoList);
|
||||||
|
AgvEvent agvEvent = new AgvEvent(AgvEventConstant.CommandCode_ISSUE_SUBSCRIPTION,subscribeParam.toBytes());
|
||||||
|
return agvEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.codec;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelInitializer;
|
||||||
|
import io.netty.channel.socket.nio.NioDatagramChannel;
|
||||||
|
import org.opentcs.kc.udp.io.UDPClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 作者:蔡翔
|
||||||
|
*/
|
||||||
|
public class AgvUdpChannelInitializer extends ChannelInitializer<NioDatagramChannel> {
|
||||||
|
private UDPClient client;
|
||||||
|
|
||||||
|
public AgvUdpChannelInitializer(UDPClient client) {
|
||||||
|
System.out.println(client.getHost());
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initChannel(NioDatagramChannel channel) throws Exception {
|
||||||
|
// Modbus
|
||||||
|
// 在管道中添加我们自己的接收数据实现方法
|
||||||
|
//todo 到时候这里改成 FixedLengthFrameDecoder 资料:https://www.cnblogs.com/java-chen-hao/p/11571229.html
|
||||||
|
// channel.pipeline().addLast(
|
||||||
|
// new LengthFieldBasedFrameDecoder(
|
||||||
|
// 200,
|
||||||
|
// 4,
|
||||||
|
// 2,
|
||||||
|
// 1,
|
||||||
|
// 7,
|
||||||
|
// true)
|
||||||
|
// );
|
||||||
|
channel.pipeline().addLast(new AgvUdpDecode(this.client)); //InBoundHandler
|
||||||
|
//channel.pipeline().addLast(new AgvUdpEncode()); //OutBoundHandler
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.codec;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.SimpleChannelInboundHandler;
|
||||||
|
import io.netty.channel.socket.DatagramPacket;
|
||||||
|
import org.opentcs.kc.common.Package;
|
||||||
|
import org.opentcs.kc.syn.SendedList;
|
||||||
|
import org.opentcs.kc.udp.agv.param.rsp.RcvEventPackage;
|
||||||
|
import org.opentcs.kc.udp.io.UDPClient;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2024-06-06 10:45
|
||||||
|
*/
|
||||||
|
public class AgvUdpDecode extends SimpleChannelInboundHandler<DatagramPacket> {
|
||||||
|
|
||||||
|
private UDPClient client;
|
||||||
|
|
||||||
|
//授权码16字节 + 报文头8个字节 + 报文数据长度2个字节 + 保留2字节 = 28字节
|
||||||
|
private static final int HEADER_SIZE = 28;
|
||||||
|
|
||||||
|
public AgvUdpDecode(UDPClient client) {
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg)
|
||||||
|
throws Exception {
|
||||||
|
//获得应答,DatagramPacket提供了content()方法取得报文的实际内容
|
||||||
|
ByteBuf in = msg.content();
|
||||||
|
if (in.readableBytes() < HEADER_SIZE){
|
||||||
|
throw new Exception("readed bytes < header length");
|
||||||
|
}
|
||||||
|
|
||||||
|
int dataLength = in.getShortLE(24);
|
||||||
|
if (dataLength < 0) {
|
||||||
|
throw new Exception("bodyLength [" + dataLength + "] is not right, remote:" + ctx.channel().remoteAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
int neededLength = HEADER_SIZE + dataLength;
|
||||||
|
int isDataEnough = in.readableBytes() - neededLength;
|
||||||
|
|
||||||
|
|
||||||
|
//收到的数据是否足够组包
|
||||||
|
if(isDataEnough<0){
|
||||||
|
// 不够消息体长度(剩下的buffe组不了消息体),重新去组包
|
||||||
|
throw new Exception("readed bytes < content length");
|
||||||
|
}else {
|
||||||
|
|
||||||
|
//todo这里重写subscribe 的逻辑,注意要区分是 订阅的还是 主动请求的。
|
||||||
|
//组包成功
|
||||||
|
byte[] body = new byte[neededLength];
|
||||||
|
in.readBytes(body);
|
||||||
|
//System.out.println("received bytes :"+ Arrays.toString(body));
|
||||||
|
String uuid = body[18]+"-"+body[19];
|
||||||
|
Package mbPackage = new Package(body,uuid);
|
||||||
|
byte commandCode = body[21];
|
||||||
|
|
||||||
|
if(body[18]==(byte)0x00 && body[19]==(byte)0x00){
|
||||||
|
if(commandCode == (byte)0xAF ){
|
||||||
|
client.subscribe0xAF(new RcvEventPackage(body[22],body));
|
||||||
|
}else if(commandCode == (byte)0xB0){
|
||||||
|
client.subscribe0xB0(new RcvEventPackage(body[22],body));
|
||||||
|
}else {
|
||||||
|
SendedList.set(uuid , mbPackage);
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
SendedList.set(uuid , mbPackage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
|
||||||
|
throws Exception {
|
||||||
|
cause.printStackTrace();
|
||||||
|
ctx.close();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import org.opentcs.kc.common.Package;
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtil;
|
||||||
|
//import org.opentcs.kc.common.byteutils.ByteUtil;
|
||||||
|
//import org.opentcs.kc.common.byteutils.ByteUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2024/12/27 15:59
|
||||||
|
*/
|
||||||
|
public class AgvEvent extends AgvEventHeader implements IAgvEvent, Serializable {
|
||||||
|
private byte[] bodyLength;
|
||||||
|
private byte[] retain;
|
||||||
|
private byte[] body;
|
||||||
|
|
||||||
|
//for Request
|
||||||
|
//有content 传,没有content 传new byte[]
|
||||||
|
public AgvEvent(Byte commandCode) {
|
||||||
|
super(commandCode);
|
||||||
|
//初始化
|
||||||
|
bodyLength = new byte[]{0x00,0x00};
|
||||||
|
retain = new byte[]{0x00,0x00};
|
||||||
|
body = new byte[]{};
|
||||||
|
}
|
||||||
|
public AgvEvent(Byte commandCode,byte[] body) {
|
||||||
|
super(commandCode);
|
||||||
|
//初始化
|
||||||
|
bodyLength = new byte[]{0x00,0x00};
|
||||||
|
retain = new byte[]{0x00,0x00};
|
||||||
|
if(commandCode.equals(AgvEventConstant.CommandCode_ISSUE_SUBSCRIPTION)){
|
||||||
|
//依据命令码 构建参数
|
||||||
|
this.bodyLength = ByteUtil.shortToBytes((short) body.length);
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBody(byte[] body) {
|
||||||
|
this.bodyLength = ByteUtil.shortToBytes((short) body.length);
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
//for Response
|
||||||
|
public AgvEvent(Byte serviceCode, Byte commandCode, Byte executionCode) {
|
||||||
|
super(serviceCode, commandCode, executionCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Package toBytes() {
|
||||||
|
List<Byte> headerBytes = getHeaderBytes();
|
||||||
|
for (Byte b : bodyLength) {
|
||||||
|
headerBytes.add(b);
|
||||||
|
}
|
||||||
|
for (Byte b : retain) {
|
||||||
|
headerBytes.add(b);
|
||||||
|
}
|
||||||
|
for (Byte b : body) {
|
||||||
|
headerBytes.add(b);
|
||||||
|
}
|
||||||
|
byte[] bytes = new byte[headerBytes.size()];
|
||||||
|
for (int i = 0; i < headerBytes.size(); i++) {
|
||||||
|
bytes[i] = headerBytes.get(i);
|
||||||
|
}
|
||||||
|
return new Package(bytes, getTransationIdString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTransationIdString() {
|
||||||
|
return transationId[0]+"-"+transationId[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Package toBytes(Object newValue) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "AgvEvent{" +
|
||||||
|
"Header=" + headerToString() +
|
||||||
|
", bodyLength=" + Arrays.toString(bodyLength) +
|
||||||
|
", body=" + Arrays.toString(body) +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String protocolName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2024/12/27 10:19
|
||||||
|
*/
|
||||||
|
public class AgvEventConstant {
|
||||||
|
|
||||||
|
public static final byte[] AuthorizationCode = new byte[]{ (byte)0xd4 , (byte)0x97 , (byte)0x44 , (byte)0x9c , (byte)0xcb , (byte)0xcf , (byte)0x0b , (byte)0x4c , (byte)0x95 , (byte)0x51 , (byte)0xd8 , (byte)0x61 , (byte)0x70 , (byte)0xf1 , (byte)0xe7 , (byte)0x94};
|
||||||
|
|
||||||
|
|
||||||
|
public static final byte VersionNum = 0x01;
|
||||||
|
public static final byte MSGTypeRequest = 0x00;
|
||||||
|
public static final byte MSGTypeResponse = 0x01;
|
||||||
|
|
||||||
|
//命令码 开始
|
||||||
|
public static final byte ServiceCode = 0x10;
|
||||||
|
|
||||||
|
|
||||||
|
public static final byte CommandCode_READ = 0x02;
|
||||||
|
public static final byte CommandCode_WRITE = 0x03;
|
||||||
|
public static final byte CommandCode_MIXED_ISSUANCE_TASK = (byte) 0xAE;
|
||||||
|
public static final byte CommandCode_QUERY_ROBOT_STATUS = (byte) 0xAF;
|
||||||
|
public static final byte CommandCode_QUERY_CARRY_STATUS = (byte) 0xB0;
|
||||||
|
public static final byte CommandCode_ISSUE_SUBSCRIPTION = (byte) 0xB1;
|
||||||
|
public static final byte CommandCode_ACT_IMMEDIATELY = (byte) 0xB2;
|
||||||
|
public static final byte CommandCode_SET_ABILITY= (byte) 0xB7;
|
||||||
|
public static final byte CommandCode_ROBOT_SET_POSITION= (byte) 0x14;
|
||||||
|
public static final byte CommandCode_GET_ROBOT_POSITION= (byte) 0x15;
|
||||||
|
public static final byte CommandCode_NAVIGATION_CONTROL= (byte) 0x16;
|
||||||
|
public static final byte CommandCode_QUERY_ROBOT_NAVIGATION_STATUS= (byte) 0x1D;
|
||||||
|
public static final byte CommandCode_QUERY_ROBOT_RUN_STATUS= (byte) 0x17;
|
||||||
|
public static final byte CommandCode_CONFIRM_ROBOT_POSITION= (byte) 0x1F;
|
||||||
|
//命令码 结束
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,112 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import org.opentcs.kc.common.CaffeineUtil;
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2024-06-07 13:29
|
||||||
|
*/
|
||||||
|
public class AgvEventHeader {
|
||||||
|
|
||||||
|
|
||||||
|
//常量区域 开始
|
||||||
|
//授权码
|
||||||
|
public byte[] authorizationCode;
|
||||||
|
//header 开始
|
||||||
|
//版本号
|
||||||
|
public byte versionNum;
|
||||||
|
//报文类型 0x00:请求报文 ; 0x01:应答报文
|
||||||
|
public byte msgType;
|
||||||
|
//request 和 response ,transationId一致,2个字节
|
||||||
|
public byte[] transationId;
|
||||||
|
//服务码
|
||||||
|
public byte serviceCode;
|
||||||
|
//命令码,用于区分不同命令,request 和 response 相同
|
||||||
|
public byte commandCode;
|
||||||
|
//执行码,应答报文填写,表明命令执行情况,请求数据包置 0
|
||||||
|
public byte executionCode;
|
||||||
|
//保留
|
||||||
|
public byte retain;
|
||||||
|
|
||||||
|
//header 结束
|
||||||
|
//常量区域 结束
|
||||||
|
|
||||||
|
|
||||||
|
//request
|
||||||
|
public AgvEventHeader(Byte commandCode) {
|
||||||
|
this.authorizationCode = AgvEventConstant.AuthorizationCode;
|
||||||
|
this.versionNum = AgvEventConstant.VersionNum;
|
||||||
|
this.msgType = AgvEventConstant.MSGTypeRequest;
|
||||||
|
|
||||||
|
this.transationId = CaffeineUtil.getUUIDAGV();
|
||||||
|
this.serviceCode = 0x10;
|
||||||
|
this.commandCode = commandCode;
|
||||||
|
this.executionCode = 0x00;
|
||||||
|
this.retain = 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AgvEventHeader(byte[] src){
|
||||||
|
this.authorizationCode = ByteUtils.copyOfRange(src,0,16);
|
||||||
|
this.versionNum = src[16];
|
||||||
|
this.msgType = src[17];
|
||||||
|
this.transationId = ByteUtils.copyOfRange(src,18,20);
|
||||||
|
this.serviceCode = src[20];
|
||||||
|
this.commandCode = src[21];
|
||||||
|
this.executionCode = src[22];
|
||||||
|
this.retain = src[23];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//response
|
||||||
|
public AgvEventHeader(Byte serviceCode, Byte commandCode, Byte executionCode) {
|
||||||
|
this.authorizationCode = AgvEventConstant.AuthorizationCode;
|
||||||
|
this.versionNum = AgvEventConstant.VersionNum;
|
||||||
|
this.msgType = AgvEventConstant.MSGTypeResponse;
|
||||||
|
this.transationId = CaffeineUtil.getUUIDAGV();
|
||||||
|
this.serviceCode = serviceCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getTransationId() {
|
||||||
|
return transationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String headerToString() {
|
||||||
|
return "AgvEventHeader{" +
|
||||||
|
"authorizationCode=" + Arrays.toString(authorizationCode) +
|
||||||
|
", versionNum=" + versionNum +
|
||||||
|
", msgType=" + msgType +
|
||||||
|
", transationId=" + Arrays.toString(transationId) +
|
||||||
|
", serviceCode=" + serviceCode +
|
||||||
|
", commandCode=" + commandCode +
|
||||||
|
", executionCode=" + executionCode +
|
||||||
|
", retain=" + retain +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Byte> getHeaderBytes(){
|
||||||
|
List<Byte> bytes = new ArrayList<>();
|
||||||
|
//add 授权码
|
||||||
|
for (byte b : authorizationCode) {
|
||||||
|
bytes.add(b);
|
||||||
|
}
|
||||||
|
//add 协议版本号
|
||||||
|
bytes.add(versionNum);
|
||||||
|
//add 报文类型
|
||||||
|
bytes.add(msgType);
|
||||||
|
//add 报文标识
|
||||||
|
for (byte b : transationId) {
|
||||||
|
bytes.add(b);
|
||||||
|
}
|
||||||
|
bytes.add(serviceCode);
|
||||||
|
bytes.add(commandCode);
|
||||||
|
bytes.add(executionCode);
|
||||||
|
bytes.add(retain);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param;
|
||||||
|
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtil;
|
||||||
|
import org.opentcs.kc.common.Package;
|
||||||
|
public interface IAgvEvent {
|
||||||
|
//read sended
|
||||||
|
public Package toBytes();
|
||||||
|
//write sended
|
||||||
|
public Package toBytes(Object newValue);
|
||||||
|
|
||||||
|
public String protocolName();
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.af;
|
||||||
|
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/1/21 10:03
|
||||||
|
*/
|
||||||
|
public class AbnormalEventStatusInfo {
|
||||||
|
private byte[] src;
|
||||||
|
//事件码,2个字节
|
||||||
|
public byte[] eventCode;
|
||||||
|
//异常等级,2个字节
|
||||||
|
public byte[] abnormalLevel;
|
||||||
|
//remain,8个字节
|
||||||
|
public byte[] remain;
|
||||||
|
|
||||||
|
public AbnormalEventStatusInfo(byte[] src) {
|
||||||
|
this.src = src;
|
||||||
|
this.eventCode = ByteUtils.copyBytes(src, 0, 2);
|
||||||
|
this.abnormalLevel = ByteUtils.copyBytes(src, 2, 2);
|
||||||
|
this.remain = ByteUtils.copyBytes(src, 4, 8);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.af;
|
||||||
|
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/1/21 10:38
|
||||||
|
*/
|
||||||
|
public class ActionInfo {
|
||||||
|
private byte[] src;
|
||||||
|
//动作 ID,4个字节
|
||||||
|
public byte[] actionId;
|
||||||
|
//动作状态,1个字节
|
||||||
|
public byte actionStatus;
|
||||||
|
//预留,7个字节
|
||||||
|
public byte[] remain;
|
||||||
|
|
||||||
|
public ActionInfo(byte[] src) {
|
||||||
|
this.src = src;
|
||||||
|
this.actionId = ByteUtils.copyBytes(src, 0, 4);
|
||||||
|
this.actionStatus = src[4];
|
||||||
|
this.remain = ByteUtils.copyBytes(src,5,7);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.af;
|
||||||
|
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/1/20 16:28
|
||||||
|
*/
|
||||||
|
public class BatteryStatusInfo {
|
||||||
|
//src
|
||||||
|
private byte[] src;
|
||||||
|
//电量百分比
|
||||||
|
public float batteryPercentage;
|
||||||
|
//电压
|
||||||
|
public float voltage;
|
||||||
|
//电流
|
||||||
|
public float electricCurrent;
|
||||||
|
//充电情况,1个字节
|
||||||
|
public byte chargingState;
|
||||||
|
//预留,7个字节
|
||||||
|
public byte[] remain;
|
||||||
|
public BatteryStatusInfo(byte[] src) {
|
||||||
|
this.src = src;
|
||||||
|
this.batteryPercentage = ByteUtils.bytesToFloat(src, 0);
|
||||||
|
this.voltage = ByteUtils.bytesToFloat(src, 4);
|
||||||
|
this.electricCurrent = ByteUtils.bytesToFloat(src, 8);
|
||||||
|
this.chargingState = src[12];
|
||||||
|
this.remain=new byte[7];
|
||||||
|
for (int i = 0; i < 7; i++) {
|
||||||
|
this.remain[i]=src[13+i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.af;
|
||||||
|
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/1/20 14:56
|
||||||
|
*/
|
||||||
|
public class LocationStatusInfo {
|
||||||
|
private byte[] src;
|
||||||
|
//4个字节,车 全局x,单位 m
|
||||||
|
public float globalX;
|
||||||
|
//4个字节,车 全局y,单位 m
|
||||||
|
public float globalY;
|
||||||
|
//机器人绝对车体方向角,单位 rad
|
||||||
|
public float absoluteDirecAngle;
|
||||||
|
//最后通过点 ID
|
||||||
|
public Integer lastPassPointId;
|
||||||
|
//最后通过edge ID
|
||||||
|
public Integer lastPassEdgeId;
|
||||||
|
//最后通过点在任务中的序列号
|
||||||
|
public Integer serialNumber;
|
||||||
|
//置信度,1个字节
|
||||||
|
public byte confidenceLevel;
|
||||||
|
//预留8个字节
|
||||||
|
public byte[] remain;
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
byte[] src = new byte[32];
|
||||||
|
src[0] = (byte) 0x73;
|
||||||
|
src[1] = (byte) 0xde;
|
||||||
|
src[2] = (byte) 0xba;
|
||||||
|
src[3] = (byte) 0xbd;
|
||||||
|
|
||||||
|
src[4] = (byte) 0x34;
|
||||||
|
src[5] = (byte) 0xbf;
|
||||||
|
src[6] = (byte) 0xd0;
|
||||||
|
src[7] = (byte) 0x40;
|
||||||
|
|
||||||
|
src[8] = (byte) 0x59;
|
||||||
|
src[9] = (byte) 0x3c;
|
||||||
|
src[10] = (byte) 0x1d;
|
||||||
|
src[11] = (byte) 0x3d;
|
||||||
|
System.out.println("x: "+ByteUtils.bytesToFloat(src, 0));
|
||||||
|
System.out.println("y: "+ByteUtils.bytesToFloat(src, 4));
|
||||||
|
System.out.println("z: "+ByteUtils.bytesToFloat(src, 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocationStatusInfo(byte[] src) {
|
||||||
|
this.src = src;
|
||||||
|
for (byte b:src){
|
||||||
|
System.out.print(byteToHex(b)+" ");
|
||||||
|
}
|
||||||
|
//[115, -34, -70, -67, 52, -65, -48, 64, 89, 60, 29, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
this.globalX = ByteUtils.bytesToFloat(src, 0);
|
||||||
|
this.globalY = ByteUtils.bytesToFloat(src, 4);
|
||||||
|
this.absoluteDirecAngle = ByteUtils.bytesToFloat(src, 8);
|
||||||
|
this.lastPassPointId = ByteUtils.bytesToInt(src, 12);
|
||||||
|
this.lastPassEdgeId = ByteUtils.bytesToInt(src, 16);
|
||||||
|
this.serialNumber = ByteUtils.bytesToInt(src, 20);
|
||||||
|
this.confidenceLevel = src[24];
|
||||||
|
this.remain = new byte[7];
|
||||||
|
for (int i = 0; i < 7; i++) {
|
||||||
|
this.remain[i] = src[25 + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String byteToHex(byte b) {
|
||||||
|
// 将byte转换为无符号整数
|
||||||
|
int unsignedByte = b & 0xFF;
|
||||||
|
// 使用Integer.toHexString方法转换为十六进制字符串
|
||||||
|
String hexString = Integer.toHexString(unsignedByte);
|
||||||
|
// 如果字符串长度为1,需要在前面补0
|
||||||
|
if (hexString.length() == 1) {
|
||||||
|
return "0" + hexString;
|
||||||
|
}
|
||||||
|
return hexString;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.af;
|
||||||
|
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/1/20 15:49
|
||||||
|
*/
|
||||||
|
public class PathStateSequence {
|
||||||
|
//src
|
||||||
|
private byte[] src;
|
||||||
|
//序列号,4个字节
|
||||||
|
public Integer serialNumber;
|
||||||
|
//序列号
|
||||||
|
public Integer pathId;
|
||||||
|
public PathStateSequence(byte[] src) {
|
||||||
|
this.src = src;
|
||||||
|
this.serialNumber = ByteUtils.bytesToInt(src, 0);
|
||||||
|
this.pathId = ByteUtils.bytesToInt(src, 4);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.af;
|
||||||
|
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/1/20 15:49
|
||||||
|
*/
|
||||||
|
public class PointStateSequence {
|
||||||
|
//src
|
||||||
|
private byte[] src;
|
||||||
|
//序列号,4个字节
|
||||||
|
public Integer serialNumber;
|
||||||
|
//序列号
|
||||||
|
public Integer pointId;
|
||||||
|
public PointStateSequence(byte[] src) {
|
||||||
|
this.src = src;
|
||||||
|
this.serialNumber = ByteUtils.bytesToInt(src, 0);
|
||||||
|
this.pointId = ByteUtils.bytesToInt(src, 4);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.af;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/1/20 14:55
|
||||||
|
*/
|
||||||
|
public class QueryRobotStatusRsp {
|
||||||
|
private byte[] src;
|
||||||
|
public byte abnormal_size;
|
||||||
|
public byte action_size;
|
||||||
|
//预留2个字节
|
||||||
|
public byte[] remain;
|
||||||
|
public LocationStatusInfo locationStatusInfo;
|
||||||
|
public RunningStatusInfo runningStatusInfo;
|
||||||
|
public TaskStatusInfo taskStatusInfo;
|
||||||
|
public BatteryStatusInfo batteryStatusInfo;
|
||||||
|
public List<AbnormalEventStatusInfo> abnormalEventStatusInfoList;
|
||||||
|
public List<ActionInfo> actionInfoList;
|
||||||
|
|
||||||
|
public QueryRobotStatusRsp(byte[] src) {
|
||||||
|
this.src = src;
|
||||||
|
this.abnormal_size = src[0];
|
||||||
|
this.action_size = src[1];
|
||||||
|
this.remain = ByteUtils.copyBytes(src,2,2); //index 2
|
||||||
|
this.locationStatusInfo = new LocationStatusInfo(ByteUtils.copyBytes(src,4,32));
|
||||||
|
this.runningStatusInfo = new RunningStatusInfo(ByteUtils.copyBytes(src,36,20));
|
||||||
|
|
||||||
|
Integer pointSize = ByteUtils.toInt(src[60]);
|
||||||
|
Integer edgeSize = ByteUtils.toInt(src[61]);
|
||||||
|
Integer taskByteSize = 12+8*(pointSize+edgeSize);
|
||||||
|
this.taskStatusInfo = new TaskStatusInfo(ByteUtils.copyBytes(src,56,taskByteSize));
|
||||||
|
this.batteryStatusInfo = new BatteryStatusInfo(ByteUtils.copyBytes(src,56+taskByteSize,20));
|
||||||
|
|
||||||
|
if(this.abnormal_size>0){
|
||||||
|
for(int i=0;i<this.abnormal_size;i++){
|
||||||
|
this.abnormalEventStatusInfoList.add(new AbnormalEventStatusInfo(ByteUtils.copyBytes(src,56+taskByteSize+20+12*i,12)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(this.action_size>0){
|
||||||
|
for(int i=0;i<this.action_size;i++){
|
||||||
|
this.actionInfoList.add(new ActionInfo(ByteUtils.copyBytes(src,56+taskByteSize+20+12*this.abnormal_size+12*i,12)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.af;
|
||||||
|
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/1/20 15:22
|
||||||
|
*/
|
||||||
|
public class RunningStatusInfo {
|
||||||
|
private byte[] src;
|
||||||
|
//4个字节,车 轴x 速度,单位 m/s
|
||||||
|
public float speedX;
|
||||||
|
//4个字节,车 轴y 速度,单位 m/s
|
||||||
|
public float speedY;
|
||||||
|
//车角速度,单位 rad/s
|
||||||
|
public float angularSpeed;
|
||||||
|
//工作模式
|
||||||
|
public byte workMode;
|
||||||
|
//agv 状态,1个字节
|
||||||
|
public byte agvStatus;
|
||||||
|
//机器人能力集设置状态,1个字节
|
||||||
|
public byte abilitySetStatus;
|
||||||
|
//预留,5个字节
|
||||||
|
public byte[] remain;
|
||||||
|
|
||||||
|
public RunningStatusInfo(byte[] src) {
|
||||||
|
this.src = src;
|
||||||
|
this.speedX = ByteUtils.bytesToFloat(src, 0);
|
||||||
|
this.speedY = ByteUtils.bytesToFloat(src, 4);
|
||||||
|
this.angularSpeed = ByteUtils.bytesToFloat(src, 8);
|
||||||
|
this.workMode = src[12];
|
||||||
|
this.agvStatus = src[13];
|
||||||
|
this.abilitySetStatus = src[14];
|
||||||
|
this.remain = new byte[5];
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
this.remain[i] = src[15 + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.af;
|
||||||
|
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/1/20 15:22
|
||||||
|
*/
|
||||||
|
public class TaskStatusInfo {
|
||||||
|
private byte[] src;
|
||||||
|
//订单 ID
|
||||||
|
public Integer orderId;
|
||||||
|
//任务 KEY
|
||||||
|
public Integer taskKey;
|
||||||
|
//点状态序列数量
|
||||||
|
public Integer pointStatusNum;
|
||||||
|
//段状态序列数量
|
||||||
|
public Integer edgeStatusNum;
|
||||||
|
//预留,2个字节
|
||||||
|
public byte[] remain;
|
||||||
|
//点状态序列
|
||||||
|
public PointStateSequence[] pointStatusInfo;
|
||||||
|
//段状态序列
|
||||||
|
public PathStateSequence[] pathStatusInfo;
|
||||||
|
|
||||||
|
public TaskStatusInfo(byte[] src) {
|
||||||
|
this.src = src;
|
||||||
|
this.orderId = ByteUtils.bytesToInt(src, 0);
|
||||||
|
this.taskKey = ByteUtils.bytesToInt(src, 4);
|
||||||
|
|
||||||
|
this.pointStatusNum = ByteUtils.toInt(src[8]);
|
||||||
|
this.edgeStatusNum = ByteUtils.toInt(src[9]);
|
||||||
|
this.remain = ByteUtils.copyBytes(src, 10, 2);
|
||||||
|
if (pointStatusNum > 0) {
|
||||||
|
this.pointStatusInfo = new PointStateSequence[pointStatusNum];
|
||||||
|
for (int i = 0; i < pointStatusNum; i++) {
|
||||||
|
this.pointStatusInfo[i] = new PointStateSequence(ByteUtils.copyBytes(src, 12 + 8 * i, 8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (edgeStatusNum > 0) {
|
||||||
|
this.pathStatusInfo = new PathStateSequence[edgeStatusNum];
|
||||||
|
for (int i = 0; i < edgeStatusNum; i++) {
|
||||||
|
this.pathStatusInfo[i] = new PathStateSequence(ByteUtils.copyBytes(src, 12 + pointStatusNum*8 + 8 * pointStatusNum + 8 * i, 8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.b0;
|
||||||
|
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/1/23 13:34
|
||||||
|
*/
|
||||||
|
public class QueryCargoStatusRsp {
|
||||||
|
//src
|
||||||
|
private byte[] src;
|
||||||
|
//是否载货
|
||||||
|
public byte isCargo;
|
||||||
|
//预留,3个字节
|
||||||
|
public byte[] reserved;
|
||||||
|
|
||||||
|
public QueryCargoStatusRsp(byte[] src) {
|
||||||
|
this.src = src;
|
||||||
|
this.isCargo = src[0];
|
||||||
|
this.reserved = ByteUtils.copyBytes(src, 1, 3);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.b1;
|
||||||
|
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/1/23 14:30
|
||||||
|
*/
|
||||||
|
public class SubscribeInfo {
|
||||||
|
//站控协议命令码,2个字节
|
||||||
|
public byte[] commandCode;
|
||||||
|
//上报间隔时间 ,单位ms ,2个字节
|
||||||
|
public byte[] intervalTime;
|
||||||
|
//上报持续时间,单位ms ,4个字节
|
||||||
|
public byte[] durationTime;
|
||||||
|
//预留,8个字节
|
||||||
|
public byte[] reserved;
|
||||||
|
|
||||||
|
public SubscribeInfo(byte[] commandCode, Short intervalTime, Integer durationTime) {
|
||||||
|
this.commandCode = commandCode;
|
||||||
|
this.intervalTime = ByteUtils.shortToBytes(intervalTime);
|
||||||
|
this.durationTime = ByteUtils.intToBytes(durationTime,4);
|
||||||
|
this.reserved = new byte[8];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.b1;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/1/23 14:23
|
||||||
|
*/
|
||||||
|
public class SubscribeParam {
|
||||||
|
private byte[] src;
|
||||||
|
private List<SubscribeInfo> subscribeInfoList;
|
||||||
|
//uuid,64个字节
|
||||||
|
private byte[] uuid;
|
||||||
|
public SubscribeParam(List<SubscribeInfo> subscribeInfoList) {
|
||||||
|
this.subscribeInfoList = subscribeInfoList;
|
||||||
|
this.uuid = generate64ByteUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成 64 字节长度的 UUID,返回 byte[] 类型
|
||||||
|
public static byte[] generate64ByteUUID() {
|
||||||
|
// 创建 SecureRandom 实例用于生成安全的随机数
|
||||||
|
SecureRandom secureRandom = new SecureRandom();
|
||||||
|
// 定义一个长度为 64 的字节数组
|
||||||
|
byte[] uuidBytes = new byte[64];
|
||||||
|
// 使用 SecureRandom 生成随机字节填充字节数组
|
||||||
|
secureRandom.nextBytes(uuidBytes);
|
||||||
|
return uuidBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] toBytes(){
|
||||||
|
src = new byte[192];
|
||||||
|
List<Byte> bytes = new ArrayList<>();
|
||||||
|
for (SubscribeInfo subscribeInfo : subscribeInfoList) {
|
||||||
|
bytes.add(subscribeInfo.commandCode[0]);
|
||||||
|
bytes.add(subscribeInfo.commandCode[1]);
|
||||||
|
bytes.add(subscribeInfo.intervalTime[0]);
|
||||||
|
bytes.add(subscribeInfo.intervalTime[1]);
|
||||||
|
bytes.add(subscribeInfo.durationTime[0]);
|
||||||
|
bytes.add(subscribeInfo.durationTime[1]);
|
||||||
|
bytes.add(subscribeInfo.durationTime[2]);
|
||||||
|
bytes.add(subscribeInfo.durationTime[3]);
|
||||||
|
bytes.add(subscribeInfo.reserved[0]);
|
||||||
|
bytes.add(subscribeInfo.reserved[1]);
|
||||||
|
bytes.add(subscribeInfo.reserved[2]);
|
||||||
|
bytes.add(subscribeInfo.reserved[3]);
|
||||||
|
bytes.add(subscribeInfo.reserved[4]);
|
||||||
|
bytes.add(subscribeInfo.reserved[5]);
|
||||||
|
bytes.add(subscribeInfo.reserved[6]);
|
||||||
|
bytes.add(subscribeInfo.reserved[7]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < bytes.size(); i++) {
|
||||||
|
src[i] = bytes.get(i);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < uuid.length; i++) {
|
||||||
|
src[i+128] = uuid[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.b1;
|
||||||
|
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/2/5 14:21
|
||||||
|
*/
|
||||||
|
public class SubscribeRsp {
|
||||||
|
//源数组
|
||||||
|
private byte[] src;
|
||||||
|
//uuid 64个字节
|
||||||
|
private byte[] uuid;
|
||||||
|
//errCode 1个字节
|
||||||
|
private byte errCode;
|
||||||
|
//reserved 3个字节
|
||||||
|
private byte[] reserved;
|
||||||
|
public SubscribeRsp(byte[] src) {
|
||||||
|
this.src = src;
|
||||||
|
this.uuid = ByteUtils.copyOfRange(src,0,64);
|
||||||
|
this.errCode = src[64];
|
||||||
|
this.reserved = ByteUtils.copyOfRange(src,65,3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOk(){
|
||||||
|
if(this.errCode==0){
|
||||||
|
return true;
|
||||||
|
}else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.navigation;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/2/8 10:05
|
||||||
|
*/
|
||||||
|
public class Action {
|
||||||
|
//动作类型,2个字节
|
||||||
|
private byte[] actionType;
|
||||||
|
//执行动作并行方式,1个字节
|
||||||
|
private byte actionParallel;
|
||||||
|
//预留,1个字节
|
||||||
|
private byte actionRetain;
|
||||||
|
//动作 id,4个字节,actionId 自己定义不要重复就行,可以从0开始累加
|
||||||
|
private byte[] actionId;
|
||||||
|
//参数长度,1个字节
|
||||||
|
private byte paramSize;
|
||||||
|
//预留,3个字节
|
||||||
|
private byte[] paramRetain2;
|
||||||
|
//参数内容,这里的长度 是 paramSize 长度,todo 注意 这里的paramContent 需要字节去构建一下 ,目前 这里还没有
|
||||||
|
private byte[] paramContent;
|
||||||
|
|
||||||
|
|
||||||
|
public Action(byte actionType, byte actionParallel, Integer actionId, Integer paramSize, byte[] paramContent) {
|
||||||
|
this.actionType = new byte[]{actionType, (byte)0x00};
|
||||||
|
this.actionParallel = actionParallel;
|
||||||
|
this.actionRetain = (byte)0x00;
|
||||||
|
this.actionId = ByteUtils.intToBytes(actionId,1);
|
||||||
|
this.paramSize = ByteUtils.usintTo1Byte(paramSize);
|
||||||
|
this.paramRetain2 = new byte[]{(byte)0x00, (byte)0x00,(byte)0x00};
|
||||||
|
this.paramContent = paramContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] toBytes() {
|
||||||
|
ByteBuf byteBuf = Unpooled.buffer();
|
||||||
|
byteBuf.writeBytes(actionType);
|
||||||
|
byteBuf.writeByte(actionParallel);
|
||||||
|
byteBuf.writeByte(actionRetain);
|
||||||
|
byteBuf.writeBytes(actionId);
|
||||||
|
byteBuf.writeByte(paramSize);
|
||||||
|
byteBuf.writeBytes(paramRetain2);
|
||||||
|
byteBuf.writeBytes(paramContent);
|
||||||
|
int i = byteBuf.writerIndex();
|
||||||
|
byte[] bytes = new byte[i];
|
||||||
|
byteBuf.readBytes(bytes);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.navigation;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/2/10 8:56
|
||||||
|
*/
|
||||||
|
public class ActionSet {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
byte[] stop = stop0x01(1, (byte) 0x01);
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte stop0x01 = 0x01;
|
||||||
|
public static byte recover0x02 = 0x02;
|
||||||
|
public static byte cancelTask0x03 = 0x03;
|
||||||
|
public static byte forkliftElevation0x12 = 0x12;
|
||||||
|
public static byte trayElevation0x16 = 0x16;
|
||||||
|
|
||||||
|
|
||||||
|
public static Integer stop0x01_paramsize = 8;
|
||||||
|
public static Integer recover0x02_paramsize = 8;
|
||||||
|
public static Integer cancelTask0x03_paramsize = 8;
|
||||||
|
public static Integer forkliftElevation0x12_paramsize = 8;
|
||||||
|
public static Integer trayElevation0x16_paramsize = 4;
|
||||||
|
|
||||||
|
|
||||||
|
public static byte[] stop0x01(Integer orderId,byte isStopImmediately){
|
||||||
|
ByteBuf byteBuf = Unpooled.buffer(8);
|
||||||
|
byteBuf.writeBytes(ByteUtils.intToBytes(orderId,1));
|
||||||
|
byteBuf.writeByte(isStopImmediately);
|
||||||
|
return byteBuf.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static byte[] recover0x02(Integer orderId,Integer taskKey){
|
||||||
|
ByteBuf byteBuf = Unpooled.buffer(8);
|
||||||
|
byteBuf.writeBytes(ByteUtils.intToBytes(orderId,1));
|
||||||
|
byteBuf.writeBytes(ByteUtils.intToBytes(taskKey,1));
|
||||||
|
return byteBuf.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
//取消任务
|
||||||
|
public static byte[] cancelTask0x03(Integer orderId,byte isStopImmediately){
|
||||||
|
ByteBuf byteBuf = Unpooled.buffer(8);
|
||||||
|
byteBuf.writeBytes(ByteUtils.intToBytes(orderId,1));
|
||||||
|
byteBuf.writeByte(isStopImmediately);
|
||||||
|
return byteBuf.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* desc:叉齿升降
|
||||||
|
* 支持的命令:0xAE 0xB2
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
|
||||||
|
public static byte[] forkliftElevation0x12(float lifitingHeight,byte hightMean,byte moveType, byte taskOperationType){
|
||||||
|
ByteBuf byteBuf = Unpooled.buffer(8);
|
||||||
|
byteBuf.writeBytes(ByteUtils.floatToBytes(lifitingHeight));
|
||||||
|
byteBuf.writeByte(hightMean);
|
||||||
|
byteBuf.writeByte(moveType);
|
||||||
|
byteBuf.writeByte(taskOperationType);
|
||||||
|
return byteBuf.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
//托盘升降
|
||||||
|
public static byte[] trayElevation0x16(byte palletMovementMode){
|
||||||
|
ByteBuf byteBuf = Unpooled.buffer(4);
|
||||||
|
byteBuf.writeByte(palletMovementMode);
|
||||||
|
return byteBuf.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.navigation;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: "0xAE 导航参数"
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2024/12/30 15:44
|
||||||
|
*/
|
||||||
|
public class NavigationParam {
|
||||||
|
//订单 ID,4个字节 ,Integer,从1开始依次累加
|
||||||
|
private byte[] orderId;
|
||||||
|
//任务 KEY,4个字节,Integer,从1开始依次累加
|
||||||
|
private byte[] taskKey;
|
||||||
|
//路径点信息个数(单条任务最大支持 8 个路径点),1个字节 ,byte
|
||||||
|
private byte pointNum;
|
||||||
|
//路径信息个数(单条任务最大支持 8 个路径点),1个字节,byte
|
||||||
|
private byte pathNum;
|
||||||
|
//保留,2个字节
|
||||||
|
private byte[] retain;
|
||||||
|
//任务中路径点信息结构体,长度 pointNum
|
||||||
|
private Point[] points;
|
||||||
|
//任务中路径信息结构体,长度 pathNum
|
||||||
|
private Path[] paths;
|
||||||
|
|
||||||
|
public NavigationParam(Integer orderId, Integer taskKey, byte pointNum, byte pathNum, Point[] points, Path[] paths) {
|
||||||
|
this.orderId = ByteUtils.intToBytes(orderId,1);
|
||||||
|
this.taskKey = ByteUtils.intToBytes(taskKey,1);
|
||||||
|
this.pointNum = pointNum;
|
||||||
|
this.pathNum = pathNum;
|
||||||
|
this.retain = new byte[]{(byte)0x00, (byte)0x00};
|
||||||
|
this.points = points;
|
||||||
|
this.paths = paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] toBytes() {
|
||||||
|
ByteBuf byteBuf = Unpooled.buffer();
|
||||||
|
byteBuf.writeBytes(orderId);
|
||||||
|
byteBuf.writeBytes(taskKey);
|
||||||
|
byteBuf.writeByte(pointNum);
|
||||||
|
byteBuf.writeByte(pathNum);
|
||||||
|
byteBuf.writeBytes(retain);
|
||||||
|
|
||||||
|
for (Point point : points) {
|
||||||
|
byteBuf.writeBytes(point.toBytes());
|
||||||
|
}
|
||||||
|
for (Path path : paths) {
|
||||||
|
byteBuf.writeBytes(path.toBytes());
|
||||||
|
}
|
||||||
|
int i = byteBuf.writerIndex();
|
||||||
|
byte[] bytes = new byte[i];
|
||||||
|
byteBuf.readBytes(bytes);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.navigation;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/2/8 9:39
|
||||||
|
*/
|
||||||
|
public class Path {
|
||||||
|
//序列号,4个字节,Integer,用于定位段在整个任务中的位置,从 1 开始奇数递增(如 1、3、5......)(point 2 4 6,这样path 和point就可以结合起来了),以区分同一个段 ID 在任务中是否多次出现
|
||||||
|
private byte[] serialNum;
|
||||||
|
//段 ID,4个字节,Integer
|
||||||
|
private byte[] pathId;
|
||||||
|
//指定角度时路径点的车头角度,4个字节,float ,当 isSpecifyAngle==1 的时候这个字段才生效
|
||||||
|
private byte[] angle;
|
||||||
|
//是否指定路径点角度,1个字节,byte,1 表示指定,0 表示不指定
|
||||||
|
private byte isSpecifyAngle;
|
||||||
|
|
||||||
|
//行驶姿态,1个字节,byte
|
||||||
|
private byte drivePose;
|
||||||
|
//任务中边上动作信息个数,1个字节,byte
|
||||||
|
private byte pathActionSize;
|
||||||
|
//保留,1个字节
|
||||||
|
private byte pathRetain;
|
||||||
|
//指定的目标最大速度,4个字节,float
|
||||||
|
private byte[] maxSpeed;
|
||||||
|
//指定的目标最大角速度,4个字节,float
|
||||||
|
private byte[] maxAngularSpeed;
|
||||||
|
//预留,4个字节
|
||||||
|
private byte[] pathRetain2;
|
||||||
|
|
||||||
|
//任务中点上动作结构体,这里数组的长度是 pathActionSize 长度
|
||||||
|
private Action[] actions;
|
||||||
|
|
||||||
|
|
||||||
|
public Path(Integer serialNum, Integer pathId, float angle, byte isSpecifyAngle, byte drivePose, byte pathActionSize, float maxSpeed, float maxAngularSpeed, Action[] actions) {
|
||||||
|
this.serialNum = ByteUtils.intToBytes(serialNum,1);
|
||||||
|
this.pathId = ByteUtils.intToBytes(pathId,1);
|
||||||
|
this.angle = ByteUtils.floatToBytes(angle);
|
||||||
|
this.isSpecifyAngle = isSpecifyAngle;
|
||||||
|
this.drivePose = drivePose;
|
||||||
|
this.pathActionSize = pathActionSize;
|
||||||
|
this.pathRetain = (byte)0x00;
|
||||||
|
this.maxSpeed = ByteUtils.floatToBytes(maxSpeed);
|
||||||
|
this.maxAngularSpeed = ByteUtils.floatToBytes(maxAngularSpeed);
|
||||||
|
this.pathRetain2 = new byte[]{(byte)0x00, (byte)0x00,(byte)0x00,(byte)0x00};
|
||||||
|
this.actions = actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] toBytes() {
|
||||||
|
ByteBuf byteBuf = Unpooled.buffer();
|
||||||
|
byteBuf.writeBytes(serialNum);
|
||||||
|
byteBuf.writeBytes(pathId);
|
||||||
|
byteBuf.writeBytes(angle);
|
||||||
|
byteBuf.writeByte(isSpecifyAngle);
|
||||||
|
byteBuf.writeByte(drivePose);
|
||||||
|
byteBuf.writeByte(pathActionSize);
|
||||||
|
byteBuf.writeByte(pathRetain);
|
||||||
|
byteBuf.writeBytes(maxSpeed);
|
||||||
|
byteBuf.writeBytes(maxAngularSpeed);
|
||||||
|
byteBuf.writeBytes(pathRetain2);
|
||||||
|
for (Action action : actions) {
|
||||||
|
byteBuf.writeBytes(action.toBytes());
|
||||||
|
}
|
||||||
|
int i = byteBuf.writerIndex();
|
||||||
|
byte[] bytes = new byte[i];
|
||||||
|
byteBuf.readBytes(bytes);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.navigation;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/2/8 9:39
|
||||||
|
*/
|
||||||
|
public class Point {
|
||||||
|
//序列号,4个字节,Integer,用于定位点在整个任务中的位置,从 0 开始偶数递增(如 0、2、4、6......),以区分同一个点 ID 在任务中是否多次出现
|
||||||
|
private byte[] serialNum;
|
||||||
|
//路径点 ID,4个字节,Integer
|
||||||
|
private byte[] pointId;
|
||||||
|
//指定角度时路径点的车头角度,4个字节,float ,当 isSpecifyAngle==1 的时候这个字段才生效
|
||||||
|
private byte[] angle;
|
||||||
|
//是否指定路径点角度,1个字节,byte,1 表示指定,0 表示不指定
|
||||||
|
private byte isSpecifyAngle;
|
||||||
|
//任务中点上动作信息个数,1个字节,byte
|
||||||
|
private byte pointActionSize;
|
||||||
|
//保留,6个字节
|
||||||
|
private byte[] pointRetain;
|
||||||
|
//任务中点上动作结构体,这里数组的长度是 pointActionSize 长度
|
||||||
|
private Action[] actions;
|
||||||
|
|
||||||
|
public Point(Integer serialNum, Integer pointId, float angle, byte isSpecifyAngle, byte pointActionSize, Action[] actions) {
|
||||||
|
this.serialNum = ByteUtils.intToBytes(serialNum,1);
|
||||||
|
this.pointId = ByteUtils.intToBytes(pointId,1);
|
||||||
|
this.angle = ByteUtils.floatToBytes(angle);
|
||||||
|
this.isSpecifyAngle = isSpecifyAngle;
|
||||||
|
this.pointActionSize = pointActionSize;
|
||||||
|
this.pointRetain = new byte[]{(byte)0x00, (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00};
|
||||||
|
this.actions = actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] toBytes() {
|
||||||
|
ByteBuf byteBuf = Unpooled.buffer();
|
||||||
|
byteBuf.writeBytes(serialNum);
|
||||||
|
byteBuf.writeBytes(pointId);
|
||||||
|
byteBuf.writeBytes(angle);
|
||||||
|
byteBuf.writeByte(isSpecifyAngle);
|
||||||
|
byteBuf.writeByte(pointActionSize);
|
||||||
|
byteBuf.writeBytes(pointRetain);
|
||||||
|
for (Action action : actions) {
|
||||||
|
byteBuf.writeBytes(action.toBytes());
|
||||||
|
}
|
||||||
|
int i = byteBuf.writerIndex();
|
||||||
|
byte[] bytes = new byte[i];
|
||||||
|
byteBuf.readBytes(bytes);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.read;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2024/12/30 15:44
|
||||||
|
*/
|
||||||
|
public class ReadParam {
|
||||||
|
private byte valueNum;
|
||||||
|
private byte[] retain;
|
||||||
|
private byte[] valueId;
|
||||||
|
private byte[] readStrValues;
|
||||||
|
public ReadParam(byte[] valueId, List<ReadStrValue> rvalues) {
|
||||||
|
// 这里的valueId 也是 header里面的 transationId
|
||||||
|
valueNum = ByteUtil.intTo1Byte(rvalues.size());
|
||||||
|
retain = new byte[]{0x00,0x00,0x00};
|
||||||
|
|
||||||
|
this.valueId = new byte[]{valueId[0],valueId[1],0x00,0x00};
|
||||||
|
List<Byte> bytes = new ArrayList<>();
|
||||||
|
for (ReadStrValue b : rvalues) {
|
||||||
|
bytes.addAll(b.toBytes());
|
||||||
|
}
|
||||||
|
readStrValues = new byte[bytes.size()];
|
||||||
|
for (int i = 0; i < bytes.size(); i++) {
|
||||||
|
readStrValues[i] = bytes.get(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] toBytes() {
|
||||||
|
List<Byte> bytes = new ArrayList<>();
|
||||||
|
bytes.add(valueNum);
|
||||||
|
for (byte b : retain) {
|
||||||
|
bytes.add(b);
|
||||||
|
}
|
||||||
|
for (byte b : valueId) {
|
||||||
|
bytes.add(b);
|
||||||
|
}
|
||||||
|
for (byte b : readStrValues) {
|
||||||
|
bytes.add(b);
|
||||||
|
}
|
||||||
|
byte[] b = new byte[bytes.size()];
|
||||||
|
for (int i = 0; i < bytes.size(); i++) {
|
||||||
|
b[i] = bytes.get(i);
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.read;
|
||||||
|
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/2/5 14:55
|
||||||
|
*/
|
||||||
|
public class ReadRsp {
|
||||||
|
//源数组
|
||||||
|
private byte[] src;
|
||||||
|
//ValueID ,4个字节
|
||||||
|
public byte[] valueId;
|
||||||
|
//应答数据总长度,2个字节
|
||||||
|
public short valueByteLength;
|
||||||
|
//预留,2个字节
|
||||||
|
public byte[] reserved;
|
||||||
|
//变量值,长度是 valueByteLength
|
||||||
|
public byte[] dataValue;
|
||||||
|
|
||||||
|
public ReadRsp(byte[] src) {
|
||||||
|
this.src = src;
|
||||||
|
this.valueId = ByteUtils.copyBytes(src, 0, 4);
|
||||||
|
this.valueByteLength = ByteUtils.bytesToShort(ByteUtils.copyBytes(src, 4, 2),1);
|
||||||
|
this.reserved = ByteUtils.copyBytes(src, 6, 2);
|
||||||
|
this.dataValue = ByteUtils.copyBytes(src, 8, valueByteLength-8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOk(){
|
||||||
|
if(valueByteLength<=0){
|
||||||
|
return false;
|
||||||
|
}else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.read;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2024/12/30 15:46
|
||||||
|
*/
|
||||||
|
public class ReadStrValue {
|
||||||
|
//变量名,16个字节( 以这个变量名来定位变量的)
|
||||||
|
private byte[] varName;
|
||||||
|
//成员变量数量,4个字节
|
||||||
|
private byte[] memberVarNum;
|
||||||
|
private byte[] memberList;
|
||||||
|
public ReadStrValue(String varName, Integer memberVarNum, List<ReadValueMember> memberList) {
|
||||||
|
this.varName = ByteUtil.stringTo16Byte(varName);
|
||||||
|
this.memberVarNum = ByteUtil.intToBytes(memberVarNum);
|
||||||
|
List<Byte> bytes = new ArrayList<>();
|
||||||
|
for (ReadValueMember member : memberList) {
|
||||||
|
bytes.addAll(member.toBytes());
|
||||||
|
}
|
||||||
|
this.memberList = new byte[bytes.size()];
|
||||||
|
for (int i = 0; i < bytes.size(); i++) {
|
||||||
|
this.memberList[i] = bytes.get(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public ReadStrValue(String varName) {
|
||||||
|
this.varName = ByteUtil.stringTo16Byte(varName);
|
||||||
|
this.memberVarNum = ByteUtil.intToBytes(0);
|
||||||
|
this.memberList = new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Byte> toBytes() {
|
||||||
|
List<Byte> bytes = new ArrayList<>();
|
||||||
|
for (byte b : varName) {
|
||||||
|
bytes.add(b);
|
||||||
|
}
|
||||||
|
for (byte b : memberVarNum) {
|
||||||
|
bytes.add(b);
|
||||||
|
}
|
||||||
|
for (byte b : memberList) {
|
||||||
|
bytes.add(b);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.read;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2024/12/30 15:49
|
||||||
|
*/
|
||||||
|
public class ReadValueMember {
|
||||||
|
//偏移值,就是以这个变量的起始点为基准,偏移几个字节,来读取数组里的后面几个变量,基本上用于数组变量,单体变量用不着 2个字节
|
||||||
|
private byte[] offsetValue;
|
||||||
|
//变量成员长度,就是你要读取的content长度,可以是1个变量的长度,也可以是n个变量的长度(就是数据类型 的字节长度)
|
||||||
|
private byte[] memberVarLength;
|
||||||
|
|
||||||
|
public ReadValueMember(Short offsetValue, Short memberVarLength) {
|
||||||
|
this.offsetValue = ByteUtil.shortToBytes(offsetValue);
|
||||||
|
this.memberVarLength = ByteUtil.shortToBytes(memberVarLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Byte> toBytes() {
|
||||||
|
List<Byte> bytes = new ArrayList<>();
|
||||||
|
for (byte b : offsetValue) {
|
||||||
|
bytes.add(b);
|
||||||
|
}
|
||||||
|
for (byte b : memberVarLength) {
|
||||||
|
bytes.add(b);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.write;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import java.util.List;
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/2/5 16:41
|
||||||
|
*/
|
||||||
|
public class WriteParam {
|
||||||
|
//变量数量 1个字节 ,最大只支持 15 个变量
|
||||||
|
private byte valueNum;
|
||||||
|
//保留 3个字节
|
||||||
|
private byte[] retain;
|
||||||
|
//这里的length 就是 valueNum
|
||||||
|
private List<WriteStrValue> values;
|
||||||
|
public WriteParam(Integer valueNum, List<WriteStrValue> values) {
|
||||||
|
this.valueNum = ByteUtils.usintTo1Byte(valueNum);
|
||||||
|
this.retain = new byte[]{(byte)0x00,(byte)0x00,(byte)0x00};
|
||||||
|
this.values = values;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] toBytes() {
|
||||||
|
ByteBuf byteBuf = Unpooled.buffer();
|
||||||
|
byteBuf.writeByte(valueNum);
|
||||||
|
byteBuf.writeBytes(retain);
|
||||||
|
for (WriteStrValue value : values) {
|
||||||
|
byteBuf.writeBytes(value.toBytes());
|
||||||
|
}
|
||||||
|
int i = byteBuf.writerIndex();
|
||||||
|
byte[] bytes = new byte[i];
|
||||||
|
byteBuf.readBytes(bytes);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.write;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import java.util.List;
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/2/5 16:43
|
||||||
|
*/
|
||||||
|
public class WriteStrValue {
|
||||||
|
//变量名 16个字节
|
||||||
|
private String valueName;
|
||||||
|
//变量成员数量 4个字节
|
||||||
|
private Integer valueNum;
|
||||||
|
private List<WriteValueMember> members;
|
||||||
|
|
||||||
|
public WriteStrValue(String valueName, Integer valueNum, List<WriteValueMember> members) {
|
||||||
|
this.valueName = valueName;
|
||||||
|
this.valueNum = valueNum;
|
||||||
|
this.members = members;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] toBytes() {
|
||||||
|
ByteBuf byteBuf = Unpooled.buffer();
|
||||||
|
byteBuf.writeBytes(ByteUtils.stringToBytes(valueName, 16));
|
||||||
|
byteBuf.writeBytes(ByteUtils.intToBytes(valueNum,1));
|
||||||
|
for (WriteValueMember member : members) {
|
||||||
|
byteBuf.writeBytes(member.toBytes());
|
||||||
|
}
|
||||||
|
int i = byteBuf.writerIndex();
|
||||||
|
byte[] bytes = new byte[i];
|
||||||
|
byteBuf.readBytes(bytes);
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.write;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/2/5 16:44
|
||||||
|
*/
|
||||||
|
public class WriteValueMember {
|
||||||
|
//变量成员偏移 2个字节
|
||||||
|
private byte[] valueOffset;
|
||||||
|
//变量成员长度 2个字节
|
||||||
|
private byte[] valueLength;
|
||||||
|
//变量成员值 4个字节
|
||||||
|
private byte[] newValue;
|
||||||
|
public WriteValueMember(Short valueOffset, Short valueLength, byte[] newValue) {
|
||||||
|
this.valueOffset = ByteUtils.shortToBytes(valueOffset);
|
||||||
|
this.valueLength = ByteUtils.shortToBytes(valueLength);
|
||||||
|
this.newValue = newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] toBytes() {
|
||||||
|
ByteBuf byteBuf = Unpooled.buffer(8);
|
||||||
|
byteBuf.writeBytes(valueOffset);
|
||||||
|
byteBuf.writeBytes(valueLength);
|
||||||
|
byteBuf.writeBytes(newValue);
|
||||||
|
return byteBuf.array();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.x14;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/2/7 9:21
|
||||||
|
*/
|
||||||
|
public class RobotSetPosition {
|
||||||
|
//机器人 x 坐标 ,8 个字节
|
||||||
|
private byte[] robotX;
|
||||||
|
//机器人 y 坐标 ,8 个字节
|
||||||
|
private byte[] robotY;
|
||||||
|
//机器人朝向角度 ,8 个字节
|
||||||
|
private byte[] robotAngle;
|
||||||
|
|
||||||
|
public RobotSetPosition(double robotX, double robotY, double robotAngle) {
|
||||||
|
this.robotX = ByteUtil.doubleToBytes(robotX);
|
||||||
|
this.robotY = ByteUtil.doubleToBytes(robotY);
|
||||||
|
this.robotAngle = ByteUtil.doubleToBytes(robotAngle);
|
||||||
|
}
|
||||||
|
public byte[] toBytes() {
|
||||||
|
ByteBuf byteBuf = Unpooled.buffer(24);
|
||||||
|
byteBuf.writeBytes(robotX);
|
||||||
|
byteBuf.writeBytes(robotY);
|
||||||
|
byteBuf.writeBytes(robotAngle);
|
||||||
|
return byteBuf.array();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,130 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.function.x17;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2025/2/7 9:43
|
||||||
|
*/
|
||||||
|
public class QueryRobotRunStatusRsp {
|
||||||
|
//本体温度 ,8个字节 ,double
|
||||||
|
private double temp;
|
||||||
|
//位置的 X 坐标 , 8个字节 ,double
|
||||||
|
private double currentX;
|
||||||
|
//位置的 Y 坐标,8个字节 ,double
|
||||||
|
private double currentY;
|
||||||
|
//位置的 角度 ,8个字节 ,double
|
||||||
|
private double currentAngle;
|
||||||
|
//电池电量,,8个字节 ,double
|
||||||
|
private double battery;
|
||||||
|
//是否被阻挡,1个字节,byte
|
||||||
|
private byte isBlocked;
|
||||||
|
//是否在充电,1个字节,byte
|
||||||
|
private byte isCharging;
|
||||||
|
//运行模式,1个字节,byte
|
||||||
|
private byte runMode;
|
||||||
|
//地图载入状态,1个字节,byte
|
||||||
|
private byte mapLoadState;
|
||||||
|
//当前的目标点 id,U32 ,4个字节
|
||||||
|
private Integer currentTargetId;
|
||||||
|
|
||||||
|
//前进速度,8个字节,double
|
||||||
|
private double forwardSpeed;
|
||||||
|
//转弯速度,8个字节,double
|
||||||
|
private double turnSpeed;
|
||||||
|
//电池电压(充电为正,放电为负),8个字节,double
|
||||||
|
private double batteryVoltage;
|
||||||
|
//电流(充电为正,放电为负),double
|
||||||
|
private double electricCurrent;
|
||||||
|
//当前任务状态,1个字节,byte
|
||||||
|
private byte taskState;
|
||||||
|
//保留,1个字节
|
||||||
|
private byte retain1;
|
||||||
|
//地图版本号,2个字节
|
||||||
|
private short mapVersion;
|
||||||
|
//保留,4个字节
|
||||||
|
private byte[] retain2;
|
||||||
|
//累计行驶里程(单位 m),8个字节
|
||||||
|
private double cumulativeMileage;
|
||||||
|
//本次运行时间(单位 ms),8个字节
|
||||||
|
private double currentRunTime;
|
||||||
|
//累计运行时间(单位 ms),8个字节
|
||||||
|
private double cumulativeRunTime;
|
||||||
|
//机器人定位状态,1个字节
|
||||||
|
private byte robotLocalizationState;
|
||||||
|
//保留,3个字节
|
||||||
|
private byte[] retain3;
|
||||||
|
//地图数量,U32,4个字节
|
||||||
|
private Integer mapNum;
|
||||||
|
//当前地图名称,64字节
|
||||||
|
private String currentMapName;
|
||||||
|
//置信度,4个字节
|
||||||
|
private float confidence;
|
||||||
|
//保留,4个字节
|
||||||
|
private byte[] retain4;
|
||||||
|
|
||||||
|
public QueryRobotRunStatusRsp(byte[] src) {
|
||||||
|
this.temp = ByteUtils.bytesToDouble(src, 0);
|
||||||
|
this.currentX = ByteUtils.bytesToDouble(src, 8);
|
||||||
|
this.currentY = ByteUtils.bytesToDouble(src, 16);
|
||||||
|
this.currentAngle = ByteUtils.bytesToDouble(src, 24);
|
||||||
|
this.battery = ByteUtils.bytesToDouble(src, 32);
|
||||||
|
this.isBlocked = src[40];
|
||||||
|
this.isCharging = src[41];
|
||||||
|
this.runMode = src[42];
|
||||||
|
this.mapLoadState = src[43];
|
||||||
|
this.currentTargetId = ByteUtils.bytesToInt(src, 44);
|
||||||
|
this.forwardSpeed = ByteUtils.bytesToDouble(src, 48);
|
||||||
|
this.turnSpeed = ByteUtils.bytesToDouble(src, 56);
|
||||||
|
this.batteryVoltage = ByteUtils.bytesToDouble(src, 64);
|
||||||
|
this.electricCurrent = ByteUtils.bytesToDouble(src, 72);
|
||||||
|
this.taskState = src[80];
|
||||||
|
this.retain1 = src[81];
|
||||||
|
this.mapVersion = ByteUtils.bytesToShortLitt(src, 82);
|
||||||
|
this.retain2 = ByteUtils.copyBytes(src, 84, 4);
|
||||||
|
this.cumulativeMileage = ByteUtils.bytesToDouble(src, 88);
|
||||||
|
this.currentRunTime = ByteUtils.bytesToDouble(src, 96);
|
||||||
|
this.cumulativeRunTime = ByteUtils.bytesToDouble(src, 104);
|
||||||
|
this.robotLocalizationState = src[112];
|
||||||
|
this.retain3 = ByteUtils.copyBytes(src, 113, 3);
|
||||||
|
this.mapNum = ByteUtils.bytesToInt(src, 116);
|
||||||
|
this.currentMapName = ByteUtils.bytesToString(src, 120, 64);
|
||||||
|
this.confidence = ByteUtils.bytesToFloat(src, 184);
|
||||||
|
this.retain4 = ByteUtils.copyBytes(src, 188, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "QueryRobotRunStatusRsp{" +
|
||||||
|
"temp=" + temp +
|
||||||
|
", currentX=" + currentX +
|
||||||
|
", currentY=" + currentY +
|
||||||
|
", currentAngle=" + currentAngle +
|
||||||
|
", battery=" + battery +
|
||||||
|
", isBlocked=" + isBlocked +
|
||||||
|
", isCharging=" + isCharging +
|
||||||
|
", runMode=" + runMode +
|
||||||
|
", mapLoadState=" + mapLoadState +
|
||||||
|
", currentTargetId=" + currentTargetId +
|
||||||
|
", forwardSpeed=" + forwardSpeed +
|
||||||
|
", turnSpeed=" + turnSpeed +
|
||||||
|
", batteryVoltage=" + batteryVoltage +
|
||||||
|
", electricCurrent=" + electricCurrent +
|
||||||
|
", taskState=" + taskState +
|
||||||
|
", retain1=" + retain1 +
|
||||||
|
", mapVersion=" + mapVersion +
|
||||||
|
", retain2=" + Arrays.toString(retain2) +
|
||||||
|
", cumulativeMileage=" + cumulativeMileage +
|
||||||
|
", cuttentRunTime=" + currentRunTime +
|
||||||
|
", cumulativeRunTime=" + cumulativeRunTime +
|
||||||
|
", robotLocalizationState=" + robotLocalizationState +
|
||||||
|
", retain3=" + Arrays.toString(retain3) +
|
||||||
|
", mapNum=" + mapNum +
|
||||||
|
", currentMapName='" + currentMapName + '\'' +
|
||||||
|
", confidence=" + confidence +
|
||||||
|
", retain4=" + Arrays.toString(retain4) +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
package org.opentcs.kc.udp.agv.param.rsp;
|
||||||
|
|
||||||
|
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtils;
|
||||||
|
import org.opentcs.kc.udp.agv.param.AgvEventHeader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: "通用的数据传输底层类"
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2022/10/18 16:22
|
||||||
|
*/
|
||||||
|
public class RcvEventPackage {
|
||||||
|
|
||||||
|
private boolean isOk;
|
||||||
|
private byte[] value;
|
||||||
|
private String content;
|
||||||
|
// header size = 授权码16 + 报文头12(包括报文数据长度2个字节+保留2个字节) = 28
|
||||||
|
private AgvEventHeader header;
|
||||||
|
private byte[] dataBytes;
|
||||||
|
|
||||||
|
|
||||||
|
private String getContent(byte i) {
|
||||||
|
if(i==0x00){
|
||||||
|
return "成功执行";
|
||||||
|
}else if(i==0x01){
|
||||||
|
return "执行失败,原因未知";
|
||||||
|
}else if(i==0x02){
|
||||||
|
return "服务码错误";
|
||||||
|
}else if(i==0x03){
|
||||||
|
return "命令码错误";
|
||||||
|
}else if(i==0x04){
|
||||||
|
return "报文头部错误";
|
||||||
|
}else if(i==0x80){
|
||||||
|
return "无法执行命令,因为当前车辆导航状态与命令冲突";
|
||||||
|
}else if(i==0xFF){
|
||||||
|
return "协议授权码错误";
|
||||||
|
}else {
|
||||||
|
return "未知错误";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public RcvEventPackage(byte executionCode, byte[] value) {
|
||||||
|
if(executionCode == 0x00){
|
||||||
|
this.isOk = true;
|
||||||
|
this.header = new AgvEventHeader(ByteUtils.copyBytes(value,0,28));
|
||||||
|
this.dataBytes = ByteUtils.copyBytes(value,28,value.length-28);
|
||||||
|
}else {
|
||||||
|
this.isOk = false;
|
||||||
|
}
|
||||||
|
this.value = value;
|
||||||
|
this.content = getContent(executionCode);
|
||||||
|
}
|
||||||
|
public RcvEventPackage() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOk() {
|
||||||
|
return isOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(byte[] value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AgvEventHeader getHeader() {
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getDataBytes() {
|
||||||
|
return dataBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "RcvPackage{" +
|
||||||
|
"isOk=" + isOk +
|
||||||
|
", value=" + value +
|
||||||
|
", content='" + content + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,212 @@
|
|||||||
|
package org.opentcs.kc.udp.io;
|
||||||
|
|
||||||
|
|
||||||
|
import io.netty.bootstrap.Bootstrap;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.channel.*;
|
||||||
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
|
import io.netty.channel.socket.DatagramPacket;
|
||||||
|
import io.netty.channel.socket.nio.NioDatagramChannel;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import org.opentcs.kc.common.Package;
|
||||||
|
import org.opentcs.kc.common.byteutils.ByteUtil;
|
||||||
|
import org.opentcs.kc.syn.AsyncFuture;
|
||||||
|
import org.opentcs.kc.syn.SendedList;
|
||||||
|
import org.opentcs.kc.udp.agv.codec.AgvUdpChannelInitializer;
|
||||||
|
import org.opentcs.kc.udp.agv.param.AgvEvent;
|
||||||
|
import org.opentcs.kc.udp.agv.param.function.af.QueryRobotStatusRsp;
|
||||||
|
import org.opentcs.kc.udp.agv.param.function.b0.QueryCargoStatusRsp;
|
||||||
|
import org.opentcs.kc.udp.agv.param.rsp.RcvEventPackage;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Desc: ""
|
||||||
|
* @Author: caixiang
|
||||||
|
* @DATE: 2022/1/15 13:01
|
||||||
|
*/
|
||||||
|
public enum UDPClient {
|
||||||
|
//如果要配置多个链接, local1 local2 .... 这样排下去好了
|
||||||
|
|
||||||
|
localAGV("agv1","192.168.0.211",17804,55678),
|
||||||
|
//local("127.0.0.1",502,true),
|
||||||
|
;
|
||||||
|
private String name;
|
||||||
|
private String host;
|
||||||
|
//默认 0 port
|
||||||
|
private Integer port;
|
||||||
|
private Integer bindPort;
|
||||||
|
private Bootstrap bootstrap;
|
||||||
|
private NioEventLoopGroup group;
|
||||||
|
|
||||||
|
|
||||||
|
private Channel conn;
|
||||||
|
private boolean isOnline;
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger("AGVLOGGER");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ==== 连接节点配置信息 ===== 开始
|
||||||
|
|
||||||
|
// ==== 连接节点配置信息 ===== 结束
|
||||||
|
|
||||||
|
UDPClient() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//coreSize 是线程池的数量
|
||||||
|
UDPClient(String name, String host, Integer port, Integer bindPort) {
|
||||||
|
this.name = name;
|
||||||
|
this.host = host;
|
||||||
|
this.port = port;
|
||||||
|
this.conn = null;
|
||||||
|
this.bindPort = bindPort;
|
||||||
|
initClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public String getHost(){
|
||||||
|
return this.host;
|
||||||
|
}
|
||||||
|
public String getName(){
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
public void setIsOnline(boolean isOnline){
|
||||||
|
this.isOnline = isOnline;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* desc : 判断此链接 健康状况
|
||||||
|
* return
|
||||||
|
* true : 此tcp连接 正常
|
||||||
|
* false : 此tcp连接 异常
|
||||||
|
* */
|
||||||
|
public boolean isOnline(){
|
||||||
|
return isOnline;
|
||||||
|
}
|
||||||
|
public void close(){
|
||||||
|
//手动关闭连接,会出发InActivite 事件
|
||||||
|
group.shutdownGracefully();
|
||||||
|
}
|
||||||
|
public static ByteBuf byteArrayToByteBuf(byte[] byteArray) {
|
||||||
|
// 使用Unpooled类创建ByteBuf
|
||||||
|
ByteBuf byteBuf = Unpooled.buffer(byteArray.length);
|
||||||
|
// 将byte[]写入ByteBuf
|
||||||
|
byteBuf.writeBytes(byteArray);
|
||||||
|
return byteBuf;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* (--)线程安全的(--)
|
||||||
|
|
||||||
|
*
|
||||||
|
* 如果返回null,就代表出现了异常,并且尝试了 retryMax 次数,并且尝试重置连接
|
||||||
|
* */
|
||||||
|
public RcvEventPackage send(AgvEvent event) {
|
||||||
|
try {
|
||||||
|
//sendread 报文
|
||||||
|
//应该是ReadRequestFrame 继承 Packet 类,然后直接 Tio.bSend(this.conn, ReadRequestFrame)
|
||||||
|
|
||||||
|
Package mbPackage = event.toBytes();
|
||||||
|
AsyncFuture<Package> add = SendedList.add(mbPackage.getTransationId(),null);
|
||||||
|
|
||||||
|
this.conn.writeAndFlush(
|
||||||
|
new DatagramPacket(
|
||||||
|
byteArrayToByteBuf(mbPackage.getBody()),
|
||||||
|
new InetSocketAddress(this.host,this.port)))
|
||||||
|
.sync();
|
||||||
|
|
||||||
|
Package aPackage = add.get(5000L, mbPackage.getTransationId());
|
||||||
|
byte[] body = aPackage.getBody();
|
||||||
|
|
||||||
|
String errMsg = " [ AGVclient - send success ] [ "+name+" host: "+ this.host +" ]"+event.toString();
|
||||||
|
logger.info(errMsg);
|
||||||
|
//注意:这里的body 是整个 response结构,包括 : 授权码 + header + body
|
||||||
|
return new RcvEventPackage(body[22],body);
|
||||||
|
}catch (Throwable e) {
|
||||||
|
//e.printStackTrace();
|
||||||
|
String errMsg = " [ AGVclient - Read ] [ "+name+" host: "+ this.host +" ] ( occur err ) errTime: "+" ; send errMsg : "+e.getMessage()+" ; event :"+event.toString();
|
||||||
|
logger.info(errMsg);
|
||||||
|
throw new RuntimeException(errMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void subscribe0xB0(RcvEventPackage rcv){
|
||||||
|
if(name.equals("agv1")){
|
||||||
|
QueryCargoStatusRsp queryCargoStatusRsp = new QueryCargoStatusRsp(rcv.getDataBytes());
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("received subscribe 0xB0 List : "+ "isok:"+rcv.isOk());
|
||||||
|
for (byte b:rcv.getValue()){
|
||||||
|
System.out.print(byteToHex(b)+" ");
|
||||||
|
}
|
||||||
|
}else if(name.equals("agv2")){
|
||||||
|
//....
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public void subscribe0xAF(RcvEventPackage rcv){
|
||||||
|
if(name.equals("agv1")){
|
||||||
|
if(rcv.isOk()){
|
||||||
|
QueryRobotStatusRsp queryRobotStatusRsp = new QueryRobotStatusRsp(rcv.getDataBytes());
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("received subscribe 0xAF List : "+ "isok:"+rcv.isOk());
|
||||||
|
for (byte b:rcv.getValue()){
|
||||||
|
System.out.print(byteToHex(b)+" ");
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("received transationId : "+ "isok:"+rcv.isOk());
|
||||||
|
}
|
||||||
|
}else if(name.equals("agv2")){
|
||||||
|
//....
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void printInfo(AgvEvent agvEvent){
|
||||||
|
System.out.println("sended transationId : "+agvEvent.getTransationIdString());
|
||||||
|
for (byte b:agvEvent.toBytes().getBody()){
|
||||||
|
System.out.print(byteToHex(b)+" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static String byteToHex(byte b) {
|
||||||
|
// 将byte转换为无符号整数
|
||||||
|
int unsignedByte = b & 0xFF;
|
||||||
|
// 使用Integer.toHexString方法转换为十六进制字符串
|
||||||
|
String hexString = Integer.toHexString(unsignedByte);
|
||||||
|
// 如果字符串长度为1,需要在前面补0
|
||||||
|
if (hexString.length() == 1) {
|
||||||
|
return "0" + hexString;
|
||||||
|
}
|
||||||
|
return hexString;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* decs: 在项目启动的时候初始化 后面就不会初始化了
|
||||||
|
* */
|
||||||
|
private void initClient() {
|
||||||
|
//NioEventLoopGroup 、 Bootstrap 这些资源其实是不用 release的,因为全局共用一份的你释放了 下次还得再new
|
||||||
|
NioEventLoopGroup group = new NioEventLoopGroup();
|
||||||
|
this.bootstrap = new Bootstrap();
|
||||||
|
this.bootstrap.group(group)
|
||||||
|
/*由于我们用的是UDP协议,所以要用NioDatagramChannel来创建*/
|
||||||
|
.channel(NioDatagramChannel.class)
|
||||||
|
.handler(new ChannelInitializer<NioDatagramChannel>() {
|
||||||
|
@Override
|
||||||
|
protected void initChannel(NioDatagramChannel ch) throws Exception {
|
||||||
|
ChannelPipeline pipeline = ch.pipeline();
|
||||||
|
//0 代表禁用 readerIdleTime 事件的监听
|
||||||
|
//这两个是固定的
|
||||||
|
//pipeline.addLast(new IdleStateHandler(0,0,10, TimeUnit.SECONDS));
|
||||||
|
|
||||||
|
//协议层的handler 这里要做以区分
|
||||||
|
|
||||||
|
pipeline.addLast(new AgvUdpChannelInitializer(UDPClient.this));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
this.conn = this.bootstrap.bind(this.bindPort).sync().channel();
|
||||||
|
}catch (Exception e){
|
||||||
|
logger.info("AGV UDP Initial Exception : "+e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
51
opentcs-common/src/main/resources/logback.xml
Normal file
51
opentcs-common/src/main/resources/logback.xml
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
|
||||||
|
<!-- scan:当此属性设置为true时,配置文档如果发生改变,将会被重新加载,默认值为true -->
|
||||||
|
<!-- scanPeriod:设置监测配置文档是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。
|
||||||
|
当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
|
||||||
|
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
|
||||||
|
<configuration scan="true" scanPeriod="10 seconds">
|
||||||
|
<contextName>logback-spring</contextName>
|
||||||
|
|
||||||
|
|
||||||
|
<property name="logging.eqlog" value="/var/log/logger/EQLog/njlm" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- agv logger 开始 -->
|
||||||
|
<property name="logging.agvlog" value="/var/log/logger/agvUdp/" />
|
||||||
|
<appender name="AGV_APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<!-- 正在记录的日志文档的路径及文档名 -->
|
||||||
|
<file>${logging.agvlog}/today/log-info.log</file>
|
||||||
|
<!--日志文档输出格式-->
|
||||||
|
<encoder>
|
||||||
|
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
|
||||||
|
<charset>UTF-8</charset>
|
||||||
|
</encoder>
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||||
|
<!-- rollover daily -->
|
||||||
|
<fileNamePattern>${logging.agvlog}/agv-log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||||
|
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
|
||||||
|
<maxFileSize>100MB</maxFileSize>
|
||||||
|
<maxHistory>999</maxHistory>
|
||||||
|
<totalSizeCap>200GB</totalSizeCap>
|
||||||
|
</rollingPolicy>
|
||||||
|
<!-- 此日志文档只记录info级别的 -->
|
||||||
|
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||||
|
<level>info</level>
|
||||||
|
<onMatch>ACCEPT</onMatch>
|
||||||
|
<onMismatch>DENY</onMismatch>
|
||||||
|
</filter>
|
||||||
|
</appender>
|
||||||
|
<logger name="AGVLOGGER" level="INFO" additivity="false">
|
||||||
|
<appender-ref ref="AGV_APPENDER" />
|
||||||
|
</logger>
|
||||||
|
<!-- agv logger 结束 -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</configuration>
|
Loading…
Reference in New Issue
Block a user