# Conflicts:
#	opentcs-common/src/main/java/org/opentcs/kc/common/byteutils/ByteUtils.java
#	opentcs-common/src/main/java/org/opentcs/kc/common/enmuc/ModbusFC.java
#	opentcs-common/src/main/java/org/opentcs/kc/udp/KCCommandDemo.java
#	opentcs-common/src/main/java/org/opentcs/kc/udp/agv/param/function/af/QueryRobotStatusRsp.java
#	opentcs-common/src/main/java/org/opentcs/kc/udp/agv/param/function/b1/SubscribeRsp.java
#	opentcs-common/src/main/java/org/opentcs/kc/udp/io/UDPClient.java
This commit is contained in:
CaiXiang 2025-04-18 11:43:39 +08:00
commit 48ec2a19a3
73 changed files with 4022 additions and 2671 deletions

View File

@ -29,6 +29,7 @@ public interface InternalVehicleService
/** /**
* Updates a vehicle's energy level. * Updates a vehicle's energy level.
* 更新车辆的能级
* *
* @param ref A reference to the vehicle to be modified. * @param ref A reference to the vehicle to be modified.
* @param energyLevel The vehicle's new energy level. * @param energyLevel The vehicle's new energy level.
@ -39,6 +40,7 @@ public interface InternalVehicleService
/** /**
* Updates a vehicle's load handling devices. * Updates a vehicle's load handling devices.
* 更新车辆的负载处理设备
* *
* @param ref A reference to the vehicle to be modified. * @param ref A reference to the vehicle to be modified.
* @param devices The vehicle's new load handling devices. * @param devices The vehicle's new load handling devices.
@ -52,6 +54,7 @@ public interface InternalVehicleService
/** /**
* Updates the point which a vehicle is expected to occupy next. * Updates the point which a vehicle is expected to occupy next.
* 更新了预计接下来将占用的车辆的点
* *
* @param vehicleRef A reference to the vehicle to be modified. * @param vehicleRef A reference to the vehicle to be modified.
* @param pointRef A reference to the point which the vehicle is expected to occupy next. * @param pointRef A reference to the point which the vehicle is expected to occupy next.
@ -65,6 +68,7 @@ public interface InternalVehicleService
/** /**
* Updates a vehicle's order sequence. * Updates a vehicle's order sequence.
* 更新车辆的订单序列
* *
* @param vehicleRef A reference to the vehicle to be modified. * @param vehicleRef A reference to the vehicle to be modified.
* @param sequenceRef A reference to the order sequence the vehicle processes. * @param sequenceRef A reference to the order sequence the vehicle processes.
@ -79,6 +83,7 @@ public interface InternalVehicleService
/** /**
* Updates the vehicle's current orientation angle (-360..360 degrees, or {@link Double#NaN}, if * Updates the vehicle's current orientation angle (-360..360 degrees, or {@link Double#NaN}, if
* the vehicle doesn't provide an angle). * the vehicle doesn't provide an angle).
* 如果车辆不提供角度则更新车辆的当前方向角-360..360度{@link double}如果车辆不提供角度
* *
* @param ref A reference to the vehicle to be modified. * @param ref A reference to the vehicle to be modified.
* @param angle The vehicle's orientation angle. * @param angle The vehicle's orientation angle.
@ -92,6 +97,7 @@ public interface InternalVehicleService
/** /**
* Places a vehicle on a point. * Places a vehicle on a point.
* 将车辆放在一个点上
* *
* @param vehicleRef A reference to the vehicle to be modified. * @param vehicleRef A reference to the vehicle to be modified.
* @param pointRef A reference to the point on which the vehicle is to be placed. * @param pointRef A reference to the point on which the vehicle is to be placed.
@ -105,6 +111,7 @@ public interface InternalVehicleService
/** /**
* Updates the vehicle's current precise position in mm. * Updates the vehicle's current precise position in mm.
* 更新车辆在MM中的当前精确位置
* *
* @param ref A reference to the vehicle to be modified. * @param ref A reference to the vehicle to be modified.
* @param position The vehicle's precise position in mm. * @param position The vehicle's precise position in mm.
@ -118,6 +125,7 @@ public interface InternalVehicleService
/** /**
* Updates the vehicle's pose. * Updates the vehicle's pose.
* 更新车辆的姿势
* *
* @param ref A reference to the vehicle to be modified. * @param ref A reference to the vehicle to be modified.
* @param pose The vehicle's new pose. * @param pose The vehicle's new pose.
@ -135,6 +143,7 @@ public interface InternalVehicleService
/** /**
* Updates a vehicle's processing state. * Updates a vehicle's processing state.
* 更新车辆的处理状态
* *
* @param ref A reference to the vehicle to be modified. * @param ref A reference to the vehicle to be modified.
* @param state The vehicle's new processing state. * @param state The vehicle's new processing state.
@ -145,6 +154,7 @@ public interface InternalVehicleService
/** /**
* Updates a vehicle's recharge operation. * Updates a vehicle's recharge operation.
* 更新车辆的充电操作
* *
* @param ref A reference to the vehicle to be modified. * @param ref A reference to the vehicle to be modified.
* @param rechargeOperation The vehicle's new recharge action. * @param rechargeOperation The vehicle's new recharge action.
@ -155,6 +165,7 @@ public interface InternalVehicleService
/** /**
* Updates a vehicle's claimed resources. * Updates a vehicle's claimed resources.
* 更新车辆声称的资源
* *
* @param ref A reference to the vehicle to be modified. * @param ref A reference to the vehicle to be modified.
* @param resources The new resources. * @param resources The new resources.
@ -168,6 +179,7 @@ public interface InternalVehicleService
/** /**
* Updates a vehicle's allocated resources. * Updates a vehicle's allocated resources.
* 更新车辆分配的资源
* *
* @param ref A reference to the vehicle to be modified. * @param ref A reference to the vehicle to be modified.
* @param resources The new resources. * @param resources The new resources.
@ -181,6 +193,7 @@ public interface InternalVehicleService
/** /**
* Updates a vehicle's state. * Updates a vehicle's state.
* 更新车辆的状态
* *
* @param ref A reference to the vehicle to be modified. * @param ref A reference to the vehicle to be modified.
* @param state The vehicle's new state. * @param state The vehicle's new state.
@ -191,6 +204,7 @@ public interface InternalVehicleService
/** /**
* Updates a vehicle's length. * Updates a vehicle's length.
* 更新车辆的长度
* *
* @param ref A reference to the vehicle to be modified. * @param ref A reference to the vehicle to be modified.
* @param length The vehicle's new length. * @param length The vehicle's new length.
@ -204,6 +218,7 @@ public interface InternalVehicleService
/** /**
* Updates the vehicle's bounding box. * Updates the vehicle's bounding box.
* 更新车辆的边界框
* *
* @param ref A reference to the vehicle. * @param ref A reference to the vehicle.
* @param boundingBox The vehicle's new bounding box (in mm). * @param boundingBox The vehicle's new bounding box (in mm).
@ -219,6 +234,7 @@ public interface InternalVehicleService
/** /**
* Updates a vehicle's transport order. * Updates a vehicle's transport order.
* 更新车辆的运输订单
* *
* @param vehicleRef A reference to the vehicle to be modified. * @param vehicleRef A reference to the vehicle to be modified.
* @param orderRef A reference to the transport order the vehicle processes. * @param orderRef A reference to the transport order the vehicle processes.

View File

@ -29,6 +29,7 @@ public abstract class KernelInjectionModule
/** /**
* Sets the scheduler implementation to be used. * Sets the scheduler implementation to be used.
* 设置要使用的调度程序实现
* *
* @param clazz The implementation. * @param clazz The implementation.
*/ */
@ -38,6 +39,7 @@ public abstract class KernelInjectionModule
/** /**
* Sets the router implementation to be used. * Sets the router implementation to be used.
* 设置要使用的路由器实现
* *
* @param clazz The implementation. * @param clazz The implementation.
*/ */
@ -47,6 +49,7 @@ public abstract class KernelInjectionModule
/** /**
* Sets the dispatcher implementation to be used. * Sets the dispatcher implementation to be used.
* 设置要使用的调度程序实现
* *
* @param clazz The implementation. * @param clazz The implementation.
*/ */
@ -56,6 +59,7 @@ public abstract class KernelInjectionModule
/** /**
* Sets the peripheral job dispatcher implementation to be used. * Sets the peripheral job dispatcher implementation to be used.
* 设置要使用的外围作业调度器实现
* *
* @param clazz The implementation. * @param clazz The implementation.
*/ */
@ -65,6 +69,7 @@ public abstract class KernelInjectionModule
/** /**
* Returns a multibinder that can be used to register kernel extensions for all kernel states. * Returns a multibinder that can be used to register kernel extensions for all kernel states.
* 返回一个多索引器可用于为所有内核状态注册内核扩展
* *
* @return The multibinder. * @return The multibinder.
*/ */
@ -75,6 +80,7 @@ public abstract class KernelInjectionModule
/** /**
* Returns a multibinder that can be used to register kernel extensions for the kernel's modelling * Returns a multibinder that can be used to register kernel extensions for the kernel's modelling
* state. * state.
* 返回一个多索引器可用于为内核的建模状态注册内核扩展
* *
* @return The multibinder. * @return The multibinder.
*/ */
@ -85,6 +91,7 @@ public abstract class KernelInjectionModule
/** /**
* Returns a multibinder that can be used to register kernel extensions for the kernel's operating * Returns a multibinder that can be used to register kernel extensions for the kernel's operating
* state. * state.
* 返回一个多索引器可用于为内核的操作状态注册内核扩展
* *
* @return The multibinder. * @return The multibinder.
*/ */
@ -94,6 +101,7 @@ public abstract class KernelInjectionModule
/** /**
* Returns a multibinder that can be used to register vehicle communication adapter factories. * Returns a multibinder that can be used to register vehicle communication adapter factories.
* 返回一个多索引器可用于注册车辆通信适配器工厂
* *
* @return The multibinder. * @return The multibinder.
*/ */
@ -103,6 +111,7 @@ public abstract class KernelInjectionModule
/** /**
* Returns a multibinder that can be used to register vehicle data transformer factories. * Returns a multibinder that can be used to register vehicle data transformer factories.
* 返回一个多索引器可用于注册车辆数据转换器工厂
* *
* @return The multibinder. * @return The multibinder.
*/ */
@ -112,6 +121,7 @@ public abstract class KernelInjectionModule
/** /**
* Returns a multibinder that can be used to register peripheral communication adapter factories. * Returns a multibinder that can be used to register peripheral communication adapter factories.
* 返回一个多索引器可用于注册外围通信适配器工厂
* *
* @return The multibinder. * @return The multibinder.
*/ */
@ -121,6 +131,7 @@ public abstract class KernelInjectionModule
/** /**
* Returns a multibinder that can be used to register transport order cleanup approvals. * Returns a multibinder that can be used to register transport order cleanup approvals.
* 返回一个多索引器可用于注册运输订单清理批准
* *
* @return The multibinder. * @return The multibinder.
*/ */
@ -130,6 +141,7 @@ public abstract class KernelInjectionModule
/** /**
* Returns a multibinder that can be used to register order sequence cleanup approvals. * Returns a multibinder that can be used to register order sequence cleanup approvals.
* 返回一个多索引器可用于注册订单序列清理批准
* *
* @return The multibinder. * @return The multibinder.
*/ */
@ -139,6 +151,7 @@ public abstract class KernelInjectionModule
/** /**
* Returns a multibinder that can be used to register peripheral job cleanup approvals. * Returns a multibinder that can be used to register peripheral job cleanup approvals.
* 返回一个多索引器可用于注册订单序列清理批准
* *
* @return The multibinder. * @return The multibinder.
*/ */
@ -148,6 +161,7 @@ public abstract class KernelInjectionModule
/** /**
* Returns a multibinder that can be used to register scheduler modules. * Returns a multibinder that can be used to register scheduler modules.
* 返回一个可用于注册调度程序模块的多索引器
* *
* @return The multibinder. * @return The multibinder.
*/ */
@ -157,6 +171,7 @@ public abstract class KernelInjectionModule
/** /**
* Returns a mapbinder that can be used to register edge evaluators. * Returns a mapbinder that can be used to register edge evaluators.
* 返回一个可用于注册边缘评估器的地图绑定器
* *
* @return The mapbinder. * @return The mapbinder.
*/ */

View File

@ -7,16 +7,24 @@ import static java.util.Objects.requireNonNull;
import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.Assisted;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.time.LocalDateTime;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.opentcs.common.LoopbackAdapterConstants; import org.opentcs.common.LoopbackAdapterConstants;
import org.opentcs.customizations.kernel.KernelExecutor; import org.opentcs.customizations.kernel.KernelExecutor;
import org.opentcs.data.model.Point;
import org.opentcs.data.model.Pose;
import org.opentcs.data.model.Triple;
import org.opentcs.data.model.Vehicle; import org.opentcs.data.model.Vehicle;
import org.opentcs.data.order.Route.Step; import org.opentcs.data.order.Route.Step;
import org.opentcs.data.order.TransportOrder; import org.opentcs.data.order.TransportOrder;
@ -27,6 +35,20 @@ import org.opentcs.drivers.vehicle.SimVehicleCommAdapter;
import org.opentcs.drivers.vehicle.VehicleCommAdapter; import org.opentcs.drivers.vehicle.VehicleCommAdapter;
import org.opentcs.drivers.vehicle.VehicleProcessModel; import org.opentcs.drivers.vehicle.VehicleProcessModel;
import org.opentcs.drivers.vehicle.management.VehicleProcessModelTO; import org.opentcs.drivers.vehicle.management.VehicleProcessModelTO;
import org.opentcs.kc.udp.Service.ConfirmRelocation;
import org.opentcs.kc.udp.Service.HybridNavigation;
import org.opentcs.kc.udp.Service.ManualPosition;
import org.opentcs.kc.udp.Service.QryRobotRunStatus;
import org.opentcs.kc.udp.Service.QryRobotStatus;
import org.opentcs.kc.udp.Service.SubCargoStatus;
import org.opentcs.kc.udp.Service.SubRobotStatue;
import org.opentcs.kc.udp.Service.SwitchAutomaticMode;
import org.opentcs.kc.udp.Service.SwitchManualMode;
import org.opentcs.kc.udp.agv.param.function.af.LocationStatusInfo;
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.function.x17.QueryRobotRunStatusRsp;
import org.opentcs.kc.udp.agv.param.rsp.RcvEventPackage;
import org.opentcs.util.ExplainedBoolean; import org.opentcs.util.ExplainedBoolean;
import org.opentcs.virtualvehicle.VelocityController.WayEntry; import org.opentcs.virtualvehicle.VelocityController.WayEntry;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -44,6 +66,7 @@ public class LoopbackCommunicationAdapter
/** /**
* The name of the load handling device set by this adapter. * The name of the load handling device set by this adapter.
* 此适配器设置的负载处理设备的名称
*/ */
public static final String LHD_NAME = "default"; public static final String LHD_NAME = "default";
/** /**
@ -52,38 +75,75 @@ public class LoopbackCommunicationAdapter
private static final Logger LOG = LoggerFactory.getLogger(LoopbackCommunicationAdapter.class); private static final Logger LOG = LoggerFactory.getLogger(LoopbackCommunicationAdapter.class);
/** /**
* An error code indicating that there's a conflict between a load operation and the vehicle's * An error code indicating that there's a conflict between a load operation and the vehicle's
* 一个错误代码指示加载作与车辆的
* current load state. * current load state.
* 当前负载状态
*/ */
private static final String LOAD_OPERATION_CONFLICT = "cannotLoadWhenLoaded"; private static final String LOAD_OPERATION_CONFLICT = "cannotLoadWhenLoaded";
/** /**
* An error code indicating that there's a conflict between an unload operation and the vehicle's * An error code indicating that there's a conflict between an unload operation and the vehicle's
* 一个错误代码指示卸载作与车辆的
* current load state. * current load state.
* 当前负载状态
*/ */
private static final String UNLOAD_OPERATION_CONFLICT = "cannotUnloadWhenNotLoaded"; private static final String UNLOAD_OPERATION_CONFLICT = "cannotUnloadWhenNotLoaded";
/** /**
* The time (in ms) of a single simulation step. * The time (in ms) of a single simulation step.
* 单个仿真步骤的时间 毫秒
*/ */
private static final int SIMULATION_PERIOD = 100; private static final int SIMULATION_PERIOD = 100;
/** /**
* This instance's configuration. * This instance's configuration.
* 此实例的配置
*/ */
private final VirtualVehicleConfiguration configuration; private final VirtualVehicleConfiguration configuration;
/** /**
* Indicates whether the vehicle simulation is running or not. * Indicates whether the vehicle simulation is running or not.
* 指示车辆模拟是否正在运行
*/ */
private volatile boolean isSimulationRunning; private volatile boolean isSimulationRunning;
/** /**
* The vehicle to this comm adapter instance. * The vehicle to this comm adapter instance.
* 车辆到此通信适配器实例
*/ */
private final Vehicle vehicle; private final Vehicle vehicle;
/** /**
* The vehicle's load state. * The vehicle's load state.
* 车辆的负载状态
*/ */
private LoadState loadState = LoadState.EMPTY; private LoadState loadState = LoadState.EMPTY;
/** /**
* Whether the loopback adapter is initialized or not. * Whether the loopback adapter is initialized or not.
* 环回适配器是否已初始化
*/ */
private boolean initialized; private boolean initialized;
/**
* 0xAF上报截止时间.
*/
private long sub0xafDeadline;
/**
* 0xB0上报截止时间.
*/
private long sub0xb0Deadline;
/**
* 创建线程
*/
private final ExecutorService messageProcessingPool = new ThreadPoolExecutor(
4,
8,
60L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(100),
new ThreadPoolExecutor.CallerRunsPolicy()
);
/**
* 订阅状态
*/
private static boolean SUBSCRIBE_STATUS;
/**
* 最后经过点位
*/
private static String LAST_PASSED_POINT;
/** /**
* Creates a new instance. * Creates a new instance.
@ -145,41 +205,52 @@ public class LoopbackCommunicationAdapter
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
//调用父类的 propertyChange 方法处理事件
super.propertyChange(evt); super.propertyChange(evt);
//如果事件源不是 LoopbackVehicleModel 类型直接返回
if (!((evt.getSource()) instanceof LoopbackVehicleModel)) { if (!((evt.getSource()) instanceof LoopbackVehicleModel)) {
return; return;
} }
//如果事件属性名为 LOAD_HANDLING_DEVICES
if (Objects.equals( if (Objects.equals(
evt.getPropertyName(), evt.getPropertyName(),
VehicleProcessModel.Attribute.LOAD_HANDLING_DEVICES.name() VehicleProcessModel.Attribute.LOAD_HANDLING_DEVICES.name()
)) { )) {
if (!getProcessModel().getLoadHandlingDevices().isEmpty() if (!getProcessModel().getLoadHandlingDevices().isEmpty()
&& getProcessModel().getLoadHandlingDevices().get(0).isFull()) { && getProcessModel().getLoadHandlingDevices().get(0).isFull()) {
//检查负载处理设备是否为空且第一个设备是否满载更新负载状态为 FULL 并设置车辆长度为加载状态下的长度
loadState = LoadState.FULL; loadState = LoadState.FULL;
getProcessModel().setBoundingBox( getProcessModel().setBoundingBox(
getProcessModel().getBoundingBox().withLength(configuration.vehicleLengthLoaded()) getProcessModel().getBoundingBox().withLength(configuration.vehicleLengthLoaded())
); );
} }
else { else {
//否则更新负载状态为 EMPTY 并设置车辆长度为未加载状态下的长度
loadState = LoadState.EMPTY; loadState = LoadState.EMPTY;
getProcessModel().setBoundingBox( getProcessModel().setBoundingBox(
getProcessModel().getBoundingBox().withLength(configuration.vehicleLengthUnloaded()) getProcessModel().getBoundingBox().withLength(configuration.vehicleLengthUnloaded())
); );
} }
} }
//如果事件属性名为 SINGLE_STEP_MODE
if (Objects.equals( if (Objects.equals(
evt.getPropertyName(), evt.getPropertyName(),
LoopbackVehicleModel.Attribute.SINGLE_STEP_MODE.name() LoopbackVehicleModel.Attribute.SINGLE_STEP_MODE.name()
)) { )) {
// When switching from single step mode to automatic mode and there are commands to be // When switching from single step mode to automatic mode and there are commands to be
// processed, ensure that we start/continue processing them. // processed, ensure that we start/continue processing them.
//如果单步模式关闭待处理命令队列非空且模拟未运行则启动车辆模拟
if (!getProcessModel().isSingleStepModeEnabled() if (!getProcessModel().isSingleStepModeEnabled()
&& !getSentCommands().isEmpty() && !getSentCommands().isEmpty()
&& !isSimulationRunning) { && !isSimulationRunning) {
//标记模拟正在运行
isSimulationRunning = true; isSimulationRunning = true;
//提交任务到线程池执行队列中的第一个命令
((ExecutorService) getExecutor()).submit( ((ExecutorService) getExecutor()).submit(
() -> startVehicleSimulation(getSentCommands().peek()) () -> startVehicle(getSentCommands().peek())
); );
} }
} }
@ -210,18 +281,66 @@ public class LoopbackCommunicationAdapter
public synchronized void sendCommand(MovementCommand cmd) { public synchronized void sendCommand(MovementCommand cmd) {
requireNonNull(cmd, "cmd"); requireNonNull(cmd, "cmd");
// Start the simulation task if we're not in single step mode and not simulating already. System.out.println(cmd);
System.out.println("send cmd print start");
// SubRobotStatue.command();
//订单ID
String orderName = cmd.getTransportOrder().getName();
System.out.println("orderID:" + orderName);
//路线名称
String pathName = cmd.getStep().getPath().getName();
System.out.println("pathName:" + pathName);
//路线长度
long length = cmd.getStep().getPath().getLength();
System.out.println("pathLength:" + length);
//当前点位操作
String operation = cmd.getOperation();
System.out.println("operation:" + operation);
//下发起点
String sourcePoint = cmd.getStep().getSourcePoint().getName();
System.out.println("sourcePoint:" + sourcePoint);
//下发终点
Point destinationPoint = cmd.getStep().getDestinationPoint();
//获取点类型
String pointProperty = destinationPoint.getProperty(LoopbackAdapterConstants.POINT_TYPE);
System.out.println("destinationPoint_tcs:point:" + pointProperty);
String destinationPointName = cmd.getStep().getDestinationPoint().getName();
System.out.println("destinationPointName:" + destinationPointName);
System.out.println("send cmd print end");
//订阅0xAF
// sub0xAF();
//AGV控制器执行命令
HybridNavigation.command(orderName, sourcePoint, destinationPointName, operation);
//检查当前车辆模型是否处于单步模式且未运行若满足条件则设置运行状态为true
if (!getProcessModel().isSingleStepModeEnabled() if (!getProcessModel().isSingleStepModeEnabled()
&& !isSimulationRunning) { && !isSimulationRunning) {
isSimulationRunning = true; isSimulationRunning = true;
// The command is added to the sent queue after this method returns. Therefore
// we have to explicitly start the simulation like this. if (LAST_PASSED_POINT == null) {
//设置最后经过点为起点
LAST_PASSED_POINT = sourcePoint;
//设置订阅状态为true
SUBSCRIBE_STATUS = true;
}
sub0xAF();
// 展示模拟车辆
if (getSentCommands().isEmpty()) { if (getSentCommands().isEmpty()) {
((ExecutorService) getExecutor()).submit(() -> startVehicleSimulation(cmd)); ((ExecutorService) getExecutor()).submit(() -> startVehicle(cmd));
} }
else { else {
((ExecutorService) getExecutor()).submit( ((ExecutorService) getExecutor()).submit(
() -> startVehicleSimulation(getSentCommands().peek()) () -> startVehicle(getSentCommands().peek())
); );
} }
} }
@ -234,6 +353,101 @@ public class LoopbackCommunicationAdapter
@Override @Override
public void processMessage(Object message) { public void processMessage(Object message) {
if (message instanceof byte[]) {
updateVehicleModel(message);
}
else if (message instanceof HashMap<?, ?>) {
//todo 测试代码----成功
HashMap<?, ?> msg = (HashMap<?, ?>) message;
getProcessModel().setEnergyLevel((int) msg.get("energy"));
getProcessModel().setState(Vehicle.State.EXECUTING);
long positionX = (long) msg.get("positionX");
long positionY = (long) msg.get("positionY");
Triple triple = new Triple(positionX, positionY, 0);
double positionAngle = (double) msg.get("positionAngle");
getProcessModel().setPose(new Pose(triple, positionAngle));
}
}
private void updateVehicleModel(Object message) {
try {
byte[] body = (byte[])message;
RcvEventPackage rcv = new RcvEventPackage(body[22], body);
if (body[21] == (byte) 0xAF) {
System.out.println("0xAF sub success");
//AGV状态订阅
QueryRobotStatusRsp queryRobotStatusRsp = new QueryRobotStatusRsp(rcv.getDataBytes());
System.out.println();
//电量--目前无电量返回设置一个随机值
float batteryPercentage = queryRobotStatusRsp.batteryStatusInfo.batteryPercentage;
getProcessModel().setEnergyLevel(89);
//设置AGV最后一个点位置,不设置最后经过点opentcs无法调度
String vehicleNowPosition = getProcessModel().getPosition();
String lastPassPointId = (queryRobotStatusRsp.locationStatusInfo.lastPassPointId).toString();
if (vehicleNowPosition == null || !vehicleNowPosition.equals(lastPassPointId)) {
initVehiclePosition(lastPassPointId);
}
//设置车辆姿势(官方弃用设置车辆精确位置)-------------------目前车辆返回位置为固定值
/* long positionX = (long) queryRobotStatusRsp.locationStatusInfo.globalX;
long positionY = (long) queryRobotStatusRsp.locationStatusInfo.globalY;
Triple triple = new Triple(positionX, positionY, 0);
double positionAngle = queryRobotStatusRsp.locationStatusInfo.absoluteDirecAngle;
LocalDateTime now = LocalDateTime.now();
System.out.println(now + "[positionX:" + positionX + "] [positionY:" + positionY + "] [positionAngle:" + positionAngle + "]");
getProcessModel().setPose(new Pose(triple, positionAngle));*/
//到期续订
renewalSubscribe0xAF();
} else if (body[21] == (byte) 0xB0) {
System.out.println("0xB0 sub success");
//载货状态订阅
QueryCargoStatusRsp queryCargoStatusRsp = new QueryCargoStatusRsp(rcv.getDataBytes());
if (queryCargoStatusRsp.isCargo == 0) {
this.loadState = LoadState.EMPTY;
}
else {
this.loadState = LoadState.FULL;
}
//到期续订
renewalSubscribe0xB0();
}
} catch (Exception e) {
throw new RuntimeException("processMessage_messageExecutorPool:" + e);
}
}
/**
* 订阅到期自动续订0xAF.
*/
private void renewalSubscribe0xAF() {
Date now = new Date();
if (sub0xafDeadline - now.getTime() <= SubRobotStatue.intervalTime && SUBSCRIBE_STATUS) {
sub0xAF();
}
}
/**
* 订阅到期自动续订0xB0.
*/
private void renewalSubscribe0xB0() {
Date now = new Date();
if (sub0xb0Deadline - now.getTime() <= SubCargoStatus.intervalTime && SUBSCRIBE_STATUS) {
sub0xB0();
}
} }
@Override @Override
@ -293,15 +507,18 @@ public class LoopbackCommunicationAdapter
@Override @Override
protected synchronized void connectVehicle() { protected synchronized void connectVehicle() {
// getProcessModel().setCommAdapterConnected(true);
initAGV();
} }
@Override @Override
protected synchronized void disconnectVehicle() { protected synchronized void disconnectVehicle() {
getProcessModel().setCommAdapterConnected(false);
} }
@Override @Override
protected synchronized boolean isVehicleConnected() { protected synchronized boolean isVehicleConnected() {
return true; return getProcessModel().isCommAdapterConnected();
} }
@Override @Override
@ -332,6 +549,36 @@ public class LoopbackCommunicationAdapter
} }
} }
/**
* 执行车辆移动指令
*
* @param command 移动指令
*/
private void startVehicle(MovementCommand command) {
LOG.debug("-Starting vehicle for command: {}", command);
Step step = command.getStep();
getProcessModel().setState(Vehicle.State.EXECUTING);
if (step.getPath() == null) {
LOG.debug("-Starting operation...");
//动作执行待完成
operationExec(command);
} else {
getProcessModel().getVelocityController().addWayEntry(
new WayEntry(
step.getPath().getLength(),
maxVelocity(step),
step.getDestinationPoint().getName(),
step.getVehicleOrientation()
)
);
LOG.debug("-Starting movement ...");
movementExec(command);
}
}
private void startVehicleSimulation(MovementCommand command) { private void startVehicleSimulation(MovementCommand command) {
LOG.debug("Starting vehicle simulation for command: {}", command); LOG.debug("Starting vehicle simulation for command: {}", command);
Step step = command.getStep(); Step step = command.getStep();
@ -370,12 +617,54 @@ public class LoopbackCommunicationAdapter
: step.getPath().getMaxVelocity(); : step.getPath().getMaxVelocity();
} }
/**
* 执行运行指令.
*
* @param command 要执行的命令
*/
private void movementExec(MovementCommand command) {
//检查当前车辆模型的速度控制器是否有路径条目若无则直接返回
if (!getProcessModel().getVelocityController().hasWayEntries()) {
return;
}
//获取AGV最终经过点
String currentPoint = getProcessModel().getPosition() != null ? getProcessModel().getPosition() : "";
//获取当前路径条目并推进时间步长检查是否仍处于同一路径条目
if (currentPoint.equals(LAST_PASSED_POINT)) {
//若是则重新调度当前方法以继续模拟
getExecutor().schedule(
() -> movementExec(command),
SIMULATION_PERIOD,
TimeUnit.MILLISECONDS
);
} else {
LAST_PASSED_POINT = currentPoint;
//若否更新车辆位置为上一路径条目的目标点并根据命令是否有操作决定进入操作模拟或完成命令并模拟下一个命令
LOG.debug("-Movement finished.");
if (!command.hasEmptyOperation()) {
//执行AGV动作
LOG.debug("-Starting operation...");
operationExec(command);
} else {
//完成当前命令
finishMoveCmd(command);
//执行下一个命令
nextCommand();
}
}
}
/** /**
* Simulate the movement part of a MovementCommand. * Simulate the movement part of a MovementCommand.
* *
* @param command The command to simulate. * @param command The command to simulate.
*/ */
private void movementSimulation(MovementCommand command) { private void movementSimulation(MovementCommand command) {
//检查当前车辆模型的速度控制器是否有路径条目若无则直接返回
if (!getProcessModel().getVelocityController().hasWayEntries()) { if (!getProcessModel().getVelocityController().hasWayEntries()) {
return; return;
} }
@ -384,7 +673,9 @@ public class LoopbackCommunicationAdapter
getProcessModel().getVelocityController().advanceTime(getSimulationTimeStep()); getProcessModel().getVelocityController().advanceTime(getSimulationTimeStep());
WayEntry currentWayEntry = getProcessModel().getVelocityController().getCurrentWayEntry(); WayEntry currentWayEntry = getProcessModel().getVelocityController().getCurrentWayEntry();
//if we are still on the same way entry then reschedule to do it again //if we are still on the same way entry then reschedule to do it again
//获取当前路径条目并推进时间步长检查是否仍处于同一路径条目
if (prevWayEntry == currentWayEntry) { if (prevWayEntry == currentWayEntry) {
//若是则重新调度当前方法以继续模拟
getExecutor().schedule( getExecutor().schedule(
() -> movementSimulation(command), () -> movementSimulation(command),
SIMULATION_PERIOD, SIMULATION_PERIOD,
@ -394,6 +685,7 @@ public class LoopbackCommunicationAdapter
else { else {
//if the way enties are different then we have finished this step //if the way enties are different then we have finished this step
//and we can move on. //and we can move on.
//若否更新车辆位置为上一路径条目的目标点并根据命令是否有操作决定进入操作模拟或完成命令并模拟下一个命令
getProcessModel().setPosition(prevWayEntry.getDestPointName()); getProcessModel().setPosition(prevWayEntry.getDestPointName());
LOG.debug("Movement simulation finished."); LOG.debug("Movement simulation finished.");
if (!command.hasEmptyOperation()) { if (!command.hasEmptyOperation()) {
@ -411,8 +703,18 @@ public class LoopbackCommunicationAdapter
} }
} }
/**
* 执行移动命令的操作部分
*
* @param command 要执行的命令
*/
private void operationExec(MovementCommand command) {
}
/** /**
* Simulate the operation part of a movement command. * Simulate the operation part of a movement command.
* 模拟移动命令的操作部分
* *
* @param command The command to simulate. * @param command The command to simulate.
* @param timePassed The amount of time passed since starting the simulation. * @param timePassed The amount of time passed since starting the simulation.
@ -467,6 +769,7 @@ public class LoopbackCommunicationAdapter
/** /**
* Simulate recharging the vehicle. * Simulate recharging the vehicle.
* 模拟为车辆充电
* *
* @param rechargePosition The vehicle position where the recharge simulation was started. * @param rechargePosition The vehicle position where the recharge simulation was started.
* @param rechargePercentage The recharge percentage of the vehicle while it is charging. * @param rechargePercentage The recharge percentage of the vehicle while it is charging.
@ -512,14 +815,43 @@ public class LoopbackCommunicationAdapter
+ (float) (configuration.rechargePercentagePerSecond() / 1000.0) * SIMULATION_PERIOD; + (float) (configuration.rechargePercentagePerSecond() / 1000.0) * SIMULATION_PERIOD;
} }
/**
* 结束移动命令
* @param command 指令
*/
private void finishMoveCmd(MovementCommand command) {
//检查已发送命令队列的大小是否小于等于1且未发送命令队列是否为空
if (getSentCommands().size() <= 1 && getUnsentCommands().isEmpty()) {
System.out.println("-getSentCommands <= 1 && getUnsentCommands is null");
getProcessModel().setState(Vehicle.State.IDLE);
//清除订单对应唯一ID
HybridNavigation.delUniqueOrderID(command);
}
//如果传入指令和移动指令队列第一条数据相同
if (Objects.equals(getSentCommands().peek(), command)) {
// 完成当前任务
getProcessModel().commandExecuted(getSentCommands().poll());
} else {
//
LOG.warn(
"-{}: Exec command not oldest in sent queue: {} != {}",
getName(),
command,
getSentCommands().peek()
);
}
}
private void finishMovementCommand(MovementCommand command) { private void finishMovementCommand(MovementCommand command) {
//Set the vehicle state to idle //Set the vehicle state to idle
if (getSentCommands().size() <= 1 && getUnsentCommands().isEmpty()) { if (getSentCommands().size() <= 1 && getUnsentCommands().isEmpty()) {
getProcessModel().setState(Vehicle.State.IDLE); getProcessModel().setState(Vehicle.State.IDLE);
} }
if (Objects.equals(getSentCommands().peek(), command)) { if (Objects.equals(getSentCommands().peek(), command)) {
// Let the comm adapter know we have finished this command. // Let the comm adapter know we have finished this command. 让通信适配器知道我们已经完成了这个命令
getProcessModel().commandExecuted(getSentCommands().poll()); getProcessModel().commandExecuted(getSentCommands().poll());
// HybridNavigation.delUniqueOrderID(command);
} }
else { else {
LOG.warn( LOG.warn(
@ -531,6 +863,22 @@ public class LoopbackCommunicationAdapter
} }
} }
void nextCommand() {
if (getSentCommands().isEmpty() || getProcessModel().isSingleStepModeEnabled()) {
LOG.debug("Vehicle exec is done.");
getProcessModel().setState(Vehicle.State.IDLE);
isSimulationRunning = false;
LAST_PASSED_POINT = null;
SUBSCRIBE_STATUS = false;
}
else {
LOG.debug("Triggering exec for next command: {}", getSentCommands().peek());
((ExecutorService) getExecutor()).submit(
() -> startVehicle(getSentCommands().peek())
);
}
}
void simulateNextCommand() { void simulateNextCommand() {
if (getSentCommands().isEmpty() || getProcessModel().isSingleStepModeEnabled()) { if (getSentCommands().isEmpty() || getProcessModel().isSingleStepModeEnabled()) {
LOG.debug("Vehicle simulation is done."); LOG.debug("Vehicle simulation is done.");
@ -556,4 +904,145 @@ public class LoopbackCommunicationAdapter
EMPTY, EMPTY,
FULL; FULL;
} }
/**
* 初始化AGV
* 步骤:
* 1调度软件启动机器人启动无顺序要求
* 2等待调度系统以及机器人控制器启动完成调度系统启动即向机器人发送状态查询查询成功即为启动完成
* 3调度软件发送订阅信令至机器人表明订阅机器人状态信息或载货状态机器人接收到订阅信令会依据订阅要求推送订阅信息度软件需要根据订阅信令中上报持续时间提前刷新机器人推送的上报持续时间
* 4调度软件持续监控机器人实时状态
* 5导航初始化
*/
private void initAGV() {
if (true) {
//0xAF获取AGV状态
System.out.println("=================---initAGV_0xAF");
QueryRobotStatusRsp qryRobotStatusRsp = QryRobotStatus.command();
if (qryRobotStatusRsp == null) {
getProcessModel().setCommAdapterConnected(false);
throw new RuntimeException("initAGV 0xAF response is null");
}
//开启0xAF订阅
sub0xAF();
//开启0xB0订阅
// sub0xB0()
// //设置车辆姿势(官方弃用设置车辆精确位置)-------------------目前车辆返回位置为固定值
// long positionX = (long) qryRobotStatusRsp.locationStatusInfo.globalX;
// long positionY = (long) qryRobotStatusRsp.locationStatusInfo.globalY;
// Triple triple = new Triple(positionX, positionY, 0);
// double positionAngle = qryRobotStatusRsp.locationStatusInfo.absoluteDirecAngle;
// getProcessModel().setPose(new Pose(triple, positionAngle));
//3 查询机器人运行状态命令码0x17等待机器人定位状态为定位完成
System.out.println("=================---initAGV_0x17");
QueryRobotRunStatusRsp qryRobotRunStatusRsp = QryRobotRunStatus.command();
if (qryRobotRunStatusRsp == null) {
getProcessModel().setCommAdapterConnected(false);
throw new RuntimeException("initAGV 0x17 response is null");
}
if (qryRobotRunStatusRsp.robotLocalizationState == 0) {
getProcessModel().setCommAdapterConnected(false);
throw new RuntimeException("AGV定位失败请执行手动定位");
}
else if (qryRobotRunStatusRsp.robotLocalizationState == 2) {
getProcessModel().setCommAdapterConnected(false);
throw new RuntimeException("AGV定位中,请稍后再试");
}
//5 切换成自动模式命令码0x03变量NaviControl 修改为1
System.out.println();
System.out.println("=================---initAGV_0x03-----111111");
SwitchAutomaticMode.command();
getProcessModel().setState(Vehicle.State.IDLE);
//打开通讯适配器连接
getProcessModel().setCommAdapterConnected(true);
} else {
//0xAF获取AGV状态
System.out.println("=================---initAGV_0xAF");
QueryRobotStatusRsp qryRobotStatusRsp = QryRobotStatus.command();
if (qryRobotStatusRsp == null) {
getProcessModel().setCommAdapterConnected(false);
throw new RuntimeException("initAGV 0xAF response is null");
}
//开启0xAF订阅
sub0xAF();
//开启0xB0订阅
sub0xB0();
//1 切换定位为手动模式命令码0x03变量NaviControl 修改为0
System.out.println();
System.out.println("=================---initAGV_0x03-----000000");
SwitchManualMode.command();
getProcessModel().setState(Vehicle.State.UNAVAILABLE);
//设置AGV最后一个点位置
String lastPassPointId = (qryRobotStatusRsp.locationStatusInfo.lastPassPointId).toString();
initVehiclePosition(lastPassPointId);
//2 执行机器人手动定位 命令码0x14
System.out.println("=================---initAGV_0x14");
LocationStatusInfo locationStatusInfo = qryRobotStatusRsp.locationStatusInfo;
double agvX = locationStatusInfo.globalX;
double agvY = locationStatusInfo.globalY;
double agvAngle = locationStatusInfo.absoluteDirecAngle;
ManualPosition.command(agvX, agvY, agvAngle);
//3 查询机器人运行状态命令码0x17等待机器人定位状态为定位完成
System.out.println("=================---initAGV_0x17");
QueryRobotRunStatusRsp qryRobotRunStatusRsp = QryRobotRunStatus.command();
if (qryRobotRunStatusRsp == null) {
getProcessModel().setCommAdapterConnected(false);
throw new RuntimeException("initAGV 0x17 response is null");
}
if (qryRobotRunStatusRsp.robotLocalizationState == 0) {
getProcessModel().setCommAdapterConnected(false);
throw new RuntimeException("AGV定位失败执行手动定位中");
}
else if (qryRobotRunStatusRsp.robotLocalizationState == 2) {
getProcessModel().setCommAdapterConnected(false);
throw new RuntimeException("AGV定位中,请稍后再试");
}
//4 确认初始位置命令码0x1F
System.out.println("=================---initAGV_0x1F");
ConfirmRelocation.commnd();
//5 切换成自动模式命令码0x03变量NaviControl 修改为1
System.out.println("=================---initAGV_0x03-----111111");
SwitchAutomaticMode.command();
getProcessModel().setState(Vehicle.State.IDLE);
//打开通讯适配器连接
getProcessModel().setCommAdapterConnected(true);
}
}
private void sub0xAF(){
messageProcessingPool.submit(() -> {
Date now = new Date();
sub0xafDeadline = now.getTime() + 10000;
SubRobotStatue.command();
});
}
private void sub0xB0(){
messageProcessingPool.submit(() -> {
Date now = new Date();
sub0xb0Deadline = now.getTime() + 10000;
SubCargoStatus.command();
});
}
} }

View File

@ -58,6 +58,11 @@ public class LoopbackVehicleModelTO
return singleStepModeEnabled; return singleStepModeEnabled;
} }
/**
* 设置单步模式已启用.
* @param singleStepModeEnabled 启用单步模式
* @return LoopbackVehicleModelTO
*/
public LoopbackVehicleModelTO setSingleStepModeEnabled(boolean singleStepModeEnabled) { public LoopbackVehicleModelTO setSingleStepModeEnabled(boolean singleStepModeEnabled) {
this.singleStepModeEnabled = singleStepModeEnabled; this.singleStepModeEnabled = singleStepModeEnabled;
return this; return this;
@ -67,6 +72,11 @@ public class LoopbackVehicleModelTO
return loadOperation; return loadOperation;
} }
/**
* 设置装载操作.
* @param loadOperation 状态
* @return LoopbackVehicleModelTO
*/
public LoopbackVehicleModelTO setLoadOperation(String loadOperation) { public LoopbackVehicleModelTO setLoadOperation(String loadOperation) {
this.loadOperation = loadOperation; this.loadOperation = loadOperation;
return this; return this;
@ -76,6 +86,11 @@ public class LoopbackVehicleModelTO
return unloadOperation; return unloadOperation;
} }
/**
* 设置卸载操作.
* @param unloadOperation 状态
* @return LoopbackVehicleModelTO
*/
public LoopbackVehicleModelTO setUnloadOperation(String unloadOperation) { public LoopbackVehicleModelTO setUnloadOperation(String unloadOperation) {
this.unloadOperation = unloadOperation; this.unloadOperation = unloadOperation;
return this; return this;
@ -85,6 +100,11 @@ public class LoopbackVehicleModelTO
return operatingTime; return operatingTime;
} }
/**
* 设置运行时间.
* @param operatingTime 时间戳
* @return LoopbackVehicleModelTO
*/
public LoopbackVehicleModelTO setOperatingTime(int operatingTime) { public LoopbackVehicleModelTO setOperatingTime(int operatingTime) {
this.operatingTime = operatingTime; this.operatingTime = operatingTime;
return this; return this;
@ -94,6 +114,11 @@ public class LoopbackVehicleModelTO
return maxAcceleration; return maxAcceleration;
} }
/**
* 设置最大加速度.
* @param maxAcceleration
* @return LoopbackVehicleModelTO
*/
public LoopbackVehicleModelTO setMaxAcceleration(int maxAcceleration) { public LoopbackVehicleModelTO setMaxAcceleration(int maxAcceleration) {
this.maxAcceleration = maxAcceleration; this.maxAcceleration = maxAcceleration;
return this; return this;
@ -103,6 +128,11 @@ public class LoopbackVehicleModelTO
return maxDeceleration; return maxDeceleration;
} }
/**
* 设置最大减速.
* @param maxDeceleration
* @return LoopbackVehicleModelTO
*/
public LoopbackVehicleModelTO setMaxDeceleration(int maxDeceleration) { public LoopbackVehicleModelTO setMaxDeceleration(int maxDeceleration) {
this.maxDeceleration = maxDeceleration; this.maxDeceleration = maxDeceleration;
return this; return this;
@ -112,6 +142,11 @@ public class LoopbackVehicleModelTO
return maxFwdVelocity; return maxFwdVelocity;
} }
/**
* 设置最大前驱速度.
* @param maxFwdVelocity
* @return LoopbackVehicleModelTO
*/
public LoopbackVehicleModelTO setMaxFwdVelocity(int maxFwdVelocity) { public LoopbackVehicleModelTO setMaxFwdVelocity(int maxFwdVelocity) {
this.maxFwdVelocity = maxFwdVelocity; this.maxFwdVelocity = maxFwdVelocity;
return this; return this;
@ -121,6 +156,11 @@ public class LoopbackVehicleModelTO
return maxRevVelocity; return maxRevVelocity;
} }
/**
* 设置最大速度.
* @param maxRevVelocity
* @return LoopbackVehicleModelTO
*/
public LoopbackVehicleModelTO setMaxRevVelocity(int maxRevVelocity) { public LoopbackVehicleModelTO setMaxRevVelocity(int maxRevVelocity) {
this.maxRevVelocity = maxRevVelocity; this.maxRevVelocity = maxRevVelocity;
return this; return this;
@ -130,6 +170,11 @@ public class LoopbackVehicleModelTO
return vehiclePaused; return vehiclePaused;
} }
/**
* 将车辆设置为暂停.
* @param vehiclePaused
* @return LoopbackVehicleModelTO
*/
public LoopbackVehicleModelTO setVehiclePaused(boolean vehiclePaused) { public LoopbackVehicleModelTO setVehiclePaused(boolean vehiclePaused) {
this.vehiclePaused = vehiclePaused; this.vehiclePaused = vehiclePaused;
return this; return this;

View File

@ -15,4 +15,12 @@ public interface GuestUserCredentials {
* The default/guest password. * The default/guest password.
*/ */
String PASSWORD = "xyz"; String PASSWORD = "xyz";
/**
* 主机IP
*/
String IP = "192.168.124.111";
/**
* 内核开放端口
*/
Integer PORT = 1099;
} }

View File

@ -41,5 +41,21 @@ public interface LoopbackAdapterConstants {
* The key of the vehicle property that specifies the maximum decceleration of a vehicle. * The key of the vehicle property that specifies the maximum decceleration of a vehicle.
*/ */
String PROPKEY_DECELERATION = "loopback:deceleration"; String PROPKEY_DECELERATION = "loopback:deceleration";
/**
* AGV 控制器授权码
*/
String AGV_AUTHORIZE_CODE = "AGV:AUTHORIZE_CODE";
/**
* AGV IP
*/
String AGV_IP = "AGV:IP";
/**
* AGV 端口
*/
String AGV_PORT = "AGV:PORT";
/**
* 定制点位类型1=WMS请求点位
*/
String POINT_TYPE = "tcs:point";
} }

View File

@ -11,133 +11,136 @@ import org.opentcs.kc.common.byteutils.ByteUtils;
public class CaffeineUtil { public class CaffeineUtil {
/** /**
* 缓存的最大容量 * 缓存的最大容量
*/ */
private static final int MAXIMUM_SIZE = 1000; private static final int MAXIMUM_SIZE = 1000;
/** /**
* 缓存项的写入后过期时间 * 缓存项的写入后过期时间
*/ */
private static final int EXPIRE_AFTER_WRITE_DURATION = 10; private static final int EXPIRE_AFTER_WRITE_DURATION = 10;
/** /**
* 过期时间单位分钟 * 过期时间单位分钟
*/ */
private static final TimeUnit EXPIRE_AFTER_WRITE_TIMEUNIT = TimeUnit.MINUTES; private static final TimeUnit EXPIRE_AFTER_WRITE_TIMEUNIT = TimeUnit.MINUTES;
private static Cache<String, Object> cache; private static Cache<String, Object> cache;
private static Cache<String, AtomicInteger> cacheUUID; private static Cache<String, AtomicInteger> cacheUUID;
/** /**
* 初始化Caffeine缓存配置 * 初始化Caffeine缓存配置
*/ */
static { static {
//todo https://github.com/ben-manes/caffeine/wiki 研究一下然后升级一下版本 //todo https://github.com/ben-manes/caffeine/wiki 研究一下然后升级一下版本
cache = Caffeine.newBuilder() cache = Caffeine.newBuilder()
.maximumSize(MAXIMUM_SIZE) .maximumSize(MAXIMUM_SIZE)
.expireAfterWrite(EXPIRE_AFTER_WRITE_DURATION, EXPIRE_AFTER_WRITE_TIMEUNIT) .expireAfterWrite(EXPIRE_AFTER_WRITE_DURATION, EXPIRE_AFTER_WRITE_TIMEUNIT)
.build(); .build();
cacheUUID = Caffeine.newBuilder() cacheUUID = Caffeine.newBuilder()
.maximumSize(10) .maximumSize(10)
.expireAfterWrite(Long.MAX_VALUE, EXPIRE_AFTER_WRITE_TIMEUNIT) .expireAfterWrite(Long.MAX_VALUE, EXPIRE_AFTER_WRITE_TIMEUNIT)
.build(); .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 {
public static void main(String[] args) { if (uuid.get() >= 32000) {
byte[] a = getUUID(); cacheUUID.put("UUID", new AtomicInteger(1));
byte[] b = getUUID(); return ByteUtils.intToBytes(1);
byte[] d = ByteUtils.intToBytes(300); }
byte[] c = ByteUtils.intToBytes(32000); else {
//todo int 转两个字节长度 return ByteUtils.intToBytes(uuid.incrementAndGet());
System.out.println(Arrays.asList(getUUID())); }
} }
}
public static synchronized byte[] getUUIDAGV() {
AtomicInteger agvuuid = cacheUUID.getIfPresent("AGVUUID");
public static synchronized byte[] getUUID() { if (agvuuid == null) {
AtomicInteger uuid = cacheUUID.getIfPresent("UUID"); //transationId 从1 开始0留给心跳变量这样就固定报文了
if(uuid == null){ cacheUUID.put("AGVUUID", new AtomicInteger(1));
//transationId 从1 开始0留给心跳变量这样就固定报文了 return ByteUtils.intToBytesS(1);
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());
}
}
} }
else {
public static synchronized byte[] getUUIDAGV() { if (agvuuid.get() >= 32000) {
AtomicInteger agvuuid = cacheUUID.getIfPresent("AGVUUID"); cacheUUID.put("AGVUUID", new AtomicInteger(1));
if(agvuuid == null){ return ByteUtils.intToBytesS(1);
//transationId 从1 开始0留给心跳变量这样就固定报文了 }
cacheUUID.put("AGVUUID",new AtomicInteger(1)); else {
return ByteUtils.intToBytesS(1); return ByteUtils.intToBytesS(agvuuid.incrementAndGet());
}else { }
if(agvuuid.get() >= 32000){
cacheUUID.put("AGVUUID",new AtomicInteger(1));
return ByteUtils.intToBytesS(1);
}else {
return ByteUtils.intToBytesS(agvuuid.incrementAndGet());
}
}
} }
}
/** /**
* 获取缓存值 * 获取缓存值
* *
* @param key 缓存键 * @param key 缓存键
* @return 缓存值 * @return 缓存值
*/ */
public static Object get(String key) { public static Object get(String key) {
return cache.getIfPresent(key); return cache.getIfPresent(key);
} }
/** /**
* 设置缓存值 * 设置缓存值
* *
* @param key 缓存键 * @param key 缓存键
* @param value 缓存值 * @param value 缓存值
*/ */
public static void put(String key, Object value) { public static void put(String key, Object value) {
cache.put(key, value); cache.put(key, value);
} }
/** /**
* 移除缓存项 * 移除缓存项
* *
* @param key 缓存键 * @param key 缓存键
*/ */
public static void remove(String key) { public static void remove(String key) {
cache.invalidate(key); cache.invalidate(key);
} }
/** /**
* 清空缓存 * 清空缓存
*/ */
public static void clear() { public static void clear() {
cache.invalidateAll(); cache.invalidateAll();
} }
/** /**
* 获取缓存中的所有值 * 获取缓存中的所有值
* *
* @return 缓存中的所有值集合 * @return 缓存中的所有值集合
*/ */
public static Collection<Object> getAllValues() { public static Collection<Object> getAllValues() {
return cache.asMap().values(); return cache.asMap().values();
} }
/** /**
* 清空缓存中的所有值 * 清空缓存中的所有值
*/ */
public static void removeAllValues() { public static void removeAllValues() {
cache.invalidateAll(); cache.invalidateAll();
} }
} }

View File

@ -6,34 +6,35 @@ package org.opentcs.kc.common;
* 2017年3月30日 下午7:05:54 * 2017年3月30日 下午7:05:54
*/ */
public interface Const { public interface Const {
/** /**
* 服务器地址 * 服务器地址
*/ */
public static final String SERVER = "127.0.0.1"; public static final String SERVER = "127.0.0.1";
public static void main(String[] args) { 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 s
String[] split = s.split(","); = "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";
System.out.println(split.length); String[] split = s.split(",");
} System.out.println(split.length);
}
/** /**
* 监听端口 * 监听端口
*/ */
public static final int PORT = 6789; public static final int PORT = 6789;
//public static final int PORT = 9000; //public static final int PORT = 9000;
/** /**
* 心跳超时时间 * 心跳超时时间
*/ */
public static final int TIMEOUT = 5000; public static final int TIMEOUT = 5000;
public static final int NEED_REPLY_TYPE = 1; public static final int NEED_REPLY_TYPE = 1;
public static final int NO_REPLY_TYPE = 2; public static final int NO_REPLY_TYPE = 2;
public static final int HEARTBEAT_TYPE = 3; public static final int HEARTBEAT_TYPE = 3;
public static final Integer REQUEST_TYPE = 1; public static final Integer REQUEST_TYPE = 1;
public static final Integer RESPONSE_TYPE = 2; public static final Integer RESPONSE_TYPE = 2;
} }

View File

@ -3,43 +3,38 @@ package org.opentcs.kc.common;
import java.util.Arrays; import java.util.Arrays;
/**
* @Desc: "通用的数据传输底层类"
* @Author: caixiang
* @DATE: 2022/10/18 16:22
*/
public class Package { public class Package {
private byte[] body; private byte[] body;
private String transationId; private String transationId;
public Package(byte[] body, String transationId) { public Package(byte[] body, String transationId) {
this.body = body; this.body = body;
this.transationId = transationId; this.transationId = transationId;
} }
public byte[] getBody() { public byte[] getBody() {
return body; return body;
} }
public void setBody(byte[] body) { public void setBody(byte[] body) {
this.body = body; this.body = body;
} }
public String getTransationId() { public String getTransationId() {
return transationId; return transationId;
} }
public void setTransationId(String transationId) { public void setTransationId(String transationId) {
this.transationId = transationId; this.transationId = transationId;
} }
@Override @Override
public String toString() { public String toString() {
return "Package{" + return "Package{" +
"body=" + Arrays.toString(body) + "body=" + Arrays.toString(body) +
", transationId='" + transationId + '\'' + ", transationId='" + transationId + '\'' +
'}'; '}';
} }
} }

View File

@ -1,64 +1,66 @@
/* /*
Copyright 2016 S7connector members (github.com/s7connector) * Copyright 2016 S7connector members (github.com/s7connector)
*
Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
You may obtain a copy of the License at * You may obtain a copy of the License at
*
http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
*
Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
limitations under the License. * limitations under the License.
*/ */
package org.opentcs.kc.common; package org.opentcs.kc.common;
/** /**
* The Class S7Exception is an exception related to S7 Communication * The Class S7Exception is an exception related to S7 Communication
*/ */
public final class ParseDataException extends RuntimeException { public final class ParseDataException
extends
RuntimeException {
/** The Constant serialVersionUID. */ /** The Constant serialVersionUID. */
private static final long serialVersionUID = -4761415733559374116L; private static final long serialVersionUID = -4761415733559374116L;
/** /**
* Instantiates a new s7 exception. * Instantiates a new s7 exception.
*/ */
public ParseDataException() { public ParseDataException() {
} }
/** /**
* Instantiates a new s7 exception. * Instantiates a new s7 exception.
* *
* @param message * @param message
* the message * the message
*/ */
public ParseDataException(final String message) { public ParseDataException(final String message) {
super(message); super(message);
} }
/** /**
* Instantiates a new s7 exception. * Instantiates a new s7 exception.
* *
* @param message * @param message
* the message * the message
* @param cause * @param cause
* the cause * the cause
*/ */
public ParseDataException(final String message, final Throwable cause) { public ParseDataException(final String message, final Throwable cause) {
super(message, cause); super(message, cause);
} }
/** /**
* Instantiates a new s7 exception. * Instantiates a new s7 exception.
* *
* @param cause * @param cause
* the cause * the cause
*/ */
public ParseDataException(final Throwable cause) { public ParseDataException(final Throwable cause) {
super(cause); super(cause);
} }
} }

View File

@ -1,49 +1,44 @@
package org.opentcs.kc.common; package org.opentcs.kc.common;
/**
* @Desc: "通用的数据传输底层类"
* @Author: caixiang
* @DATE: 2022/10/18 16:22
*/
public class RcvPackage { public class RcvPackage {
private boolean isOk; private boolean isOk;
private Object value; private Object value;
private String content; private String content;
public RcvPackage(boolean isOk, Object value, String content) { public RcvPackage(boolean isOk, Object value, String content) {
this.isOk = isOk; this.isOk = isOk;
this.value = value; this.value = value;
this.content = content; this.content = content;
} }
public RcvPackage() {
}
public boolean isOk() { public RcvPackage() {
return isOk; }
}
public void setValue(Object value) { public boolean isOk() {
this.value = value; return isOk;
} }
public Object getValue() { public void setValue(Object value) {
return value; this.value = value;
} }
public String getContent() { public Object getValue() {
return content; return value;
} }
public String getContent() {
return content;
}
@Override
@Override public String toString() {
public String toString() { return "RcvPackage{" +
return "RcvPackage{" + "isOk=" + isOk +
"isOk=" + isOk + ", value=" + value +
", value=" + value + ", content='" + content + '\'' +
", content='" + content + '\'' + '}';
'}'; }
}
} }

View File

@ -6,73 +6,60 @@ import java.time.format.DateTimeFormatter;
import java.util.Random; import java.util.Random;
import java.util.UUID; import java.util.UUID;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2022/10/24 16:27
*/
public class Utils { public class Utils {
/**
* java生成随机数字15位数 public static String getTransationId() {
* @return String val = String.valueOf(System.currentTimeMillis());
*/ Random random = new Random();
public static String getTransationId() { for (int i = 0; i < 2; i++) {
String val = String.valueOf(System.currentTimeMillis()); val += String.valueOf(random.nextInt(10));
Random random = new Random(); }
for (int i = 0; i < 2; i++) { return val;
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();
* (获取指定长度uuid)
*
* @return
*/
public static String getUUID(int len)
{
if(0 >= len)
{
return null;
}
String uuid = getUUID(); for (int i = 0; i < len; i++) {
StringBuffer str = new StringBuffer(); str.append(uuid.charAt(i));
for (int i = 0; i < len; i++)
{
str.append(uuid.charAt(i));
}
return str.toString();
} }
/** return str.toString();
*32位默认长度的uuid }
* (获取32位uuid)
* /**
* @return * 32位默认长度的uuid
*/ * (获取32位uuid)
public static String getUUID() *
{ * @return
return UUID.randomUUID().toString().replace("-", ""); */
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"));
/*
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"));
}
} }
}
} }

View File

@ -4,346 +4,356 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
/** // 对数字和字节进行转换 假设数据存储是以大端模式存储的
* @Desc: "" // byte: 字节类型 占8位二进制 00000000
* @Author: caixiang // char: 字符类型 占2个字节 16位二进制 byte[0] byte[1]
* @DATE: 2022/4/1 17:32 // 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]
// byte: 字节类型 占8位二进制 00000000 // float: 浮点数(小数) 占4个字节 32位二进制 byte[0] byte[1] byte[2] byte[3]
// char: 字符类型 占2个字节 16位二进制 byte[0] byte[1] // double: 双精度浮点数(小数) 占8个字节 64位二进制 byte[0] byte[1] byte[2] byte[3] byte[4]byte[5] byte[6] byte[7]
// 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 { public class ByteUtil {
/** /**
* int转byte * int转byte
* *
* @param intValue int值 * @param intValue int值
* @return byte值 * @return byte值
*/ */
public static byte intToByte(int intValue) { public static byte intToByte(int intValue) {
return (byte) 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; //
} }
public static byte intTo1Byte(int intValue) { }
ByteBuffer byteBuffer = ByteBuffer.allocate(4);
byteBuffer.putInt(intValue);
byte[] byteArray = byteBuffer.array();
return byteArray[3]; // 取低字节
}
/** /**
* byte转无符号int * int转byte数组<br>
* * 默认以小端序转换
* @param byteValue byte值 *
* @return 无符号int值 * @param intValue int值
* @since 3.2.0 * @return byte数组
*/ */
public static int byteToUnsignedInt(byte byteValue) { public static byte[] intToBytes(int intValue) {
// Java 总是把 byte 当做有符处理我们可以通过将其和 0xFF 进行二进制与得到它的无符值 return intToBytes(intValue, ByteOrder.LITTLE_ENDIAN);
return byteValue & 0xFF; }
}
/** /**
* * int转byte数组<br>
* 字符串转成16个字节的byte[] 不足16个字节前面填充0 * 自定义端序
* */ *
public static byte[] stringTo16Byte(String input) { * @param intValue int值
byte[] originalBytes = input.getBytes(StandardCharsets.UTF_8); * @param byteOrder 端序
ByteBuffer buffer = ByteBuffer.allocate(16); * @return byte数组
buffer.put(originalBytes); */
buffer.put(new byte[16 - originalBytes.length]); public static byte[] intToBytes(int intValue, ByteOrder byteOrder) {
return buffer.array();
}
/** if (ByteOrder.LITTLE_ENDIAN == byteOrder) {
* byte数组转short<br> return new byte[]{ //
* 默认以小端序转换 (byte) (intValue & 0xFF), //
* (byte) ((intValue >> 8) & 0xFF), //
* @param bytes byte数组 (byte) ((intValue >> 16) & 0xFF), //
* @return short值 (byte) ((intValue >> 24) & 0xFF) //
*/ };
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; //
}
} }
else {
/** return new byte[]{ //
* int转byte数组<br> (byte) ((intValue >> 24) & 0xFF), //
* 默认以小端序转换 (byte) ((intValue >> 16) & 0xFF), //
* (byte) ((intValue >> 8) & 0xFF), //
* @param intValue int值 (byte) (intValue & 0xFF) //
* @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[]{ // * long转byte数组<br>
(byte) (intValue & 0xFF), // * 默认以小端序转换<br>
(byte) ((intValue >> 8) & 0xFF), // * from:
(byte) ((intValue >> 16) & 0xFF), // * https://stackoverflow.com/questions/4485128/how-do-i-convert-long-to-byte-and-back-in-java
(byte) ((intValue >> 24) & 0xFF) // *
}; * @param longValue long值
* @return byte数组
*/
public static byte[] longToBytes(long longValue) {
return longToBytes(longValue, ByteOrder.LITTLE_ENDIAN);
}
} else { /**
return new byte[]{ // * long转byte数组<br>
(byte) ((intValue >> 24) & 0xFF), // * 自定义端序<br>
(byte) ((intValue >> 16) & 0xFF), // * from:
(byte) ((intValue >> 8) & 0xFF), // * https://stackoverflow.com/questions/4485128/how-do-i-convert-long-to-byte-and-back-in-java
(byte) (intValue & 0xFF) // *
}; * @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;
* 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> * double转byte数组<br>
* 自定义端序<br> * 默认以小端序转换<br>
* from: https://stackoverflow.com/questions/4485128/how-do-i-convert-long-to-byte-and-back-in-java *
* * @param doubleValue double值
* @param longValue long值 * @return byte数组
* @param byteOrder 端序 */
* @return byte数组 public static byte[] doubleToBytes(double doubleValue) {
*/ return doubleToBytes(doubleValue, ByteOrder.LITTLE_ENDIAN);
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> * double转byte数组<br>
* 默认以小端序转换<br> * 自定义端序<br>
* from: https://stackoverflow.com/questions/4485128/how-do-i-convert-long-to-byte-and-back-in-java * from:
* * https://stackoverflow.com/questions/4485128/how-do-i-convert-long-to-byte-and-back-in-java
* @param bytes byte数组 *
* @return long值 * @param doubleValue double值
*/ * @param byteOrder 端序
public static long bytesToLong(byte[] bytes) { * @return byte数组
return bytesToLong(bytes, ByteOrder.LITTLE_ENDIAN); */
} public static byte[] doubleToBytes(double doubleValue, ByteOrder byteOrder) {
return longToBytes(Double.doubleToLongBits(doubleValue), byteOrder);
}
/** /**
* byte数组转long<br> * byte数组转Double<br>
* 自定义端序<br> * 默认以小端序转换<br>
* from: https://stackoverflow.com/questions/4485128/how-do-i-convert-long-to-byte-and-back-in-java *
* * @param bytes byte数组
* @param bytes byte数组 * @return long值
* @param byteOrder 端序 */
* @return long值 public static double bytesToDouble(byte[] bytes) {
*/ return bytesToDouble(bytes, ByteOrder.LITTLE_ENDIAN);
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; /**
} * 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));
}
/** /**
* double转byte数组<br> * {@link Number}转换为
* 默认以小端序转换<br> *
* * @param number 数字
* @param doubleValue double值 * @return bytes
* @return byte数组 */
*/ public static byte[] numberToBytes(Number number) {
public static byte[] doubleToBytes(double doubleValue) { return numberToBytes(number, ByteOrder.LITTLE_ENDIAN);
return doubleToBytes(doubleValue, ByteOrder.LITTLE_ENDIAN); }
}
/** /**
* double转byte数组<br> * {@link Number}转换为
* 自定义端序<br> *
* from: https://stackoverflow.com/questions/4485128/how-do-i-convert-long-to-byte-and-back-in-java * @param number 数字
* * @param byteOrder 端序
* @param doubleValue double值 * @return bytes
* @param byteOrder 端序 */
* @return byte数组 public static byte[] numberToBytes(Number number, ByteOrder byteOrder) {
*/ if (number instanceof Double) {
public static byte[] doubleToBytes(double doubleValue, ByteOrder byteOrder) { return doubleToBytes((Double) number, byteOrder);
return longToBytes(Double.doubleToLongBits(doubleValue), byteOrder);
} }
else if (number instanceof Long) {
/** return longToBytes((Long) number, byteOrder);
* byte数组转Double<br>
* 默认以小端序转换<br>
*
* @param bytes byte数组
* @return long值
*/
public static double bytesToDouble(byte[] bytes) {
return bytesToDouble(bytes, ByteOrder.LITTLE_ENDIAN);
} }
else if (number instanceof Integer) {
/** return intToBytes((Integer) number, byteOrder);
* 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));
} }
else if (number instanceof Short) {
/** return shortToBytes((Short) number, byteOrder);
* {@link Number}转换为
*
* @param number 数字
* @return bytes
*/
public static byte[] numberToBytes(Number number) {
return numberToBytes(number, ByteOrder.LITTLE_ENDIAN);
} }
else {
/** return doubleToBytes(number.doubleValue(), byteOrder);
* {@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);
}
} }
}
} }

View File

@ -1,21 +1,16 @@
package org.opentcs.kc.common.byteutils; package org.opentcs.kc.common.byteutils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2022/12/5 11:11
*/
public class CommonFunctions { public class CommonFunctions {
/** /**
* a 整除 b 如果有余数+1 * a 整除 b 如果有余数+1
* */ */
public static Integer exactDivision(Integer a,Integer b) { public static Integer exactDivision(Integer a, Integer b) {
int c = a/b; int c = a / b;
if(a%b!=0){ if (a % b != 0) {
c = a/b+1; c = a / b + 1;
}
return c;
} }
return c;
}
} }

View File

@ -1,32 +1,27 @@
package org.opentcs.kc.common.byteutils; package org.opentcs.kc.common.byteutils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2024-05-08 11:02
*/
public class StringArrayStruc { public class StringArrayStruc {
private String[] content; private String[] content;
private Integer strSize; private Integer strSize;
public StringArrayStruc(String[] content, Integer strSize) { public StringArrayStruc(String[] content, Integer strSize) {
this.content = content; this.content = content;
this.strSize = strSize; this.strSize = strSize;
} }
public String[] getContent() { public String[] getContent() {
return content; return content;
} }
public void setContent(String[] content) { public void setContent(String[] content) {
this.content = content; this.content = content;
} }
public Integer getStrSize() { public Integer getStrSize() {
return strSize; return strSize;
} }
public void setStrSize(Integer strSize) { public void setStrSize(Integer strSize) {
this.strSize = strSize; this.strSize = strSize;
} }
} }

View File

@ -5,61 +5,62 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* @Author: 蔡翔 * 重点.
* @Date: 2019/11/6 14:11 * AsyncFuture 这个类就是票据 刚拿到这个票据是没有信息的当done == true 的时候这个票据 上就自动有信息了.
* @Version 1.0 * 这个结果类设计的比较神奇.
*
* 重点
* AsyncFuture 这个类就是票据 刚拿到这个票据是没有信息的当done == true 的时候这个票据 上就自动有信息了
* 这个结果类设计的比较神奇
*/ */
public class AsyncFuture<Object> implements Future<Object> { public class AsyncFuture<Object>
implements
Future<Object> {
private static final Logger logger = LoggerFactory.getLogger(AsyncFuture.class); private static final Logger logger = LoggerFactory.getLogger(AsyncFuture.class);
private volatile boolean done = false; private volatile boolean done = false;
private Object oldRequest; private Object oldRequest;
private Object result; private Object result;
public AsyncFuture(Object oldRequest) { public AsyncFuture(Object oldRequest) {
this.oldRequest = oldRequest; this.oldRequest = oldRequest;
} }
public AsyncFuture() {
} public AsyncFuture() {
}
public void done(Object result){
synchronized (this){ public void done(Object result) {
this.result = result; synchronized (this) {
this.done = true; this.result = result;
//注意这里的notifyAll只能唤醒 本锁的所有 下的所有 wait(),这里的锁就是 AsyncFuture这个类 this.done = true;
notifyAll(); //注意这里的notifyAll只能唤醒 本锁的所有 下的所有 wait(),这里的锁就是 AsyncFuture这个类
} notifyAll();
} }
}
@Override @Override
public Object get(Long timeout) throws Exception { public Object get(Long timeout)
return null; throws Exception {
} return null;
}
@Override @Override
public Object get(Long timeout, String transationId) throws Exception { public Object get(Long timeout, String transationId)
synchronized (this){ throws Exception {
//其实有 synchronize就相当于有一个阻塞队列当有线程执行了wait 方法就会把执行wait的这个线程给加入wait<Thread> 队列 synchronized (this) {
//当有线程执行notify方法的时候就会往这个队列中取出一个或者多个Thread,取出来以后就能执行后续代码了 //其实有 synchronize就相当于有一个阻塞队列当有线程执行了wait 方法就会把执行wait的这个线程给加入wait<Thread> 队列
//当有线程执行notify方法的时候就会往这个队列中取出一个或者多个Thread,取出来以后就能执行后续代码了
// System.out.println("get"); // System.out.println("get");
// 当线程执行wait()会把当前的锁释放然后让出CPU进入等待状态 wait()会立刻释放synchronizedobj中的obj锁以便其他线程可以执行obj.notify() // 当线程执行wait()会把当前的锁释放然后让出CPU进入等待状态 wait()会立刻释放synchronizedobj中的obj锁以便其他线程可以执行obj.notify()
// * 当线程执行notify()/notifyAll()方法时会唤醒一个处于等待状态该对象锁的线程然后继续往下执行直到执行完退出对象锁锁住的区域synchronized修饰的代码块后再释放锁 // * 当线程执行notify()/notifyAll()方法时会唤醒一个处于等待状态该对象锁的线程然后继续往下执行直到执行完退出对象锁锁住的区域synchronized修饰的代码块后再释放锁
this.wait(timeout); this.wait(timeout);
if(!done){ if (!done) {
//logger.error("T3 timeout , request information: "+oldRequest.toString()); //logger.error("T3 timeout , request information: "+oldRequest.toString());
SendedList.remove(transationId); SendedList.remove(transationId);
throw new Exception("T3 timeout , request information: "+oldRequest.toString()); throw new Exception("T3 timeout , request information: " + oldRequest.toString());
} }
//因为上面的代码是加锁的所以这里的代码也是加锁的
return result;
}
//因为上面的代码是加锁的所以这里的代码也是加锁的
return result;
} }
}
} }

View File

@ -1,14 +1,11 @@
package org.opentcs.kc.syn; package org.opentcs.kc.syn;
/**
* @Author: 蔡翔
* @Date: 2019/11/6 13:49
* @Version 1.0
*/
public interface Future<MQMessage> { public interface Future<MQMessage> {
//别人调用我的时候我先给他们返回一个结果 //别人调用我的时候我先给他们返回一个结果
MQMessage get(Long timeout) throws Exception; MQMessage get(Long timeout)
throws Exception;
MQMessage get(Long timeout, String transationId) throws Exception; MQMessage get(Long timeout, String transationId)
throws Exception;
} }

View File

@ -1,13 +1,6 @@
package org.opentcs.kc.syn; package org.opentcs.kc.syn;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2021/8/17 14:35
*/
public class Main { public class Main {
// public static void main(String[] args) throws Exception { // public static void main(String[] args) throws Exception {

View File

@ -4,12 +4,6 @@ package org.opentcs.kc.syn;
import org.opentcs.kc.common.CaffeineUtil; import org.opentcs.kc.common.CaffeineUtil;
import org.opentcs.kc.common.Package; import org.opentcs.kc.common.Package;
/**
* @Desc: "MES端 发送远程指令列表"
* @Author: caixiang
* @DATE: 2021/8/17 14:14
*/
public class SendedList { public class SendedList {
// private static HashMap<String, AsyncFuture<MBPackage>> list = new HashMap<>(); // private static HashMap<String, AsyncFuture<MBPackage>> list = new HashMap<>();
// public static synchronized AsyncFuture<MQMessage> get(String transitionId){ // public static synchronized AsyncFuture<MQMessage> get(String transitionId){
@ -19,33 +13,33 @@ public class SendedList {
// list.put(transitionId,asyncFuture); // list.put(transitionId,asyncFuture);
// } // }
public static synchronized AsyncFuture<Package> add(String transitionId, Package PackageRequest) { public static synchronized AsyncFuture<Package> add(String transitionId, Package PackageRequest) {
AsyncFuture<Package> objectAsyncFuture = new AsyncFuture<>(PackageRequest); AsyncFuture<Package> objectAsyncFuture = new AsyncFuture<>(PackageRequest);
//list.put(transitionId,objectAsyncFuture); //list.put(transitionId,objectAsyncFuture);
CaffeineUtil.put(transitionId,objectAsyncFuture); CaffeineUtil.put(transitionId, objectAsyncFuture);
return 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);
//如果超时了那么让外部把这个key 给清除掉防止 list 过大 //清除 防止这个hashMap过大
public static synchronized void remove(String transitionId) { CaffeineUtil.remove(transitionId);
//list.remove(transitionId); return true;
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;
}
} }

View File

@ -13,23 +13,24 @@ import java.util.Random;
* 类说明日志信息用String数组代替 * 类说明日志信息用String数组代替
*/ */
public class LogConst { public class LogConst {
public final static int MONITOR_SIDE_PORT = 9998; public final static int MONITOR_SIDE_PORT = 9998;
private static final String[] LOG_INFOS = { private static final String[] LOG_INFOS = {
"晨光微好,暖在夕阳。幽幽的巷子里,有着岁月酝酿的酒,愈久愈淳。一笔墨香,一盏明灯,记千帆过浪,数不尽的悲欢离合,待那水莲花开。", "晨光微好,暖在夕阳。幽幽的巷子里,有着岁月酝酿的酒,愈久愈淳。一笔墨香,一盏明灯,记千帆过浪,数不尽的悲欢离合,待那水莲花开。",
"未来无期,静在安好。一剪寒梅,纷扰了岁月,抚平了伤痕。摆动的双桨,拨动了心的潭水。陌上花开,落一地秋霜,红枫染了红尘,便许了你十里红装。", "未来无期,静在安好。一剪寒梅,纷扰了岁月,抚平了伤痕。摆动的双桨,拨动了心的潭水。陌上花开,落一地秋霜,红枫染了红尘,便许了你十里红装。",
"离别的风,风干了月的泪。夜里的美", "离别的风,风干了月的泪。夜里的美",
"是梦的呢喃低语,挥走一片彩云,段落成珠。拂袖离去,乘鹤而来,古道西风瘦马。斑驳的树影中,眉目如画的眼,轻语告别了往事如烟。", "是梦的呢喃低语,挥走一片彩云,段落成珠。拂袖离去,乘鹤而来,古道西风瘦马。斑驳的树影中,眉目如画的眼,轻语告别了往事如烟。",
"无言的殇,几世沧桑,几生悲凉。一起剪了西窗烛,听了夜来风吹雨。昨日的叹息,今日的迷离,执一伞,存了一世一笔的温情。一曲长歌,唱尽了一世繁华,一世缘……", "无言的殇,几世沧桑,几生悲凉。一起剪了西窗烛,听了夜来风吹雨。昨日的叹息,今日的迷离,执一伞,存了一世一笔的温情。一曲长歌,唱尽了一世繁华,一世缘……",
"一世恋书,那便十里花开。一生凄凉,那便霜花十里。" , "一世恋书,那便十里花开。一生凄凉,那便霜花十里。",
"一抹浓烟,便翻页书,展颜一笑,是时间带来遥远的梦。细数树的年轮,感受昨日惆怅,留一半清醒,梦一半叶落。在指尖流过的沙,海边浪花一朵朵,不相遇,才有不约而同。", "一抹浓烟,便翻页书,展颜一笑,是时间带来遥远的梦。细数树的年轮,感受昨日惆怅,留一半清醒,梦一半叶落。在指尖流过的沙,海边浪花一朵朵,不相遇,才有不约而同。",
"这世俗,太多牵挂留在心间,一点朱砂泪,一曲相诗歌。岁月朦胧,梦醒了人生,风雨相容,演绎了一段风情。雪亦梦,雨亦梦,万张红纸从天洒来。惊动了山,撼动了天。" + "这世俗,太多牵挂留在心间,一点朱砂泪,一曲相诗歌。岁月朦胧,梦醒了人生,风雨相容,演绎了一段风情。雪亦梦,雨亦梦,万张红纸从天洒来。惊动了山,撼动了天。" +
"一纸情愁,一指烟凉。一相思,一思量,水漫岸头,我们都有着自己不同的三生故事。迎一夜秋风,送一世暖阳,一切冰雪里的花开,是我一生的柔情。" + "一纸情愁,一指烟凉。一相思,一思量,水漫岸头,我们都有着自己不同的三生故事。迎一夜秋风,送一世暖阳,一切冰雪里的花开,是我一生的柔情。" +
"记忆中的短笛,有着清风须来的气息,那时我们面向大海,海风在耳边述说着大海边缘的温暖故事。安好一轮冷月,静好了一残红日,这便是我的语言,我的情丝。" + "记忆中的短笛,有着清风须来的气息,那时我们面向大海,海风在耳边述说着大海边缘的温暖故事。安好一轮冷月,静好了一残红日,这便是我的语言,我的情丝。" +
"一漫山水,一段情,留在了岁月,拭去了风,晴雨清风,倒是暖阳拂绿草。" + "一漫山水,一段情,留在了岁月,拭去了风,晴雨清风,倒是暖阳拂绿草。" +
"这便,晨光微好,花开静好……"}; "这便,晨光微好,花开静好……"};
private final static Random r = new Random(); private final static Random r = new Random();
public static String getLogInfo(){
return LOG_INFOS[r.nextInt(LOG_INFOS.length-1)]; public static String getLogInfo() {
} return LOG_INFOS[r.nextInt(LOG_INFOS.length - 1)];
}
} }

View File

@ -19,56 +19,66 @@ import java.net.InetSocketAddress;
* 类说明日志的广播端 ( Client ) * 类说明日志的广播端 ( Client )
*/ */
public class LogEventBroadcaster { public class LogEventBroadcaster {
private final EventLoopGroup group; private final EventLoopGroup group;
private final Bootstrap bootstrap; private final Bootstrap bootstrap;
public LogEventBroadcaster(InetSocketAddress remoteAddress) { public LogEventBroadcaster(InetSocketAddress remoteAddress) {
group = new NioEventLoopGroup(); group = new NioEventLoopGroup();
bootstrap = new Bootstrap(); bootstrap = new Bootstrap();
//引导该 NioDatagramChannel无连接的 //引导该 NioDatagramChannel无连接的
bootstrap.group(group).channel(NioDatagramChannel.class) bootstrap.group(group).channel(NioDatagramChannel.class)
//todo 设置 SO_BROADCAST 套接字选项option so_broadcast差异这里和单播 是有差异的 //todo 设置 SO_BROADCAST 套接字选项option so_broadcast差异这里和单播 是有差异的
.option(ChannelOption.SO_BROADCAST, true) .option(ChannelOption.SO_BROADCAST, true)
.handler(new LogEventEncoder(remoteAddress)); .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 run() throws Exception { public void stop() {
//绑定 Channel group.shutdownGracefully();
Channel ch = bootstrap.bind(0).sync().channel(); }
long count = 0;
//启动主处理循环模拟日志发送 public static void main(String[] args)
for (;;) { throws Exception {
ch.writeAndFlush(new LogMsg(null, ++count,
LogConst.getLogInfo())); //创建并启动一个新的 UdpQuestionSide 的实例
try { LogEventBroadcaster broadcaster = new LogEventBroadcaster(
//休眠 2 如果被中断则退出循环 //表明本应用发送的报文并没有一个确定的目的地也就是进行广播
Thread.sleep(2000); //就是往这个网络下 所有主机发送数据往这些主机的 LogConst.MONITOR_SIDE_PORT端口 发送数据
} catch (InterruptedException e) { //todo 这里的设置和 单播是有差异的
Thread.interrupted(); new InetSocketAddress(
break; "255.255.255.255",
} LogConst.MONITOR_SIDE_PORT
} )
);
try {
System.out.println("广播服务启动");
broadcaster.run();
} }
finally {
public void stop() { broadcaster.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();
}
} }
}
} }

View File

@ -18,30 +18,38 @@ import java.util.List;
* 作者DarkKIng * 作者DarkKIng
* 类说明解码将DatagramPacket解码为实际的日志实体类 * 类说明解码将DatagramPacket解码为实际的日志实体类
*/ */
public class LogEventDecoder extends MessageToMessageDecoder<DatagramPacket> { public class LogEventDecoder
extends
MessageToMessageDecoder<DatagramPacket> {
@Override @Override
protected void decode(ChannelHandlerContext ctx, protected void decode(
DatagramPacket datagramPacket, List<Object> out) ChannelHandlerContext ctx,
throws Exception { DatagramPacket datagramPacket, List<Object> out
//获取对 DatagramPacket 中的数据ByteBuf的引用 )
ByteBuf data = datagramPacket.content(); throws Exception {
long time = new Date().getTime(); //获取对 DatagramPacket 中的数据ByteBuf的引用
ByteBuf data = datagramPacket.content();
long time = new Date().getTime();
System.out.println(time+" 接受到发送的消息:"); System.out.println(time + " 接受到发送的消息:");
//获得消息的id //获得消息的id
long msgId = data.readLong(); long msgId = data.readLong();
//获得分隔符SEPARATOR //获得分隔符SEPARATOR
byte sepa = data.readByte(); byte sepa = data.readByte();
//获取读索引的当前位置就是分隔符的索引+1 //获取读索引的当前位置就是分隔符的索引+1
int idx = data.readerIndex(); int idx = data.readerIndex();
//提取日志消息从读索引开始到最后为日志的信息 //提取日志消息从读索引开始到最后为日志的信息
String sendMsg = data.slice(idx , String sendMsg = data.slice(
data.readableBytes()).toString(CharsetUtil.UTF_8); idx,
//构建一个新的 LogMsg 对象并且将它添加到已经解码的消息的列表中 data.readableBytes()
LogMsg event = new LogMsg(datagramPacket.sender(), ).toString(CharsetUtil.UTF_8);
msgId, sendMsg); //构建一个新的 LogMsg 对象并且将它添加到已经解码的消息的列表中
//作为本handler的处理结果交给后面的handler进行处理 LogMsg event = new LogMsg(
out.add(event); datagramPacket.sender(),
} msgId, sendMsg
);
//作为本handler的处理结果交给后面的handler进行处理
out.add(event);
}
} }

View File

@ -18,31 +18,36 @@ import java.util.List;
* 作者DarkKIng * 作者DarkKIng
* 类说明编码将实际的日志实体类编码为DatagramPacket * 类说明编码将实际的日志实体类编码为DatagramPacket
*/ */
public class LogEventEncoder extends MessageToMessageEncoder<LogMsg> { public class LogEventEncoder
private final InetSocketAddress remoteAddress; extends
MessageToMessageEncoder<LogMsg> {
private final InetSocketAddress remoteAddress;
//LogEventEncoder 创建了即将被发送到指定的 InetSocketAddress //LogEventEncoder 创建了即将被发送到指定的 InetSocketAddress
// DatagramPacket 消息 // DatagramPacket 消息
public LogEventEncoder(InetSocketAddress remoteAddress) { public LogEventEncoder(InetSocketAddress remoteAddress) {
this.remoteAddress = remoteAddress; this.remoteAddress = remoteAddress;
} }
@Override @Override
protected void encode(ChannelHandlerContext channelHandlerContext, protected void encode(
LogMsg logMsg, List<Object> out) throws Exception { ChannelHandlerContext channelHandlerContext,
byte[] msg = logMsg.getMsg().getBytes(CharsetUtil.UTF_8); LogMsg logMsg, List<Object> out
//容量的计算两个long型+消息的内容+分割符 )
ByteBuf buf = channelHandlerContext.alloc() throws Exception {
.buffer(8*2 + msg.length + 1); byte[] msg = logMsg.getMsg().getBytes(CharsetUtil.UTF_8);
//将发送时间写入到 ByteBuf中 //容量的计算两个long型+消息的内容+分割符
buf.writeLong(logMsg.getTime()); ByteBuf buf = channelHandlerContext.alloc()
//将消息id写入到 ByteBuf中 .buffer(8 * 2 + msg.length + 1);
buf.writeLong(logMsg.getMsgId()); //将发送时间写入到 ByteBuf中
//添加一个 SEPARATOR buf.writeLong(logMsg.getTime());
buf.writeByte(LogMsg.SEPARATOR); //将消息id写入到 ByteBuf中
//将日志消息写入 ByteBuf中 buf.writeLong(logMsg.getMsgId());
buf.writeBytes(msg); //添加一个 SEPARATOR
//将一个拥有数据和目的地地址的新 DatagramPacket 添加到出站的消息列表中 buf.writeByte(LogMsg.SEPARATOR);
out.add(new DatagramPacket(buf, remoteAddress)); //将日志消息写入 ByteBuf中
} buf.writeBytes(msg);
//将一个拥有数据和目的地地址的新 DatagramPacket 添加到出站的消息列表中
out.add(new DatagramPacket(buf, remoteAddress));
}
} }

View File

@ -14,29 +14,36 @@ import io.netty.channel.SimpleChannelInboundHandler;
* 类说明日志的业务处理类,实际的业务处理接受日志信息 * 类说明日志的业务处理类,实际的业务处理接受日志信息
*/ */
public class LogEventHandler public class LogEventHandler
extends SimpleChannelInboundHandler<LogMsg> { extends
SimpleChannelInboundHandler<LogMsg> {
@Override @Override
public void exceptionCaught(ChannelHandlerContext ctx, public void exceptionCaught(
Throwable cause) throws Exception { ChannelHandlerContext ctx,
//当异常发生时打印栈跟踪信息并关闭对应的 Channel Throwable cause
cause.printStackTrace(); )
ctx.close(); throws Exception {
} //当异常发生时打印栈跟踪信息并关闭对应的 Channel
cause.printStackTrace();
ctx.close();
}
@Override @Override
public void channelRead0(ChannelHandlerContext ctx, public void channelRead0(
LogMsg event) throws Exception { ChannelHandlerContext ctx,
//创建 StringBuilder并且构建输出的字符串 LogMsg event
StringBuilder builder = new StringBuilder(); )
builder.append(event.getTime()); throws Exception {
builder.append(" ["); //创建 StringBuilder并且构建输出的字符串
builder.append(event.getSource().toString()); StringBuilder builder = new StringBuilder();
builder.append("] ["); builder.append(event.getTime());
builder.append(event.getMsgId()); builder.append(" [");
builder.append("] "); builder.append(event.getSource().toString());
builder.append(event.getMsg()); builder.append("] [");
//打印 LogMsg 的数据 builder.append(event.getMsgId());
System.out.println(builder.toString()); builder.append("] ");
} builder.append(event.getMsg());
//打印 LogMsg 的数据
System.out.println(builder.toString());
}
} }

View File

@ -6,58 +6,55 @@ import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.channel.socket.nio.NioDatagramChannel;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
/**
* @Desc: ""
* @Author: caixiang ( Server 接受消息 )
* @DATE: 2024/12/15 10:57
*/
public class LogEventMonitor { public class LogEventMonitor {
private final EventLoopGroup group; private final EventLoopGroup group;
private final Bootstrap bootstrap; private final Bootstrap bootstrap;
public LogEventMonitor(InetSocketAddress address) { public LogEventMonitor(InetSocketAddress address) {
group = new NioEventLoopGroup(); group = new NioEventLoopGroup();
bootstrap = new Bootstrap(); bootstrap = new Bootstrap();
//引导该 NioDatagramChannel //引导该 NioDatagramChannel
bootstrap.group(group) bootstrap.group(group)
.channel(NioDatagramChannel.class) .channel(NioDatagramChannel.class)
//设置套接字选项 SO_BROADCAST //设置套接字选项 SO_BROADCAST
.option(ChannelOption.SO_BROADCAST, true) .option(ChannelOption.SO_BROADCAST, true)
//允许重用 //允许重用
.option(ChannelOption.SO_REUSEADDR,true) .option(ChannelOption.SO_REUSEADDR, true)
.handler( new ChannelInitializer<Channel>() { .handler(new ChannelInitializer<Channel>() {
@Override @Override
protected void initChannel(Channel channel) protected void initChannel(Channel channel)
throws Exception { throws Exception {
ChannelPipeline pipeline = channel.pipeline(); ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(new LogEventDecoder()); pipeline.addLast(new LogEventDecoder());
pipeline.addLast(new LogEventHandler()); pipeline.addLast(new LogEventHandler());
} }
} ) })
.localAddress(address); .localAddress(address);
} }
public Channel bind() { public Channel bind() {
//绑定 Channel注意DatagramChannel 是无连接的 //绑定 Channel注意DatagramChannel 是无连接的
return bootstrap.bind().syncUninterruptibly().channel(); return bootstrap.bind().syncUninterruptibly().channel();
} }
public void stop() { public void stop() {
group.shutdownGracefully(); group.shutdownGracefully();
} }
public static void main(String[] args) throws Exception { public static void main(String[] args)
//构造一个新的 UdpAnswerSide并指明监听端口 throws Exception {
LogEventMonitor monitor = new LogEventMonitor( //构造一个新的 UdpAnswerSide并指明监听端口
new InetSocketAddress(LogConst.MONITOR_SIDE_PORT)); LogEventMonitor monitor = new LogEventMonitor(
try { new InetSocketAddress(LogConst.MONITOR_SIDE_PORT)
//绑定本地监听端口 );
Channel channel = monitor.bind(); try {
System.out.println("UdpAnswerSide running"); //绑定本地监听端口
channel.closeFuture().sync(); Channel channel = monitor.bind();
} finally { System.out.println("UdpAnswerSide running");
monitor.stop(); channel.closeFuture().sync();
}
} }
finally {
monitor.stop();
}
}
} }

View File

@ -13,51 +13,53 @@ import java.net.InetSocketAddress;
* 类说明日志实体类 * 类说明日志实体类
*/ */
public final class LogMsg { public final class LogMsg {
public static final byte SEPARATOR = (byte) ':'; public static final byte SEPARATOR = (byte) ':';
/*源的 InetSocketAddress*/ /* 源的 InetSocketAddress */
private final InetSocketAddress source; private final InetSocketAddress source;
/*消息内容*/ /* 消息内容 */
private final String msg; private final String msg;
/*消息id*/ /* 消息id */
private final long msgId; private final long msgId;
/*消息发送的时间*/ /* 消息发送的时间 */
private final long time; private final long time;
//用于传入消息的构造函数 //用于传入消息的构造函数
public LogMsg(String msg) { public LogMsg(String msg) {
this(null, msg,-1,System.currentTimeMillis()); this(null, msg, -1, System.currentTimeMillis());
} }
//用于传出消息的构造函数 //用于传出消息的构造函数
public LogMsg(InetSocketAddress source, long msgId, public LogMsg(
String msg) { InetSocketAddress source, long msgId,
this(source,msg,msgId,System.currentTimeMillis()); String msg
} ) {
this(source, msg, msgId, System.currentTimeMillis());
}
public LogMsg(InetSocketAddress source, String msg, long msgId, long time) { public LogMsg(InetSocketAddress source, String msg, long msgId, long time) {
this.source = source; this.source = source;
this.msg = msg; this.msg = msg;
this.msgId = msgId; this.msgId = msgId;
this.time = time; this.time = time;
} }
//返回发送 LogMsg 的源的 InetSocketAddress //返回发送 LogMsg 的源的 InetSocketAddress
public InetSocketAddress getSource() { public InetSocketAddress getSource() {
return source; return source;
} }
//返回消息内容 //返回消息内容
public String getMsg() { public String getMsg() {
return msg; return msg;
} }
//返回消息id //返回消息id
public long getMsgId() { public long getMsgId() {
return msgId; return msgId;
} }
//返回消息中的时间 //返回消息中的时间
public long getTime() { public long getTime() {
return time; return time;
} }
} }

View File

@ -3,7 +3,6 @@ package org.opentcs.kc.udp;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
//import org.opentcs.kc.udp.agv.param.AgvEvent;
import org.opentcs.kc.common.byteutils.ByteUtils; import org.opentcs.kc.common.byteutils.ByteUtils;
import org.opentcs.kc.udp.agv.param.AgvEvent; import org.opentcs.kc.udp.agv.param.AgvEvent;
import org.opentcs.kc.udp.agv.param.AgvEventConstant; import org.opentcs.kc.udp.agv.param.AgvEventConstant;
@ -22,45 +21,44 @@ import org.opentcs.kc.udp.agv.param.function.x14.RobotSetPosition;
import org.opentcs.kc.udp.agv.param.function.x17.QueryRobotRunStatusRsp; import org.opentcs.kc.udp.agv.param.function.x17.QueryRobotRunStatusRsp;
import org.opentcs.kc.udp.agv.param.rsp.RcvEventPackage; import org.opentcs.kc.udp.agv.param.rsp.RcvEventPackage;
import org.opentcs.kc.udp.io.UDPClient; import org.opentcs.kc.udp.io.UDPClient;
import org.opentcs.kc.udp.agv.param.AgvEvent;
/** /**
*
* AGV启动 * AGV启动
* 0xAF(查询机器人状态) 👌 有response * 0xAF(查询机器人状态) 👌
* 0xB1(下发订阅信令) 👌 * 0xB1(下发订阅信令) 👌
* 初始化 * 初始化
* 0x03(切换手自动) 👌 * 0x03(切换手自动) 👌
* 0x14(手动定位) 👌 * 0x14(手动定位) 👌
* 0x17(查询机器人运行状态) 👌 有response * 0x17(查询机器人运行状态) 👌
* 0x1F(确认初始位置) 👌 * 0x1F(确认初始位置) 👌
* 运行 * 运行
* 0xAE(导航控制导航点控制) 👌 * 0xAE(导航控制导航点控制) 👌
*
*
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/1/17 16:25
*/ */
public class KCCommandDemo { public class KCCommandDemo {
public static void main(String [] args) throws Exception{ public static void main(String[] args)
{ throws Exception {
//0xAF(查询机器人状态) // {
AgvEvent agvEvent = queryStatus(); // //0xAF(查询机器人状态)
printInfo(agvEvent); // AgvEvent agvEvent = queryStatus();
RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent); // printInfo(agvEvent);
if(rcv.isOk()){ // RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
QueryRobotStatusRsp queryRobotStatusRsp = new QueryRobotStatusRsp(rcv.getDataBytes()); // if(rcv.isOk()){
System.out.println(); // System.out.println();
System.out.println("received transationId : "+ "isok:"+rcv.isOk()); // System.out.println("received transationId : "+ "isok:"+rcv.isOk());
for (byte b:rcv.getValue()){ // for (byte b:rcv.getValue()){
System.out.print(byteToHex(b)+" "); // System.out.print(byteToHex(b)+" ");
} // }
}else { // System.out.println();
System.out.println(); // System.out.println("---------------------");
System.out.println("received transationId : "+ "isok:"+rcv.isOk()); // for (byte c:rcv.getDataBytes()){
} // System.out.print(byteToHex(c)+" ");
} // }
// QueryRobotStatusRsp queryRobotStatusRsp = new QueryRobotStatusRsp(rcv.getDataBytes());
// }else {
// System.out.println();
// System.out.println("received transationId : "+ "isok:"+rcv.isOk());
// }
// }
// { // {
// //0xB0(查询载货状态) // //0xB0(查询载货状态)
@ -89,12 +87,16 @@ public class KCCommandDemo {
// if(rcv.isOk()){ // if(rcv.isOk()){
// System.out.println(); // System.out.println();
// System.out.println("received transationId : "+ "isok:"+rcv.isOk()); // System.out.println("received transationId : "+ "isok:"+rcv.isOk());
//// SubscribeRsp subscribeRsp = new SubscribeRsp(rcv.getDataBytes()); // for (byte b:rcv.getDataBytes()) {
//// if(subscribeRsp.isOk()){ // System.out.print(byteToHex(b)+" ");
//// //... // }
//// }else { //
//// //... // SubscribeRsp subscribeRsp = new SubscribeRsp(rcv.getDataBytes());
//// } // if(subscribeRsp.isOk()){
// //...
// }else {
// //...
// }
// }else { // }else {
// System.out.println(); // System.out.println();
// System.out.println("received transationId : "+ "isok:"+rcv.isOk()); // System.out.println("received transationId : "+ "isok:"+rcv.isOk());
@ -145,26 +147,26 @@ public class KCCommandDemo {
// } // }
// { {
// //0x14(手动定位) //0x14(手动定位)
// AgvEvent agvEvent = manualLocation(); AgvEvent agvEvent = manualLocation();
// printInfo(agvEvent); printInfo(agvEvent);
// RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent); RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
// if(rcv.isOk()){ if(rcv.isOk()){
// System.out.println(); System.out.println();
// System.out.println("received "+ "isok:"+rcv.isOk()+" dataBytes:"); System.out.println("received "+ "isok:"+rcv.isOk()+" dataBytes:");
// printInfo(rcv); printInfo(rcv);
// if(rcv.isOk()){ if(rcv.isOk()){
// //get and parse value //get and parse value
// System.out.println("0x14 ok"); System.out.println("0x14 ok");
// }else { }else {
// System.out.println("0x14 failed"); System.out.println("0x14 failed");
// } }
// }else { }else {
// System.out.println(); System.out.println();
// System.out.println("received transationId : "+ "isok:"+rcv.isOk()); System.out.println("received transationId : "+ "isok:"+rcv.isOk());
// } }
// } }
// { // {
@ -173,33 +175,40 @@ public class KCCommandDemo {
// printInfo(agvEvent); // printInfo(agvEvent);
// RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent); // RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
// if(rcv.isOk()){ // if(rcv.isOk()){
// System.out.println();
// System.out.println("received transationId : "+ "isok:"+rcv.isOk());
// for (byte b:rcv.getValue()){
// System.out.print(byteToHex(b)+" ");
// }
// System.out.println();
// System.out.println("print databytes:-----=======");
// for (byte c:rcv.getDataBytes()){
// System.out.print(byteToHex(c)+" ");
// }
// //QueryCargoStatusRsp queryCargoStatusRsp = new QueryCargoStatusRsp(rcv.getDataBytes()); // //QueryCargoStatusRsp queryCargoStatusRsp = new QueryCargoStatusRsp(rcv.getDataBytes());
// QueryRobotRunStatusRsp queryRobotRunStatusRsp = new QueryRobotRunStatusRsp(rcv.getDataBytes()); // QueryRobotRunStatusRsp queryRobotRunStatusRsp = new QueryRobotRunStatusRsp(rcv.getDataBytes());
// System.out.println(queryRobotRunStatusRsp.toString()); // 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 { // }else {
// System.out.println(); // System.out.println();
// System.out.println("received transationId : "+ "isok:"+rcv.isOk()); // System.out.println("received transationId : "+ "isok:"+rcv.isOk());
// } // }
// } // }
// { // {
// //0x1F(确认初始位置) // //0x1F(确认初始位置)
// AgvEvent agvEvent = confirmInitialPosition(); // AgvEvent agvEvent = confirmInitialPosition();
// printInfo(agvEvent); // printInfo(agvEvent);
// RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent); // RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
// if(rcv.isOk()){ // if (rcv.isOk()) {
// System.out.println("0x1F ok"); // System.out.println("0x1F ok");
// }else { // }
// System.out.println(); // else {
// System.out.println("0x1F fail"); // System.out.println();
// System.out.println("received transationId : "+ "isok:"+rcv.isOk()); // System.out.println("0x1F fail");
// } // System.out.println("received transationId : " + "isok:" + rcv.isOk());
// } // }
// }
// { // {
// //0xAE(导航控制导航点控制) // //0xAE(导航控制导航点控制)
@ -215,196 +224,235 @@ public class KCCommandDemo {
// } // }
// } // }
}
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) {
public static String byteToHex(byte b) { System.out.println("sended transationId : " + agvEvent.getTransationIdString());
// 将byte转换为无符号整数 for (byte b : agvEvent.toBytes().getBody()) {
int unsignedByte = b & 0xFF; System.out.print(byteToHex(b) + " ");
// 使用Integer.toHexString方法转换为十六进制字符串
String hexString = Integer.toHexString(unsignedByte);
// 如果字符串长度为1需要在前面补0
if (hexString.length() == 1) {
return "0" + hexString;
}
return hexString;
} }
}
public static void printInfo(AgvEvent agvEvent){ public static void printInfo(RcvEventPackage rcv) {
System.out.println("sended transationId : "+agvEvent.getTransationIdString());
for (byte b:agvEvent.toBytes().getBody()){ for (byte b : rcv.getDataBytes()) {
System.out.print(byteToHex(b)+" "); System.out.print(byteToHex(b) + " ");
}
} }
}
public static void printInfo(RcvEventPackage rcv){ /**
* 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);
for (byte b: rcv.getDataBytes()){ List<ReadStrValue> readStrValueList = new ArrayList<>();
System.out.print(byteToHex(b)+" "); ReadStrValue readStrValue = new ReadStrValue("Battry_SOC", 1, readValueMemberList);
} readStrValueList.add(readStrValue);
}
/** ReadParam readParam = new ReadParam(agvEvent.getTransationId(), readStrValueList);
* decs: read操作 agvEvent.setBody(readParam.toBytes());
* 指令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<>(); return agvEvent;
ReadStrValue readStrValue = new ReadStrValue("Battry_SOC", 1, readValueMemberList); }
readStrValueList.add(readStrValue);
ReadParam readParam = new ReadParam(agvEvent.getTransationId(),readStrValueList ); /**
agvEvent.setBody(readParam.toBytes()); * decs: write操作
* 指令0x03
* author: caixiang
* date: 2025/1/17 16:25
*/
public static AgvEvent writeValue() {
AgvEvent agvEvent = new AgvEvent(AgvEventConstant.CommandCode_WRITE);
return agvEvent; 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<>();
* decs: write操作 WriteStrValue strValue = new WriteStrValue("TestRW", 1, valueMemberList);
* 指令0x03 strValueList.add(strValue);
* author: caixiang
* date: 2025/1/17 16:25
* */
public static AgvEvent writeValue() {
AgvEvent agvEvent = new AgvEvent(AgvEventConstant.CommandCode_WRITE);
List<WriteValueMember> valueMemberList = new ArrayList<>(); WriteParam param = new WriteParam(1, strValueList);
WriteValueMember valueMember1 = new WriteValueMember(Short.valueOf("0"),Short.valueOf("4"), ByteUtils.uintToBytes(3, ByteOrder.LITTLE_ENDIAN)); agvEvent.setBody(param.toBytes());
valueMemberList.add(valueMember1);
List<WriteStrValue> strValueList = new ArrayList<>(); return agvEvent;
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: 查询机器人状态 * decs: 查询载货状态
* 指令0xAF * 指令0xB0
* author: caixiang * author: caixiang
* date: 2025/1/17 16:25 * date: 2025/1/17 16:25
* */ */
public static AgvEvent queryStatus() { public static AgvEvent checkCargoStatus() {
AgvEvent agvEvent = new AgvEvent(AgvEventConstant.CommandCode_QUERY_ROBOT_STATUS); AgvEvent agvEvent = new AgvEvent(AgvEventConstant.CommandCode_QUERY_CARRY_STATUS);
return agvEvent; return agvEvent;
} }
/** /**
* decs: 导航控制 * decs: 下发订阅信息
* 指令0xAE * 指令0xB1
* author: caixiang * author: caixiang
* date: 2025/1/17 16:25 * date: 2025/1/17 16:25
* */ */
public static AgvEvent navigationControl() { public static AgvEvent issueSubscribe() {
AgvEvent agvEvent = new AgvEvent(AgvEventConstant.CommandCode_QUERY_ROBOT_STATUS); List<SubscribeInfo> subscribeInfoList = new ArrayList<>();
//TODO 构建 SubscribeInfo subscribeInfo = new SubscribeInfo(
Integer orderId = 1; new byte[]{(byte) 0xaf, (byte) 0x00}, (short) 100, 1000
);
//构建point //SubscribeInfo subscribeInfo = new SubscribeInfo(new byte[]{(byte)0xb0,(byte)0x00}, (short) 100,1000);
Action[] pointActions1 = new Action[]{ subscribeInfoList.add(subscribeInfo);
new Action(ActionSet.stop0x01, (byte) 0x00, 1, ActionSet.stop0x01_paramsize, ActionSet.stop0x01(orderId, (byte) 0x01)) SubscribeParam subscribeParam = new SubscribeParam(subscribeInfoList);
//,new Action()... 每一个point 可以绑定一个或者多个 action AgvEvent agvEvent = new AgvEvent(
}; AgvEventConstant.CommandCode_ISSUE_SUBSCRIPTION, subscribeParam.toBytes()
Action[] pointActions2 = new Action[]{ );
new Action(ActionSet.stop0x01, (byte) 0x00, 1, ActionSet.stop0x01_paramsize, ActionSet.stop0x01(orderId, (byte) 0x01)) return agvEvent;
}; }
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;
}
} }

View File

@ -0,0 +1,34 @@
package org.opentcs.kc.udp.Service;
import org.opentcs.kc.udp.agv.param.AgvEvent;
import org.opentcs.kc.udp.agv.param.rsp.RcvEventPackage;
public class BaseCommand {
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) + " ");
}
}
}

View File

@ -0,0 +1,37 @@
package org.opentcs.kc.udp.Service;
import org.opentcs.kc.udp.agv.param.AgvEvent;
import org.opentcs.kc.udp.agv.param.AgvEventConstant;
import org.opentcs.kc.udp.agv.param.rsp.RcvEventPackage;
import org.opentcs.kc.udp.io.UDPClient;
public class ConfirmRelocation
extends
BaseCommand {
/**
* 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;
}
public static void commnd() {
//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());
}
}
}

View File

@ -0,0 +1,183 @@
package org.opentcs.kc.udp.Service;
import java.util.HashMap;
import org.opentcs.drivers.vehicle.MovementCommand;
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.navigation.Action;
import org.opentcs.kc.udp.agv.param.function.navigation.ActionSet;
import org.opentcs.kc.udp.agv.param.function.navigation.NavigationParam;
import org.opentcs.kc.udp.agv.param.function.navigation.Path;
import org.opentcs.kc.udp.agv.param.function.navigation.Point;
import org.opentcs.kc.udp.agv.param.rsp.RcvEventPackage;
import org.opentcs.kc.udp.io.UDPClient;
public class HybridNavigation
extends
BaseCommand {
/**
* 测试模式true=开启测试false=关闭测试
* 测试模式因orderName为String类型数据无法转换为Integer
*/
private static boolean TEST_MODEL = false;
/**
* 订单ID
* 订单的唯一标识用于标识多个任务KEY是否属于同一个订单机器人单次只能接收同一订单ID的任务直至订单结束调度可下发新的订单ID订单ID从1开始累加每次+1
* 同一订单ID支持不停车更新导航
*/
private static Integer orderID = 0;
/**
* 任务ID
* 任务的唯一标识与订单ID绑定从1开始当同一订单下发新的资源时加1订单ID发生改变任务KEY需要重新计数
*/
private static Integer taskKey;
/**
* 序列号
* 用于定位点在整个任务中的位置目的是区分同一个点ID是否在一个任务中出现多次从0开始偶数递增例如0->2->4->6
*/
private static Integer pointSerialNum = 0;
/**
* 用于定位段在整个任务中的位置目的是区分同一个段ID是否在一个任务中出现多次从1开始奇数递增例如1->3->5->7
*/
private static Integer pathSerialNum = 1;
/**
* 订单名映射int类型数据.
*/
private static HashMap<String, Integer> orderNameMap = new HashMap<>();
/**
* 订单映射最大订单ID.
*/
private static int currentMaxiOrderId = 0;
/**
* decs: 导航控制
* 指令0xAE
* author: caixiang
* date: 2025/1/17 16:25
*/
public static AgvEvent navigationControl(
Integer sourcePoint, Integer destinationPoint, Integer pathID, String operation
) {
AgvEvent agvEvent = new AgvEvent(AgvEventConstant.CommandCode_MIXED_ISSUANCE_TASK);
//TODO 构建
Integer oldPointSerialNum = pointSerialNum;
pointSerialNum += 2;
Point[] points = new Point[]{
new Point(
oldPointSerialNum, sourcePoint, 0f, (byte) 0x00, ByteUtils.usintTo1Byte(0), null
),
new Point(
pointSerialNum, destinationPoint, 0f, (byte) 0x00, ByteUtils.usintTo1Byte(0), null
)
};
Path[] paths = new Path[]{
new Path(
pathSerialNum, pathID, 0f, (byte) 0x00, (byte) 0x01, ByteUtils.usintTo1Byte(0), 1f, 1f, null
),
};
pathSerialNum += 2;
NavigationParam navigationParam = new NavigationParam(
orderID, taskKey, ByteUtils.usintTo1Byte(points.length), ByteUtils.usintTo1Byte(
points.length - 1
), points, paths
);
taskKey++;
agvEvent.setBody(navigationParam.toBytes());
return agvEvent;
}
/**
*
* @param orderName 订单ID
* @param sourcePoint 下发起点
* @param destinationPoint 下发终点
* @param operation 执行操作
*/
public static void command(
String orderName, String sourcePoint, String destinationPoint, String operation
) {
// if (TEST_MODEL) {
// return;
// }
Integer newOrderName = getUniqueOrderID(orderName);
Integer newSourcePoint = Integer.parseInt(sourcePoint);
Integer newDestinationPoint = Integer.parseInt(destinationPoint);
//拼接起点终点字符串转为Integer类型获取唯一pathID
Integer pathID;
if (newSourcePoint <= newDestinationPoint) {
pathID = Integer.parseInt(sourcePoint + destinationPoint);
} else {
pathID = Integer.parseInt(destinationPoint + sourcePoint);
}
System.out.println("pathID:" + pathID);
if (!orderID.equals(newOrderName)) {
//切换订单重置参数
initParams(newOrderName);
}
//0xAE(导航控制导航点控制)
AgvEvent agvEvent = navigationControl(newSourcePoint, newDestinationPoint, pathID, operation);
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());
}
}
/**
* 初始化参数
*
* @param newOrderName 新的订单ID
*/
private static void initParams(Integer newOrderName) {
orderID = newOrderName;
taskKey = 1;
pointSerialNum = 0;
pathSerialNum = 1;
}
/**
* 维护订单名对应int类型唯一ID
* @param orderName 订单名
* @return Integer
*/
private static Integer getUniqueOrderID(String orderName){
Integer orderId;
if (orderNameMap.containsKey(orderName)) {
//订单名已存在
orderId = orderNameMap.get(orderName);
} else {
//订单名不存在创建对应映射
currentMaxiOrderId = currentMaxiOrderId + 1;
orderId = currentMaxiOrderId;
orderNameMap.put(orderName, orderId);
}
return orderId;
}
/**
* 任务结束删除唯一orderID.
* @param command 订单
*/
public static void delUniqueOrderID(MovementCommand command){
String orderName = command.getTransportOrder().getName();
orderNameMap.remove(orderName);
}
}

View File

@ -0,0 +1,45 @@
package org.opentcs.kc.udp.Service;
import org.opentcs.kc.udp.agv.param.AgvEvent;
import org.opentcs.kc.udp.agv.param.AgvEventConstant;
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;
public class ManualPosition
extends
BaseCommand {
public static AgvEvent manualLocation(double agvX, double agvY, double agvAngle) {
AgvEvent agvEvent = new AgvEvent(AgvEventConstant.CommandCode_ROBOT_SET_POSITION);
RobotSetPosition robotSetPosition = new RobotSetPosition(agvX, agvY, agvAngle);
byte[] bytes = robotSetPosition.toBytes();
agvEvent.setBody(bytes);
return agvEvent;
}
public static void command(double agvX, double agvY, double agvAngle) {
//0x14(手动定位)
AgvEvent agvEvent = manualLocation(agvX, agvY, agvAngle);
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());
throw new RuntimeException("0x14 failed");
}
}
}

View File

@ -0,0 +1,48 @@
package org.opentcs.kc.udp.Service;
import org.opentcs.kc.udp.agv.param.AgvEvent;
import org.opentcs.kc.udp.agv.param.AgvEventConstant;
import org.opentcs.kc.udp.agv.param.function.x17.QueryRobotRunStatusRsp;
import org.opentcs.kc.udp.agv.param.rsp.RcvEventPackage;
import org.opentcs.kc.udp.io.UDPClient;
public class QryRobotRunStatus
extends
BaseCommand {
/**
* 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 QueryRobotRunStatusRsp command() {
//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) + " ");
}
return queryRobotRunStatusRsp;
}
else {
System.out.println();
System.out.println("received transationId : " + "isok:" + rcv.isOk());
throw new RuntimeException("0x17 fail");
}
}
}

View File

@ -0,0 +1,51 @@
package org.opentcs.kc.udp.Service;
import org.opentcs.kc.udp.agv.param.AgvEvent;
import org.opentcs.kc.udp.agv.param.AgvEventConstant;
import org.opentcs.kc.udp.agv.param.function.af.QueryRobotStatusRsp;
import org.opentcs.kc.udp.agv.param.rsp.RcvEventPackage;
import org.opentcs.kc.udp.io.UDPClient;
public class QryRobotStatus
extends
BaseCommand {
/**
* 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;
}
public static QueryRobotStatusRsp command() {
//0xAF(查询机器人状态)
AgvEvent agvEvent = queryStatus();
printInfo(agvEvent);
RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
if(rcv.isOk()){
System.out.println();
for (byte b:rcv.getValue()){
System.out.print(byteToHex(b)+" ");
}
System.out.println();
System.out.println("0xAF received transationId : "+ "isok:"+rcv.isOk());
// for (byte c:rcv.getDataBytes()){
// System.out.print(byteToHex(c)+" ");
// }
QueryRobotStatusRsp queryRobotStatusRsp = new QueryRobotStatusRsp(rcv.getDataBytes());
return queryRobotStatusRsp;
}
else {
System.out.println();
System.out.println("received transationId : " + "isok:" + rcv.isOk());
throw new RuntimeException("0xAF fail");
}
}
}

View File

@ -0,0 +1,78 @@
package org.opentcs.kc.udp.Service;
import static java.util.Objects.requireNonNull;
import java.util.ArrayList;
import java.util.List;
import org.opentcs.kc.udp.agv.param.AgvEvent;
import org.opentcs.kc.udp.agv.param.AgvEventConstant;
import org.opentcs.kc.udp.agv.param.function.b0.QueryCargoStatusRsp;
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.b1.SubscribeRsp;
import org.opentcs.kc.udp.agv.param.rsp.RcvEventPackage;
import org.opentcs.kc.udp.io.UDPClient;
public class SubCargoStatus
extends
BaseCommand {
public static final int intervalTime = 1000;
/**
* 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) intervalTime, 10000
);
subscribeInfoList.add(subscribeInfo);
SubscribeParam subscribeParam = new SubscribeParam(subscribeInfoList);
AgvEvent agvEvent = new AgvEvent(
AgvEventConstant.CommandCode_ISSUE_SUBSCRIPTION, subscribeParam.toBytes()
);
return agvEvent;
}
public static void command() {
// 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());
}
}
//订阅执行操作
public void subscribe0xB0Operate(String name, QueryCargoStatusRsp queryCargoStatusRsp) {
// Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, name);
//修改载货状态
if (queryCargoStatusRsp.isCargo == 0) {
//未载货
}
else if (queryCargoStatusRsp.isCargo == 1) {
//载货
}
}
}

View File

@ -0,0 +1,61 @@
package org.opentcs.kc.udp.Service;
import java.util.ArrayList;
import java.util.List;
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.b1.SubscribeRsp;
import org.opentcs.kc.udp.agv.param.rsp.RcvEventPackage;
import org.opentcs.kc.udp.io.UDPClient;
public class SubRobotStatue
extends
BaseCommand {
public static final int intervalTime = 1000;
/**
* 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) intervalTime, 10000
);
// 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;
}
public static void command() {
// 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());
}
}
}

View File

@ -0,0 +1,66 @@
package org.opentcs.kc.udp.Service;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
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.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.rsp.RcvEventPackage;
import org.opentcs.kc.udp.io.UDPClient;
public class SwitchAutomaticMode
extends
BaseCommand {
/**
* 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("1"), 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;
}
public static void command() {
//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());
}
}
}

View File

@ -0,0 +1,66 @@
package org.opentcs.kc.udp.Service;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
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.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.rsp.RcvEventPackage;
import org.opentcs.kc.udp.io.UDPClient;
public class SwitchManualMode
extends
BaseCommand {
/**
* 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;
}
public static void command() {
//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());
}
}
}

View File

@ -7,20 +7,23 @@ import org.opentcs.kc.udp.io.UDPClient;
/** /**
* 作者蔡翔 * 作者蔡翔
*/ */
public class AgvUdpChannelInitializer extends ChannelInitializer<NioDatagramChannel> { public class AgvUdpChannelInitializer
private UDPClient client; extends
ChannelInitializer<NioDatagramChannel> {
private UDPClient client;
public AgvUdpChannelInitializer(UDPClient client) { public AgvUdpChannelInitializer(UDPClient client) {
System.out.println(client.getHost()); System.out.println(client.getHost());
this.client = client; this.client = client;
} }
@Override @Override
protected void initChannel(NioDatagramChannel channel) throws Exception { protected void initChannel(NioDatagramChannel channel)
// Modbus throws Exception {
// 在管道中添加我们自己的接收数据实现方法 // Modbus
//todo 到时候这里改成 FixedLengthFrameDecoder 资料https://www.cnblogs.com/java-chen-hao/p/11571229.html // 在管道中添加我们自己的接收数据实现方法
//todo 到时候这里改成 FixedLengthFrameDecoder 资料https://www.cnblogs.com/java-chen-hao/p/11571229.html
// channel.pipeline().addLast( // channel.pipeline().addLast(
// new LengthFieldBasedFrameDecoder( // new LengthFieldBasedFrameDecoder(
// 200, // 200,
@ -30,7 +33,7 @@ public class AgvUdpChannelInitializer extends ChannelInitializer<NioDatagramChan
// 7, // 7,
// true) // true)
// ); // );
channel.pipeline().addLast(new AgvUdpDecode(this.client)); //InBoundHandler channel.pipeline().addLast(new AgvUdpDecode(this.client)); //InBoundHandler
//channel.pipeline().addLast(new AgvUdpEncode()); //OutBoundHandler //channel.pipeline().addLast(new AgvUdpEncode()); //OutBoundHandler
} }
} }

View File

@ -4,79 +4,84 @@ import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket; import io.netty.channel.socket.DatagramPacket;
import java.net.InetSocketAddress;
import org.opentcs.kc.common.Package; import org.opentcs.kc.common.Package;
import org.opentcs.kc.syn.SendedList; import org.opentcs.kc.syn.SendedList;
import org.opentcs.kc.udp.agv.param.rsp.RcvEventPackage; import org.opentcs.kc.udp.agv.param.rsp.RcvEventPackage;
import org.opentcs.kc.udp.io.UDPClient; import org.opentcs.kc.udp.io.UDPClient;
/** public class AgvUdpDecode
* @Desc: "" extends
* @Author: caixiang SimpleChannelInboundHandler<DatagramPacket> {
* @DATE: 2024-06-06 10:45
*/
public class AgvUdpDecode extends SimpleChannelInboundHandler<DatagramPacket> {
private UDPClient client; private UDPClient client;
//授权码16字节 + 报文头8个字节 + 报文数据长度2个字节 + 保留2字节 = 28字节 //授权码16字节 + 报文头8个字节 + 报文数据长度2个字节 + 保留2字节 = 28字节
private static final int HEADER_SIZE = 28; private static final int HEADER_SIZE = 28;
public AgvUdpDecode(UDPClient client) { public AgvUdpDecode(UDPClient client) {
this.client = 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");
} }
@Override int dataLength = in.getShortLE(24);
protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) if (dataLength < 0) {
throws Exception { throw new Exception(
//获得应答DatagramPacket提供了content()方法取得报文的实际内容 "bodyLength [" + dataLength + "] is not right, remote:" + ctx.channel().remoteAddress()
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 int neededLength = HEADER_SIZE + dataLength;
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) int isDataEnough = in.readableBytes() - neededLength;
throws Exception {
cause.printStackTrace();
ctx.close(); //收到的数据是否足够组包
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) {
//获取响应IP
InetSocketAddress sender = msg.sender();
String hostAddress = sender.getAddress().getHostAddress();
if (commandCode == (byte) 0xAF || commandCode == (byte) 0xB0) {
client.subscribeKC(new RcvEventPackage(body[22], body), 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();
}
} }

View File

@ -5,89 +5,90 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import org.opentcs.kc.common.Package; 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; // import org.opentcs.kc.common.byteutils.ByteUtil;
//import org.opentcs.kc.common.byteutils.ByteUtil; // import org.opentcs.kc.common.byteutils.ByteUtil;
/** public class AgvEvent
* @Desc: "" extends
* @Author: caixiang AgvEventHeader
* @DATE: 2024/12/27 15:59 implements
*/ IAgvEvent,
public class AgvEvent extends AgvEventHeader implements IAgvEvent, Serializable { Serializable {
private byte[] bodyLength; private byte[] bodyLength;
private byte[] retain; private byte[] retain;
private byte[] body; private byte[] body;
//for Request //for Request
//有content 没有content 传new byte[] //有content 没有content 传new byte[]
public AgvEvent(Byte commandCode) { public AgvEvent(Byte commandCode) {
super(commandCode); super(commandCode);
//初始化 //初始化
bodyLength = new byte[]{0x00,0x00}; bodyLength = new byte[]{0x00, 0x00};
retain = new byte[]{0x00,0x00}; retain = new byte[]{0x00, 0x00};
body = new byte[]{}; 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) { public AgvEvent(Byte commandCode, byte[] body) {
this.bodyLength = ByteUtil.shortToBytes((short) body.length); super(commandCode);
this.body = body; //初始化
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;
} }
}
//for Response public void setBody(byte[] body) {
public AgvEvent(Byte serviceCode, Byte commandCode, Byte executionCode) { this.bodyLength = ByteUtil.shortToBytes((short) body.length);
super(serviceCode, commandCode, executionCode); this.body = body;
} }
@Override //for Response
public Package toBytes() { public AgvEvent(Byte serviceCode, Byte commandCode, Byte executionCode) {
List<Byte> headerBytes = getHeaderBytes(); super(serviceCode, commandCode, executionCode);
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() { @Override
return transationId[0]+"-"+transationId[1]; 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());
}
@Override public String getTransationIdString() {
public Package toBytes(Object newValue) { return transationId[0] + "-" + transationId[1];
return null; }
}
@Override @Override
public String toString() { public Package toBytes(Object newValue) {
return "AgvEvent{" + return null;
"Header=" + headerToString() + }
", bodyLength=" + Arrays.toString(bodyLength) +
", body=" + Arrays.toString(body) +
'}';
}
@Override @Override
public String protocolName() { public String toString() {
return ""; return "AgvEvent{" +
} "Header=" + headerToString() +
", bodyLength=" + Arrays.toString(bodyLength) +
", body=" + Arrays.toString(body) +
'}';
}
@Override
public String protocolName() {
return "";
}
} }

View File

@ -1,38 +1,35 @@
package org.opentcs.kc.udp.agv.param; package org.opentcs.kc.udp.agv.param;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2024/12/27 10:19
*/
public class AgvEventConstant { 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[] 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 VersionNum = 0x01;
public static final byte MSGTypeRequest = 0x00; public static final byte MSGTypeRequest = 0x00;
public static final byte MSGTypeResponse = 0x01; public static final byte MSGTypeResponse = 0x01;
//命令码 开始 //命令码 开始
public static final byte ServiceCode = 0x10; public static final byte ServiceCode = 0x10;
public static final byte CommandCode_READ = 0x02; public static final byte CommandCode_READ = 0x02;
public static final byte CommandCode_WRITE = 0x03; public static final byte CommandCode_WRITE = 0x03;
public static final byte CommandCode_MIXED_ISSUANCE_TASK = (byte) 0xAE; 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_ROBOT_STATUS = (byte) 0xAF;
public static final byte CommandCode_QUERY_CARRY_STATUS = (byte) 0xB0; public static final byte CommandCode_QUERY_CARRY_STATUS = (byte) 0xB0;
public static final byte CommandCode_ISSUE_SUBSCRIPTION = (byte) 0xB1; public static final byte CommandCode_ISSUE_SUBSCRIPTION = (byte) 0xB1;
public static final byte CommandCode_ACT_IMMEDIATELY = (byte) 0xB2; public static final byte CommandCode_ACT_IMMEDIATELY = (byte) 0xB2;
public static final byte CommandCode_SET_ABILITY= (byte) 0xB7; public static final byte CommandCode_SET_ABILITY = (byte) 0xB7;
public static final byte CommandCode_ROBOT_SET_POSITION= (byte) 0x14; 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_GET_ROBOT_POSITION = (byte) 0x15;
public static final byte CommandCode_NAVIGATION_CONTROL= (byte) 0x16; 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_NAVIGATION_STATUS = (byte) 0x1D;
public static final byte CommandCode_QUERY_ROBOT_RUN_STATUS= (byte) 0x17; public static final byte CommandCode_QUERY_ROBOT_RUN_STATUS = (byte) 0x17;
public static final byte CommandCode_CONFIRM_ROBOT_POSITION= (byte) 0x1F; public static final byte CommandCode_CONFIRM_ROBOT_POSITION = (byte) 0x1F;
//命令码 结束 //命令码 结束
} }

View File

@ -6,107 +6,102 @@ import java.util.List;
import org.opentcs.kc.common.CaffeineUtil; import org.opentcs.kc.common.CaffeineUtil;
import org.opentcs.kc.common.byteutils.ByteUtils; import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2024-06-07 13:29
*/
public class AgvEventHeader { public class AgvEventHeader {
//常量区域 开始 //常量区域 开始
//授权码 //授权码
public byte[] authorizationCode; public byte[] authorizationCode;
//header 开始 //header 开始
//版本号 //版本号
public byte versionNum; public byte versionNum;
//报文类型 0x00请求报文 ; 0x01应答报文 //报文类型 0x00请求报文 ; 0x01应答报文
public byte msgType; public byte msgType;
//request response transationId一致,2个字节 //request response transationId一致,2个字节
public byte[] transationId; public byte[] transationId;
//服务码 //服务码
public byte serviceCode; public byte serviceCode;
//命令码用于区分不同命令request response 相同 //命令码用于区分不同命令request response 相同
public byte commandCode; public byte commandCode;
//执行码应答报文填写表明命令执行情况请求数据包置 0 //执行码应答报文填写表明命令执行情况请求数据包置 0
public byte executionCode; public byte executionCode;
//保留 //保留
public byte retain; public byte retain;
//header 结束 //header 结束
//常量区域 结束 //常量区域 结束
//request //request
public AgvEventHeader(Byte commandCode) { public AgvEventHeader(Byte commandCode) {
this.authorizationCode = AgvEventConstant.AuthorizationCode; this.authorizationCode = AgvEventConstant.AuthorizationCode;
this.versionNum = AgvEventConstant.VersionNum; this.versionNum = AgvEventConstant.VersionNum;
this.msgType = AgvEventConstant.MSGTypeRequest; this.msgType = AgvEventConstant.MSGTypeRequest;
this.transationId = CaffeineUtil.getUUIDAGV(); this.transationId = CaffeineUtil.getUUIDAGV();
this.serviceCode = 0x10; this.serviceCode = 0x10;
this.commandCode = commandCode; this.commandCode = commandCode;
this.executionCode = 0x00; this.executionCode = 0x00;
this.retain = 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 协议版本号
public AgvEventHeader(byte[] src){ bytes.add(versionNum);
this.authorizationCode = ByteUtils.copyOfRange(src,0,16); //add 报文类型
this.versionNum = src[16]; bytes.add(msgType);
this.msgType = src[17]; //add 报文标识
this.transationId = ByteUtils.copyOfRange(src,18,20); for (byte b : transationId) {
this.serviceCode = src[20]; bytes.add(b);
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;
} }
bytes.add(serviceCode);
bytes.add(commandCode);
bytes.add(executionCode);
bytes.add(retain);
return bytes;
}
} }

View File

@ -1,12 +1,14 @@
package org.opentcs.kc.udp.agv.param; package org.opentcs.kc.udp.agv.param;
import org.opentcs.kc.common.byteutils.ByteUtil;
import org.opentcs.kc.common.Package; import org.opentcs.kc.common.Package;
public interface IAgvEvent { import org.opentcs.kc.common.byteutils.ByteUtil;
//read sended
public Package toBytes();
//write sended
public Package toBytes(Object newValue);
public String protocolName(); public interface IAgvEvent {
//read sended
public Package toBytes();
//write sended
public Package toBytes(Object newValue);
public String protocolName();
} }

View File

@ -2,24 +2,19 @@ package org.opentcs.kc.udp.agv.param.function.af;
import org.opentcs.kc.common.byteutils.ByteUtils; import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/1/21 10:03
*/
public class AbnormalEventStatusInfo { public class AbnormalEventStatusInfo {
private byte[] src; private byte[] src;
//事件码,2个字节 //事件码,2个字节
public byte[] eventCode; public byte[] eventCode;
//异常等级,2个字节 //异常等级,2个字节
public byte[] abnormalLevel; public byte[] abnormalLevel;
//remain,8个字节 //remain,8个字节
public byte[] remain; public byte[] remain;
public AbnormalEventStatusInfo(byte[] src) { public AbnormalEventStatusInfo(byte[] src) {
this.src = src; this.src = src;
this.eventCode = ByteUtils.copyBytes(src, 0, 2); this.eventCode = ByteUtils.copyBytes(src, 0, 2);
this.abnormalLevel = ByteUtils.copyBytes(src, 2, 2); this.abnormalLevel = ByteUtils.copyBytes(src, 2, 2);
this.remain = ByteUtils.copyBytes(src, 4, 8); this.remain = ByteUtils.copyBytes(src, 4, 8);
} }
} }

View File

@ -2,24 +2,19 @@ package org.opentcs.kc.udp.agv.param.function.af;
import org.opentcs.kc.common.byteutils.ByteUtils; import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/1/21 10:38
*/
public class ActionInfo { public class ActionInfo {
private byte[] src; private byte[] src;
//动作 ID,4个字节 //动作 ID,4个字节
public byte[] actionId; public byte[] actionId;
//动作状态,1个字节 //动作状态,1个字节
public byte actionStatus; public byte actionStatus;
//预留,7个字节 //预留,7个字节
public byte[] remain; public byte[] remain;
public ActionInfo(byte[] src) { public ActionInfo(byte[] src) {
this.src = src; this.src = src;
this.actionId = ByteUtils.copyBytes(src, 0, 4); this.actionId = ByteUtils.copyBytes(src, 0, 4);
this.actionStatus = src[4]; this.actionStatus = src[4];
this.remain = ByteUtils.copyBytes(src,5,7); this.remain = ByteUtils.copyBytes(src, 5, 7);
} }
} }

View File

@ -2,34 +2,30 @@ package org.opentcs.kc.udp.agv.param.function.af;
import org.opentcs.kc.common.byteutils.ByteUtils; import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/1/20 16:28
*/
public class BatteryStatusInfo { public class BatteryStatusInfo {
//src //src
private byte[] src; private byte[] src;
//电量百分比 //电量百分比
public float batteryPercentage; public float batteryPercentage;
//电压 //电压
public float voltage; public float voltage;
//电流 //电流
public float electricCurrent; public float electricCurrent;
//充电情况,1个字节 //充电情况,1个字节
public byte chargingState; public byte chargingState;
//预留,7个字节 //预留,7个字节
public byte[] remain; public byte[] remain;
public BatteryStatusInfo(byte[] src) {
this.src = src; public BatteryStatusInfo(byte[] src) {
this.batteryPercentage = ByteUtils.bytesToFloat(src, 0); this.src = src;
this.voltage = ByteUtils.bytesToFloat(src, 4); this.batteryPercentage = ByteUtils.bytesToFloat(src, 0);
this.electricCurrent = ByteUtils.bytesToFloat(src, 8); this.voltage = ByteUtils.bytesToFloat(src, 4);
this.chargingState = src[12]; this.electricCurrent = ByteUtils.bytesToFloat(src, 8);
this.remain=new byte[7]; this.chargingState = src[12];
for (int i = 0; i < 7; i++) { this.remain = new byte[7];
this.remain[i]=src[13+i]; for (int i = 0; i < 7; i++) {
} this.remain[i] = src[13 + i];
} }
}
} }

View File

@ -2,79 +2,74 @@ package org.opentcs.kc.udp.agv.param.function.af;
import org.opentcs.kc.common.byteutils.ByteUtils; import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/1/20 14:56
*/
public class LocationStatusInfo { public class LocationStatusInfo {
private byte[] src; private byte[] src;
//4个字节, 全局x,单位 m //4个字节, 全局x,单位 m
public float globalX; public float globalX;
//4个字节, 全局y,单位 m //4个字节, 全局y,单位 m
public float globalY; public float globalY;
//机器人绝对车体方向角,单位 rad //机器人绝对车体方向角,单位 rad
public float absoluteDirecAngle; public float absoluteDirecAngle;
//最后通过点 ID //最后通过点 ID
public Integer lastPassPointId; public Integer lastPassPointId;
//最后通过edge ID //最后通过edge ID
public Integer lastPassEdgeId; public Integer lastPassEdgeId;
//最后通过点在任务中的序列号 //最后通过点在任务中的序列号
public Integer serialNumber; public Integer serialNumber;
//置信度,1个字节 //置信度,1个字节
public byte confidenceLevel; public byte confidenceLevel;
//预留8个字节 //预留8个字节
public byte[] remain; public byte[] remain;
public static void main(String[] args) { public static void main(String[] args) {
byte[] src = new byte[32]; byte[] src = new byte[32];
src[0] = (byte) 0x73; src[0] = (byte) 0x73;
src[1] = (byte) 0xde; src[1] = (byte) 0xde;
src[2] = (byte) 0xba; src[2] = (byte) 0xba;
src[3] = (byte) 0xbd; src[3] = (byte) 0xbd;
src[4] = (byte) 0x34; src[4] = (byte) 0x34;
src[5] = (byte) 0xbf; src[5] = (byte) 0xbf;
src[6] = (byte) 0xd0; src[6] = (byte) 0xd0;
src[7] = (byte) 0x40; src[7] = (byte) 0x40;
src[8] = (byte) 0x59; src[8] = (byte) 0x59;
src[9] = (byte) 0x3c; src[9] = (byte) 0x3c;
src[10] = (byte) 0x1d; src[10] = (byte) 0x1d;
src[11] = (byte) 0x3d; src[11] = (byte) 0x3d;
System.out.println("x: "+ByteUtils.bytesToFloat(src, 0)); System.out.println("x: " + ByteUtils.bytesToFloat(src, 0));
System.out.println("y: "+ByteUtils.bytesToFloat(src, 4)); System.out.println("y: " + ByteUtils.bytesToFloat(src, 4));
System.out.println("z: "+ByteUtils.bytesToFloat(src, 8)); 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]
public LocationStatusInfo(byte[] src) { this.globalX = ByteUtils.bytesToFloat(src, 0);
this.src = src; this.globalY = ByteUtils.bytesToFloat(src, 4);
for (byte b:src){ this.absoluteDirecAngle = ByteUtils.bytesToFloat(src, 8);
System.out.print(byteToHex(b)+" "); this.lastPassPointId = ByteUtils.bytesToInt(src, 12);
} this.lastPassEdgeId = ByteUtils.bytesToInt(src, 16);
//[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.serialNumber = ByteUtils.bytesToInt(src, 20);
this.globalX = ByteUtils.bytesToFloat(src, 0); this.confidenceLevel = src[24];
this.globalY = ByteUtils.bytesToFloat(src, 4); this.remain = new byte[7];
this.absoluteDirecAngle = ByteUtils.bytesToFloat(src, 8); for (int i = 0; i < 7; i++) {
this.lastPassPointId = ByteUtils.bytesToInt(src, 12); this.remain[i] = src[25 + i];
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) { public static String byteToHex(byte b) {
// 将byte转换为无符号整数 // 将byte转换为无符号整数
int unsignedByte = b & 0xFF; int unsignedByte = b & 0xFF;
// 使用Integer.toHexString方法转换为十六进制字符串 // 使用Integer.toHexString方法转换为十六进制字符串
String hexString = Integer.toHexString(unsignedByte); String hexString = Integer.toHexString(unsignedByte);
// 如果字符串长度为1需要在前面补0 // 如果字符串长度为1需要在前面补0
if (hexString.length() == 1) { if (hexString.length() == 1) {
return "0" + hexString; return "0" + hexString;
}
return hexString;
} }
return hexString;
}
} }

View File

@ -2,21 +2,17 @@ package org.opentcs.kc.udp.agv.param.function.af;
import org.opentcs.kc.common.byteutils.ByteUtils; import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/1/20 15:49
*/
public class PathStateSequence { public class PathStateSequence {
//src //src
private byte[] src; private byte[] src;
//序列号,4个字节 //序列号,4个字节
public Integer serialNumber; public Integer serialNumber;
//序列号 //序列号
public Integer pathId; public Integer pathId;
public PathStateSequence(byte[] src) {
this.src = src; public PathStateSequence(byte[] src) {
this.serialNumber = ByteUtils.bytesToInt(src, 0); this.src = src;
this.pathId = ByteUtils.bytesToInt(src, 4); this.serialNumber = ByteUtils.bytesToInt(src, 0);
} this.pathId = ByteUtils.bytesToInt(src, 4);
}
} }

View File

@ -2,21 +2,17 @@ package org.opentcs.kc.udp.agv.param.function.af;
import org.opentcs.kc.common.byteutils.ByteUtils; import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/1/20 15:49
*/
public class PointStateSequence { public class PointStateSequence {
//src //src
private byte[] src; private byte[] src;
//序列号,4个字节 //序列号,4个字节
public Integer serialNumber; public Integer serialNumber;
//序列号 //序列号
public Integer pointId; public Integer pointId;
public PointStateSequence(byte[] src) {
this.src = src; public PointStateSequence(byte[] src) {
this.serialNumber = ByteUtils.bytesToInt(src, 0); this.src = src;
this.pointId = ByteUtils.bytesToInt(src, 4); this.serialNumber = ByteUtils.bytesToInt(src, 0);
} this.pointId = ByteUtils.bytesToInt(src, 4);
}
} }

View File

@ -22,13 +22,16 @@ public class QueryRobotStatusRsp {
public List<AbnormalEventStatusInfo> abnormalEventStatusInfoList; public List<AbnormalEventStatusInfo> abnormalEventStatusInfoList;
public List<ActionInfo> actionInfoList; public List<ActionInfo> actionInfoList;
public QueryRobotStatusRsp(byte[] src) { public QueryRobotStatusRsp(byte[] src) {
this.src = src; if (src == null) {
this.abnormal_size = src[0]; throw new RuntimeException("method_QueryRobotStatusRsp_params_src_is_NUll");
this.action_size = src[1]; }
this.remain = ByteUtils.copyBytes(src,2,2); //index 2 this.src = src;
this.locationStatusInfo = new LocationStatusInfo(ByteUtils.copyBytes(src,4,32)); this.abnormal_size = src[0];
this.runningStatusInfo = new RunningStatusInfo(ByteUtils.copyBytes(src,36,20)); 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[64]); Integer pointSize = ByteUtils.toInt(src[64]);
Integer edgeSize = ByteUtils.toInt(src[65]); Integer edgeSize = ByteUtils.toInt(src[65]);

View File

@ -2,39 +2,34 @@ package org.opentcs.kc.udp.agv.param.function.af;
import org.opentcs.kc.common.byteutils.ByteUtils; import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/1/20 15:22
*/
public class RunningStatusInfo { public class RunningStatusInfo {
private byte[] src; private byte[] src;
//4个字节, 轴x 速度,单位 m/s //4个字节, 轴x 速度,单位 m/s
public float speedX; public float speedX;
//4个字节, 轴y 速度,单位 m/s //4个字节, 轴y 速度,单位 m/s
public float speedY; public float speedY;
//车角速度,单位 rad/s //车角速度,单位 rad/s
public float angularSpeed; public float angularSpeed;
//工作模式 //工作模式
public byte workMode; public byte workMode;
//agv 状态1个字节 //agv 状态1个字节
public byte agvStatus; public byte agvStatus;
//机器人能力集设置状态,1个字节 //机器人能力集设置状态,1个字节
public byte abilitySetStatus; public byte abilitySetStatus;
//预留,5个字节 //预留,5个字节
public byte[] remain; public byte[] remain;
public RunningStatusInfo(byte[] src) { public RunningStatusInfo(byte[] src) {
this.src = src; this.src = src;
this.speedX = ByteUtils.bytesToFloat(src, 0); this.speedX = ByteUtils.bytesToFloat(src, 0);
this.speedY = ByteUtils.bytesToFloat(src, 4); this.speedY = ByteUtils.bytesToFloat(src, 4);
this.angularSpeed = ByteUtils.bytesToFloat(src, 8); this.angularSpeed = ByteUtils.bytesToFloat(src, 8);
this.workMode = src[12]; this.workMode = src[12];
this.agvStatus = src[13]; this.agvStatus = src[13];
this.abilitySetStatus = src[14]; this.abilitySetStatus = src[14];
this.remain = new byte[5]; this.remain = new byte[5];
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
this.remain[i] = src[15 + i]; this.remain[i] = src[15 + i];
}
} }
}
} }

View File

@ -2,48 +2,44 @@ package org.opentcs.kc.udp.agv.param.function.af;
import org.opentcs.kc.common.byteutils.ByteUtils; import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/1/20 15:22
*/
public class TaskStatusInfo { public class TaskStatusInfo {
private byte[] src; private byte[] src;
//订单 ID //订单 ID
public Integer orderId; public Integer orderId;
//任务 KEY //任务 KEY
public Integer taskKey; public Integer taskKey;
//点状态序列数量 //点状态序列数量
public Integer pointStatusNum; public Integer pointStatusNum;
//段状态序列数量 //段状态序列数量
public Integer edgeStatusNum; public Integer edgeStatusNum;
//预留,2个字节 //预留,2个字节
public byte[] remain; public byte[] remain;
//点状态序列 //点状态序列
public PointStateSequence[] pointStatusInfo; public PointStateSequence[] pointStatusInfo;
//段状态序列 //段状态序列
public PathStateSequence[] pathStatusInfo; public PathStateSequence[] pathStatusInfo;
public TaskStatusInfo(byte[] src) { public TaskStatusInfo(byte[] src) {
this.src = src; this.src = src;
this.orderId = ByteUtils.bytesToInt(src, 0); this.orderId = ByteUtils.bytesToInt(src, 0);
this.taskKey = ByteUtils.bytesToInt(src, 4); this.taskKey = ByteUtils.bytesToInt(src, 4);
this.pointStatusNum = ByteUtils.toInt(src[8]); this.pointStatusNum = ByteUtils.toInt(src[8]);
this.edgeStatusNum = ByteUtils.toInt(src[9]); this.edgeStatusNum = ByteUtils.toInt(src[9]);
this.remain = ByteUtils.copyBytes(src, 10, 2); this.remain = ByteUtils.copyBytes(src, 10, 2);
if (pointStatusNum > 0) { if (pointStatusNum > 0) {
this.pointStatusInfo = new PointStateSequence[pointStatusNum]; this.pointStatusInfo = new PointStateSequence[pointStatusNum];
for (int i = 0; i < pointStatusNum; i++) { for (int i = 0; i < pointStatusNum; i++) {
this.pointStatusInfo[i] = new PointStateSequence(ByteUtils.copyBytes(src, 12 + 8 * i, 8)); 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));
}
}
} }
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)
);
}
}
}
} }

View File

@ -2,22 +2,17 @@ package org.opentcs.kc.udp.agv.param.function.b0;
import org.opentcs.kc.common.byteutils.ByteUtils; import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/1/23 13:34
*/
public class QueryCargoStatusRsp { public class QueryCargoStatusRsp {
//src //src
private byte[] src; private byte[] src;
//是否载货 //是否载货
public byte isCargo; public byte isCargo;
//预留,3个字节 //预留,3个字节
public byte[] reserved; public byte[] reserved;
public QueryCargoStatusRsp(byte[] src) { public QueryCargoStatusRsp(byte[] src) {
this.src = src; this.src = src;
this.isCargo = src[0]; this.isCargo = src[0];
this.reserved = ByteUtils.copyBytes(src, 1, 3); this.reserved = ByteUtils.copyBytes(src, 1, 3);
} }
} }

View File

@ -2,25 +2,20 @@ package org.opentcs.kc.udp.agv.param.function.b1;
import org.opentcs.kc.common.byteutils.ByteUtils; import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/1/23 14:30
*/
public class SubscribeInfo { public class SubscribeInfo {
//站控协议命令码2个字节 //站控协议命令码2个字节
public byte[] commandCode; public byte[] commandCode;
//上报间隔时间 ,单位ms 2个字节 //上报间隔时间 ,单位ms 2个字节
public byte[] intervalTime; public byte[] intervalTime;
//上报持续时间,单位ms 4个字节 //上报持续时间,单位ms 4个字节
public byte[] durationTime; public byte[] durationTime;
//预留,8个字节 //预留,8个字节
public byte[] reserved; public byte[] reserved;
public SubscribeInfo(byte[] commandCode, Short intervalTime, Integer durationTime) { public SubscribeInfo(byte[] commandCode, Short intervalTime, Integer durationTime) {
this.commandCode = commandCode; this.commandCode = commandCode;
this.intervalTime = ByteUtils.shortToBytes(intervalTime); this.intervalTime = ByteUtils.shortToBytes(intervalTime);
this.durationTime = ByteUtils.intToBytes(durationTime,4); this.durationTime = ByteUtils.intToBytes(durationTime, 4);
this.reserved = new byte[8]; this.reserved = new byte[8];
} }
} }

View File

@ -4,67 +4,63 @@ import java.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/1/23 14:23
*/
public class SubscribeParam { public class SubscribeParam {
private byte[] src; private byte[] src;
private List<SubscribeInfo> subscribeInfoList; private List<SubscribeInfo> subscribeInfoList;
//uuid,64个字节 //uuid,64个字节
private byte[] uuid; private byte[] uuid;
public SubscribeParam(List<SubscribeInfo> subscribeInfoList) {
this.subscribeInfoList = subscribeInfoList; public SubscribeParam(List<SubscribeInfo> subscribeInfoList) {
this.uuid = generate64ByteUUID(); 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]);
} }
public byte[] getUuid() { for (int i = 0; i < bytes.size(); i++) {
return uuid; src[i] = bytes.get(i);
}
for (int i = 0; i < uuid.length; i++) {
src[i + 128] = uuid[i];
} }
// 生成 64 字节长度的 UUID返回 byte[] 类型 return src;
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;
}
} }

View File

@ -4,50 +4,47 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import org.opentcs.kc.common.byteutils.ByteUtils; import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/2/8 10:05
*/
public class Action { public class Action {
//动作类型,2个字节 //动作类型,2个字节
private byte[] actionType; private byte[] actionType;
//执行动作并行方式,1个字节 //执行动作并行方式,1个字节
private byte actionParallel; private byte actionParallel;
//预留1个字节 //预留1个字节
private byte actionRetain; private byte actionRetain;
//动作 id,4个字节actionId 自己定义不要重复就行可以从0开始累加 //动作 id,4个字节actionId 自己定义不要重复就行可以从0开始累加
private byte[] actionId; private byte[] actionId;
//参数长度,1个字节 //参数长度,1个字节
private byte paramSize; private byte paramSize;
//预留3个字节 //预留3个字节
private byte[] paramRetain2; private byte[] paramRetain2;
//参数内容,这里的长度 paramSize 长度,todo 注意 这里的paramContent 需要字节去构建一下 目前 这里还没有 //参数内容,这里的长度 paramSize 长度,todo 注意 这里的paramContent 需要字节去构建一下 目前 这里还没有
private byte[] paramContent; private byte[] paramContent;
public Action(byte actionType, byte actionParallel, Integer actionId, Integer paramSize, byte[] paramContent) { public Action(
this.actionType = new byte[]{actionType, (byte)0x00}; byte actionType, byte actionParallel, Integer actionId, Integer paramSize, byte[] paramContent
this.actionParallel = actionParallel; ) {
this.actionRetain = (byte)0x00; this.actionType = new byte[]{actionType, (byte) 0x00};
this.actionId = ByteUtils.intToBytes(actionId,1); this.actionParallel = actionParallel;
this.paramSize = ByteUtils.usintTo1Byte(paramSize); this.actionRetain = (byte) 0x00;
this.paramRetain2 = new byte[]{(byte)0x00, (byte)0x00,(byte)0x00}; this.actionId = ByteUtils.intToBytes(actionId, 1);
this.paramContent = paramContent; this.paramSize = ByteUtils.usintTo1Byte(paramSize);
} this.paramRetain2 = new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00};
this.paramContent = paramContent;
}
public byte[] toBytes() { public byte[] toBytes() {
ByteBuf byteBuf = Unpooled.buffer(); ByteBuf byteBuf = Unpooled.buffer();
byteBuf.writeBytes(actionType); byteBuf.writeBytes(actionType);
byteBuf.writeByte(actionParallel); byteBuf.writeByte(actionParallel);
byteBuf.writeByte(actionRetain); byteBuf.writeByte(actionRetain);
byteBuf.writeBytes(actionId); byteBuf.writeBytes(actionId);
byteBuf.writeByte(paramSize); byteBuf.writeByte(paramSize);
byteBuf.writeBytes(paramRetain2); byteBuf.writeBytes(paramRetain2);
byteBuf.writeBytes(paramContent); byteBuf.writeBytes(paramContent);
int i = byteBuf.writerIndex(); int i = byteBuf.writerIndex();
byte[] bytes = new byte[i]; byte[] bytes = new byte[i];
byteBuf.readBytes(bytes); byteBuf.readBytes(bytes);
return bytes; return bytes;
} }
} }

View File

@ -4,74 +4,71 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import org.opentcs.kc.common.byteutils.ByteUtils; import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/2/10 8:56
*/
public class ActionSet { public class ActionSet {
public static void main(String[] args) { public static void main(String[] args) {
byte[] stop = stop0x01(1, (byte) 0x01); byte[] stop = stop0x01(1, (byte) 0x01);
System.out.println(); System.out.println();
} }
public static byte stop0x01 = 0x01; public static byte stop0x01 = 0x01;
public static byte recover0x02 = 0x02; public static byte recover0x02 = 0x02;
public static byte cancelTask0x03 = 0x03; public static byte cancelTask0x03 = 0x03;
public static byte forkliftElevation0x12 = 0x12; public static byte forkliftElevation0x12 = 0x12;
public static byte trayElevation0x16 = 0x16; public static byte trayElevation0x16 = 0x16;
public static Integer stop0x01_paramsize = 8; public static Integer stop0x01_paramsize = 8;
public static Integer recover0x02_paramsize = 8; public static Integer recover0x02_paramsize = 8;
public static Integer cancelTask0x03_paramsize = 8; public static Integer cancelTask0x03_paramsize = 8;
public static Integer forkliftElevation0x12_paramsize = 8; public static Integer forkliftElevation0x12_paramsize = 8;
public static Integer trayElevation0x16_paramsize = 4; public static Integer trayElevation0x16_paramsize = 4;
public static byte[] stop0x01(Integer orderId,byte isStopImmediately){ public static byte[] stop0x01(Integer orderId, byte isStopImmediately) {
ByteBuf byteBuf = Unpooled.buffer(8); ByteBuf byteBuf = Unpooled.buffer(8);
byteBuf.writeBytes(ByteUtils.intToBytes(orderId,1)); byteBuf.writeBytes(ByteUtils.intToBytes(orderId, 1));
byteBuf.writeByte(isStopImmediately); byteBuf.writeByte(isStopImmediately);
return byteBuf.array(); return byteBuf.array();
} }
public static byte[] recover0x02(Integer orderId,Integer taskKey){ public static byte[] recover0x02(Integer orderId, Integer taskKey) {
ByteBuf byteBuf = Unpooled.buffer(8); ByteBuf byteBuf = Unpooled.buffer(8);
byteBuf.writeBytes(ByteUtils.intToBytes(orderId,1)); byteBuf.writeBytes(ByteUtils.intToBytes(orderId, 1));
byteBuf.writeBytes(ByteUtils.intToBytes(taskKey,1)); byteBuf.writeBytes(ByteUtils.intToBytes(taskKey, 1));
return byteBuf.array(); return byteBuf.array();
} }
//取消任务 //取消任务
public static byte[] cancelTask0x03(Integer orderId,byte isStopImmediately){ public static byte[] cancelTask0x03(Integer orderId, byte isStopImmediately) {
ByteBuf byteBuf = Unpooled.buffer(8); ByteBuf byteBuf = Unpooled.buffer(8);
byteBuf.writeBytes(ByteUtils.intToBytes(orderId,1)); byteBuf.writeBytes(ByteUtils.intToBytes(orderId, 1));
byteBuf.writeByte(isStopImmediately); byteBuf.writeByte(isStopImmediately);
return byteBuf.array(); return byteBuf.array();
} }
/** /**
* desc:叉齿升降 * desc:叉齿升降
* 支持的命令0xAE 0xB2 * 支持的命令0xAE 0xB2
* *
* */ */
public static byte[] forkliftElevation0x12(float lifitingHeight,byte hightMean,byte moveType, byte taskOperationType){ public static byte[] forkliftElevation0x12(
ByteBuf byteBuf = Unpooled.buffer(8); float lifitingHeight, byte hightMean, byte moveType, byte taskOperationType
byteBuf.writeBytes(ByteUtils.floatToBytes(lifitingHeight)); ) {
byteBuf.writeByte(hightMean); ByteBuf byteBuf = Unpooled.buffer(8);
byteBuf.writeByte(moveType); byteBuf.writeBytes(ByteUtils.floatToBytes(lifitingHeight));
byteBuf.writeByte(taskOperationType); byteBuf.writeByte(hightMean);
return byteBuf.array(); byteBuf.writeByte(moveType);
} byteBuf.writeByte(taskOperationType);
return byteBuf.array();
}
//托盘升降 //托盘升降
public static byte[] trayElevation0x16(byte palletMovementMode){ public static byte[] trayElevation0x16(byte palletMovementMode) {
ByteBuf byteBuf = Unpooled.buffer(4); ByteBuf byteBuf = Unpooled.buffer(4);
byteBuf.writeByte(palletMovementMode); byteBuf.writeByte(palletMovementMode);
return byteBuf.array(); return byteBuf.array();
} }
} }

View File

@ -4,54 +4,51 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import org.opentcs.kc.common.byteutils.ByteUtils; import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: "0xAE 导航参数"
* @Author: caixiang
* @DATE: 2024/12/30 15:44
*/
public class NavigationParam { public class NavigationParam {
//订单 ID,4个字节 ,Integer,从1开始依次累加 //订单 ID,4个字节 ,Integer,从1开始依次累加
private byte[] orderId; private byte[] orderId;
//任务 KEY,4个字节,Integer,从1开始依次累加 //任务 KEY,4个字节,Integer,从1开始依次累加
private byte[] taskKey; private byte[] taskKey;
//路径点信息个数单条任务最大支持 8 个路径点,1个字节 ,byte //路径点信息个数单条任务最大支持 8 个路径点,1个字节 ,byte
private byte pointNum; private byte pointNum;
//路径信息个数单条任务最大支持 8 个路径点,1个字节,byte //路径信息个数单条任务最大支持 8 个路径点,1个字节,byte
private byte pathNum; private byte pathNum;
//保留,2个字节 //保留,2个字节
private byte[] retain; private byte[] retain;
//任务中路径点信息结构体,长度 pointNum //任务中路径点信息结构体,长度 pointNum
private Point[] points; private Point[] points;
//任务中路径信息结构体长度 pathNum //任务中路径信息结构体长度 pathNum
private Path[] paths; private Path[] paths;
public NavigationParam(Integer orderId, Integer taskKey, byte pointNum, byte pathNum, Point[] points, Path[] paths) { public NavigationParam(
this.orderId = ByteUtils.intToBytes(orderId,1); Integer orderId, Integer taskKey, byte pointNum, byte pathNum, Point[] points, Path[] paths
this.taskKey = ByteUtils.intToBytes(taskKey,1); ) {
this.pointNum = pointNum; this.orderId = ByteUtils.intToBytes(orderId, 1);
this.pathNum = pathNum; this.taskKey = ByteUtils.intToBytes(taskKey, 1);
this.retain = new byte[]{(byte)0x00, (byte)0x00}; this.pointNum = pointNum;
this.points = points; this.pathNum = pathNum;
this.paths = paths; 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) {
public byte[] toBytes() { byteBuf.writeBytes(path.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;
} }
int i = byteBuf.writerIndex();
byte[] bytes = new byte[i];
byteBuf.readBytes(bytes);
return bytes;
}
} }

View File

@ -4,71 +4,71 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import org.opentcs.kc.common.byteutils.ByteUtils; import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/2/8 9:39
*/
public class Path { public class Path {
//序列号,4个字节,Integer,用于定位段在整个任务中的位置 1 开始奇数递增 135......point 2 4 6这样path 和point就可以结合起来了以区分同一个段 ID 在任务中是否多次出现 //序列号,4个字节,Integer,用于定位段在整个任务中的位置 1 开始奇数递增 135......point 2 4 6这样path 和point就可以结合起来了以区分同一个段 ID 在任务中是否多次出现
private byte[] serialNum; private byte[] serialNum;
// ID,4个字节,Integer // ID,4个字节,Integer
private byte[] pathId; private byte[] pathId;
//指定角度时路径点的车头角度,4个字节,float , isSpecifyAngle==1 的时候这个字段才生效 //指定角度时路径点的车头角度,4个字节,float , isSpecifyAngle==1 的时候这个字段才生效
private byte[] angle; private byte[] angle;
//是否指定路径点角度,1个字节,byte,1 表示指定0 表示不指定 //是否指定路径点角度,1个字节,byte,1 表示指定0 表示不指定
private byte isSpecifyAngle; private byte isSpecifyAngle;
//行驶姿态,1个字节,byte //行驶姿态,1个字节,byte
private byte drivePose; private byte drivePose;
//任务中边上动作信息个数,1个字节,byte //任务中边上动作信息个数,1个字节,byte
private byte pathActionSize; private byte pathActionSize;
//保留,1个字节 //保留,1个字节
private byte pathRetain; private byte pathRetain;
//指定的目标最大速度,4个字节,float //指定的目标最大速度,4个字节,float
private byte[] maxSpeed; private byte[] maxSpeed;
//指定的目标最大角速度,4个字节,float //指定的目标最大角速度,4个字节,float
private byte[] maxAngularSpeed; private byte[] maxAngularSpeed;
//预留4个字节 //预留4个字节
private byte[] pathRetain2; private byte[] pathRetain2;
//任务中点上动作结构体,这里数组的长度是 pathActionSize 长度 //任务中点上动作结构体,这里数组的长度是 pathActionSize 长度
private Action[] actions; private Action[] actions;
public Path(Integer serialNum, Integer pathId, float angle, byte isSpecifyAngle, byte drivePose, byte pathActionSize, float maxSpeed, float maxAngularSpeed, Action[] actions) { public Path(
this.serialNum = ByteUtils.intToBytes(serialNum,1); Integer serialNum, Integer pathId, float angle, byte isSpecifyAngle, byte drivePose,
this.pathId = ByteUtils.intToBytes(pathId,1); byte pathActionSize, float maxSpeed, float maxAngularSpeed, Action[] actions
this.angle = ByteUtils.floatToBytes(angle); ) {
this.isSpecifyAngle = isSpecifyAngle; this.serialNum = ByteUtils.intToBytes(serialNum, 1);
this.drivePose = drivePose; this.pathId = ByteUtils.intToBytes(pathId, 1);
this.pathActionSize = pathActionSize; this.angle = ByteUtils.floatToBytes(angle);
this.pathRetain = (byte)0x00; this.isSpecifyAngle = isSpecifyAngle;
this.maxSpeed = ByteUtils.floatToBytes(maxSpeed); this.drivePose = drivePose;
this.maxAngularSpeed = ByteUtils.floatToBytes(maxAngularSpeed); this.pathActionSize = pathActionSize;
this.pathRetain2 = new byte[]{(byte)0x00, (byte)0x00,(byte)0x00,(byte)0x00}; this.pathRetain = (byte) 0x00;
this.actions = actions; this.maxSpeed = ByteUtils.floatToBytes(maxSpeed);
} this.maxAngularSpeed = ByteUtils.floatToBytes(maxAngularSpeed);
this.pathRetain2 = new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
public byte[] toBytes() { this.actions = actions;
ByteBuf byteBuf = Unpooled.buffer(); }
byteBuf.writeBytes(serialNum);
byteBuf.writeBytes(pathId); public byte[] toBytes() {
byteBuf.writeBytes(angle); ByteBuf byteBuf = Unpooled.buffer();
byteBuf.writeByte(isSpecifyAngle); byteBuf.writeBytes(serialNum);
byteBuf.writeByte(drivePose); byteBuf.writeBytes(pathId);
byteBuf.writeByte(pathActionSize); byteBuf.writeBytes(angle);
byteBuf.writeByte(pathRetain); byteBuf.writeByte(isSpecifyAngle);
byteBuf.writeBytes(maxSpeed); byteBuf.writeByte(drivePose);
byteBuf.writeBytes(maxAngularSpeed); byteBuf.writeByte(pathActionSize);
byteBuf.writeBytes(pathRetain2); byteBuf.writeByte(pathRetain);
for (Action action : actions) { byteBuf.writeBytes(maxSpeed);
byteBuf.writeBytes(action.toBytes()); byteBuf.writeBytes(maxAngularSpeed);
} byteBuf.writeBytes(pathRetain2);
int i = byteBuf.writerIndex(); if (actions != null) {
byte[] bytes = new byte[i]; for (Action action : actions) {
byteBuf.readBytes(bytes); byteBuf.writeBytes(action.toBytes());
return bytes; }
} }
int i = byteBuf.writerIndex();
byte[] bytes = new byte[i];
byteBuf.readBytes(bytes);
return bytes;
}
} }

View File

@ -4,51 +4,52 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import org.opentcs.kc.common.byteutils.ByteUtils; import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/2/8 9:39
*/
public class Point { public class Point {
//序列号,4个字节,Integer,用于定位点在整个任务中的位置 0 开始偶数递增 0246......以区分同一个点 ID 在任务中是否多次出现 //序列号,4个字节,Integer,用于定位点在整个任务中的位置 0 开始偶数递增 0246......以区分同一个点 ID 在任务中是否多次出现
private byte[] serialNum; private byte[] serialNum;
//路径点 ID,4个字节,Integer //路径点 ID,4个字节,Integer
private byte[] pointId; private byte[] pointId;
//指定角度时路径点的车头角度,4个字节,float , isSpecifyAngle==1 的时候这个字段才生效 //指定角度时路径点的车头角度,4个字节,float , isSpecifyAngle==1 的时候这个字段才生效
private byte[] angle; private byte[] angle;
//是否指定路径点角度,1个字节,byte,1 表示指定0 表示不指定 //是否指定路径点角度,1个字节,byte,1 表示指定0 表示不指定
private byte isSpecifyAngle; private byte isSpecifyAngle;
//任务中点上动作信息个数,1个字节,byte //任务中点上动作信息个数,1个字节,byte
private byte pointActionSize; private byte pointActionSize;
//保留,6个字节 //保留,6个字节
private byte[] pointRetain; private byte[] pointRetain;
//任务中点上动作结构体,这里数组的长度是 pointActionSize 长度 //任务中点上动作结构体,这里数组的长度是 pointActionSize 长度
private Action[] actions; private Action[] actions;
public Point(Integer serialNum, Integer pointId, float angle, byte isSpecifyAngle, byte pointActionSize, Action[] actions) { public Point(
this.serialNum = ByteUtils.intToBytes(serialNum,1); Integer serialNum, Integer pointId, float angle, byte isSpecifyAngle, byte pointActionSize,
this.pointId = ByteUtils.intToBytes(pointId,1); Action[] actions
this.angle = ByteUtils.floatToBytes(angle); ) {
this.isSpecifyAngle = isSpecifyAngle; this.serialNum = ByteUtils.intToBytes(serialNum, 1);
this.pointActionSize = pointActionSize; this.pointId = ByteUtils.intToBytes(pointId, 1);
this.pointRetain = new byte[]{(byte)0x00, (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00}; this.angle = ByteUtils.floatToBytes(angle);
this.actions = actions; 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() { public byte[] toBytes() {
ByteBuf byteBuf = Unpooled.buffer(); ByteBuf byteBuf = Unpooled.buffer();
byteBuf.writeBytes(serialNum); byteBuf.writeBytes(serialNum);
byteBuf.writeBytes(pointId); byteBuf.writeBytes(pointId);
byteBuf.writeBytes(angle); byteBuf.writeBytes(angle);
byteBuf.writeByte(isSpecifyAngle); byteBuf.writeByte(isSpecifyAngle);
byteBuf.writeByte(pointActionSize); byteBuf.writeByte(pointActionSize);
byteBuf.writeBytes(pointRetain); byteBuf.writeBytes(pointRetain);
for (Action action : actions) { if (actions != null) {
byteBuf.writeBytes(action.toBytes()); for (Action action : actions) {
} byteBuf.writeBytes(action.toBytes());
int i = byteBuf.writerIndex(); }
byte[] bytes = new byte[i];
byteBuf.readBytes(bytes);
return bytes;
} }
int i = byteBuf.writerIndex();
byte[] bytes = new byte[i];
byteBuf.readBytes(bytes);
return bytes;
}
} }

View File

@ -4,48 +4,44 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.opentcs.kc.common.byteutils.ByteUtil; import org.opentcs.kc.common.byteutils.ByteUtil;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2024/12/30 15:44
*/
public class ReadParam { public class ReadParam {
private byte valueNum; private byte valueNum;
private byte[] retain; private byte[] retain;
private byte[] valueId; private byte[] valueId;
private byte[] readStrValues; 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}; public ReadParam(byte[] valueId, List<ReadStrValue> rvalues) {
List<Byte> bytes = new ArrayList<>(); // 这里的valueId 也是 header里面的 transationId
for (ReadStrValue b : rvalues) { valueNum = ByteUtil.intTo1Byte(rvalues.size());
bytes.addAll(b.toBytes()); retain = new byte[]{0x00, 0x00, 0x00};
}
readStrValues = new byte[bytes.size()];
for (int i = 0; i < bytes.size(); i++) {
readStrValues[i] = bytes.get(i);
}
}
public byte[] toBytes() { this.valueId = new byte[]{valueId[0], valueId[1], 0x00, 0x00};
List<Byte> bytes = new ArrayList<>(); List<Byte> bytes = new ArrayList<>();
bytes.add(valueNum); for (ReadStrValue b : rvalues) {
for (byte b : retain) { bytes.addAll(b.toBytes());
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;
} }
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;
}
} }

View File

@ -2,36 +2,32 @@ package org.opentcs.kc.udp.agv.param.function.read;
import org.opentcs.kc.common.byteutils.ByteUtils; import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/2/5 14:55
*/
public class ReadRsp { public class ReadRsp {
//源数组 //源数组
private byte[] src; private byte[] src;
//ValueID ,4个字节 //ValueID ,4个字节
public byte[] valueId; public byte[] valueId;
//应答数据总长度,2个字节 //应答数据总长度,2个字节
public short valueByteLength; public short valueByteLength;
//预留,2个字节 //预留,2个字节
public byte[] reserved; public byte[] reserved;
//变量值,长度是 valueByteLength //变量值,长度是 valueByteLength
public byte[] dataValue; public byte[] dataValue;
public ReadRsp(byte[] src) { public ReadRsp(byte[] src) {
this.src = src; this.src = src;
this.valueId = ByteUtils.copyBytes(src, 0, 4); this.valueId = ByteUtils.copyBytes(src, 0, 4);
this.valueByteLength = ByteUtils.bytesToShort(ByteUtils.copyBytes(src, 4, 2),1); this.valueByteLength = ByteUtils.bytesToShort(ByteUtils.copyBytes(src, 4, 2), 1);
this.reserved = ByteUtils.copyBytes(src, 6, 2); this.reserved = ByteUtils.copyBytes(src, 6, 2);
this.dataValue = ByteUtils.copyBytes(src, 8, valueByteLength-8); this.dataValue = ByteUtils.copyBytes(src, 8, valueByteLength - 8);
} }
public boolean isOk(){ public boolean isOk() {
if(valueByteLength<=0){ if (valueByteLength <= 0) {
return false; return false;
}else {
return true;
}
} }
else {
return true;
}
}
} }

View File

@ -4,46 +4,43 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.opentcs.kc.common.byteutils.ByteUtil; import org.opentcs.kc.common.byteutils.ByteUtil;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2024/12/30 15:46
*/
public class ReadStrValue { public class ReadStrValue {
//变量名16个字节( 以这个变量名来定位变量的) //变量名16个字节( 以这个变量名来定位变量的)
private byte[] varName; private byte[] varName;
//成员变量数量4个字节 //成员变量数量4个字节
private byte[] memberVarNum; private byte[] memberVarNum;
private byte[] memberList; 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() { public ReadStrValue(String varName, Integer memberVarNum, List<ReadValueMember> memberList) {
List<Byte> bytes = new ArrayList<>(); this.varName = ByteUtil.stringTo16Byte(varName);
for (byte b : varName) { this.memberVarNum = ByteUtil.intToBytes(memberVarNum);
bytes.add(b); List<Byte> bytes = new ArrayList<>();
} for (ReadValueMember member : memberList) {
for (byte b : memberVarNum) { bytes.addAll(member.toBytes());
bytes.add(b);
}
for (byte b : memberList) {
bytes.add(b);
}
return bytes;
} }
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;
}
} }

View File

@ -4,31 +4,26 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.opentcs.kc.common.byteutils.ByteUtil; import org.opentcs.kc.common.byteutils.ByteUtil;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2024/12/30 15:49
*/
public class ReadValueMember { public class ReadValueMember {
//偏移值就是以这个变量的起始点为基准偏移几个字节来读取数组里的后面几个变量基本上用于数组变量单体变量用不着 2个字节 //偏移值就是以这个变量的起始点为基准偏移几个字节来读取数组里的后面几个变量基本上用于数组变量单体变量用不着 2个字节
private byte[] offsetValue; private byte[] offsetValue;
//变量成员长度就是你要读取的content长度可以是1个变量的长度也可以是n个变量的长度(就是数据类型 的字节长度) //变量成员长度就是你要读取的content长度可以是1个变量的长度也可以是n个变量的长度(就是数据类型 的字节长度)
private byte[] memberVarLength; private byte[] memberVarLength;
public ReadValueMember(Short offsetValue, Short memberVarLength) { public ReadValueMember(Short offsetValue, Short memberVarLength) {
this.offsetValue = ByteUtil.shortToBytes(offsetValue); this.offsetValue = ByteUtil.shortToBytes(offsetValue);
this.memberVarLength = ByteUtil.shortToBytes(memberVarLength); this.memberVarLength = ByteUtil.shortToBytes(memberVarLength);
} }
public List<Byte> toBytes() { public List<Byte> toBytes() {
List<Byte> bytes = new ArrayList<>(); List<Byte> bytes = new ArrayList<>();
for (byte b : offsetValue) { for (byte b : offsetValue) {
bytes.add(b); bytes.add(b);
}
for (byte b : memberVarLength) {
bytes.add(b);
}
return bytes;
} }
for (byte b : memberVarLength) {
bytes.add(b);
}
return bytes;
}
} }

View File

@ -5,35 +5,31 @@ import io.netty.buffer.Unpooled;
import java.util.List; import java.util.List;
import org.opentcs.kc.common.byteutils.ByteUtils; import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/2/5 16:41
*/
public class WriteParam { public class WriteParam {
//变量数量 1个字节 ,最大只支持 15 个变量 //变量数量 1个字节 ,最大只支持 15 个变量
private byte valueNum; private byte valueNum;
//保留 3个字节 //保留 3个字节
private byte[] retain; private byte[] retain;
//这里的length 就是 valueNum //这里的length 就是 valueNum
private List<WriteStrValue> values; 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() { public WriteParam(Integer valueNum, List<WriteStrValue> values) {
ByteBuf byteBuf = Unpooled.buffer(); this.valueNum = ByteUtils.usintTo1Byte(valueNum);
byteBuf.writeByte(valueNum); this.retain = new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00};
byteBuf.writeBytes(retain); this.values = values;
for (WriteStrValue value : values) { }
byteBuf.writeBytes(value.toBytes());
} public byte[] toBytes() {
int i = byteBuf.writerIndex(); ByteBuf byteBuf = Unpooled.buffer();
byte[] bytes = new byte[i]; byteBuf.writeByte(valueNum);
byteBuf.readBytes(bytes); byteBuf.writeBytes(retain);
return bytes; for (WriteStrValue value : values) {
byteBuf.writeBytes(value.toBytes());
} }
int i = byteBuf.writerIndex();
byte[] bytes = new byte[i];
byteBuf.readBytes(bytes);
return bytes;
}
} }

View File

@ -5,37 +5,32 @@ import io.netty.buffer.Unpooled;
import java.util.List; import java.util.List;
import org.opentcs.kc.common.byteutils.ByteUtils; import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/2/5 16:43
*/
public class WriteStrValue { public class WriteStrValue {
//变量名 16个字节 //变量名 16个字节
private String valueName; private String valueName;
//变量成员数量 4个字节 //变量成员数量 4个字节
private Integer valueNum; private Integer valueNum;
private List<WriteValueMember> members; private List<WriteValueMember> members;
public WriteStrValue(String valueName, Integer valueNum, List<WriteValueMember> members) { public WriteStrValue(String valueName, Integer valueNum, List<WriteValueMember> members) {
this.valueName = valueName; this.valueName = valueName;
this.valueNum = valueNum; this.valueNum = valueNum;
this.members = members; 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);
public byte[] toBytes() { return bytes;
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;
}
} }

View File

@ -4,29 +4,25 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import org.opentcs.kc.common.byteutils.ByteUtils; import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/2/5 16:44
*/
public class WriteValueMember { public class WriteValueMember {
//变量成员偏移 2个字节 //变量成员偏移 2个字节
private byte[] valueOffset; private byte[] valueOffset;
//变量成员长度 2个字节 //变量成员长度 2个字节
private byte[] valueLength; private byte[] valueLength;
//变量成员值 4个字节 //变量成员值 4个字节
private byte[] newValue; 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() { public WriteValueMember(Short valueOffset, Short valueLength, byte[] newValue) {
ByteBuf byteBuf = Unpooled.buffer(8); this.valueOffset = ByteUtils.shortToBytes(valueOffset);
byteBuf.writeBytes(valueOffset); this.valueLength = ByteUtils.shortToBytes(valueLength);
byteBuf.writeBytes(valueLength); this.newValue = newValue;
byteBuf.writeBytes(newValue); }
return byteBuf.array();
} public byte[] toBytes() {
ByteBuf byteBuf = Unpooled.buffer(8);
byteBuf.writeBytes(valueOffset);
byteBuf.writeBytes(valueLength);
byteBuf.writeBytes(newValue);
return byteBuf.array();
}
} }

View File

@ -4,29 +4,25 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import org.opentcs.kc.common.byteutils.ByteUtil; import org.opentcs.kc.common.byteutils.ByteUtil;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/2/7 9:21
*/
public class RobotSetPosition { public class RobotSetPosition {
//机器人 x 坐标 ,8 个字节 //机器人 x 坐标 ,8 个字节
private byte[] robotX; private byte[] robotX;
//机器人 y 坐标 ,8 个字节 //机器人 y 坐标 ,8 个字节
private byte[] robotY; private byte[] robotY;
//机器人朝向角度 ,8 个字节 //机器人朝向角度 ,8 个字节
private byte[] robotAngle; private byte[] robotAngle;
public RobotSetPosition(double robotX, double robotY, double robotAngle) { public RobotSetPosition(double robotX, double robotY, double robotAngle) {
this.robotX = ByteUtil.doubleToBytes(robotX); this.robotX = ByteUtil.doubleToBytes(robotX);
this.robotY = ByteUtil.doubleToBytes(robotY); this.robotY = ByteUtil.doubleToBytes(robotY);
this.robotAngle = ByteUtil.doubleToBytes(robotAngle); this.robotAngle = ByteUtil.doubleToBytes(robotAngle);
} }
public byte[] toBytes() {
ByteBuf byteBuf = Unpooled.buffer(24); public byte[] toBytes() {
byteBuf.writeBytes(robotX); ByteBuf byteBuf = Unpooled.buffer(24);
byteBuf.writeBytes(robotY); byteBuf.writeBytes(robotX);
byteBuf.writeBytes(robotAngle); byteBuf.writeBytes(robotY);
return byteBuf.array(); byteBuf.writeBytes(robotAngle);
} return byteBuf.array();
}
} }

View File

@ -3,128 +3,123 @@ package org.opentcs.kc.udp.agv.param.function.x17;
import java.util.Arrays; import java.util.Arrays;
import org.opentcs.kc.common.byteutils.ByteUtils; import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/2/7 9:43
*/
public class QueryRobotRunStatusRsp { public class QueryRobotRunStatusRsp {
//本体温度 ,8个字节 ,double //本体温度 ,8个字节 ,double
private double temp; private double temp;
//位置的 X 坐标 , 8个字节 ,double //位置的 X 坐标 , 8个字节 ,double
private double currentX; private double currentX;
//位置的 Y 坐标,8个字节 ,double //位置的 Y 坐标,8个字节 ,double
private double currentY; private double currentY;
//位置的 角度 ,8个字节 ,double //位置的 角度 ,8个字节 ,double
private double currentAngle; private double currentAngle;
//电池电量,,8个字节 ,double //电池电量,,8个字节 ,double
private double battery; private double battery;
//是否被阻挡,1个字节,byte //是否被阻挡,1个字节,byte
private byte isBlocked; private byte isBlocked;
//是否在充电,1个字节,byte //是否在充电,1个字节,byte
private byte isCharging; private byte isCharging;
//运行模式,1个字节,byte //运行模式,1个字节,byte
private byte runMode; private byte runMode;
//地图载入状态,1个字节,byte //地图载入状态,1个字节,byte
private byte mapLoadState; private byte mapLoadState;
//当前的目标点 id,U32 ,4个字节 //当前的目标点 id,U32 ,4个字节
private Integer currentTargetId; private Integer currentTargetId;
//前进速度,8个字节,double //前进速度,8个字节,double
private double forwardSpeed; private double forwardSpeed;
//转弯速度,8个字节,double //转弯速度,8个字节,double
private double turnSpeed; private double turnSpeed;
//电池电压充电为正放电为负,8个字节,double //电池电压充电为正放电为负,8个字节,double
private double batteryVoltage; private double batteryVoltage;
//电流充电为正放电为负,double //电流充电为正放电为负,double
private double electricCurrent; private double electricCurrent;
//当前任务状态,1个字节,byte //当前任务状态,1个字节,byte
private byte taskState; private byte taskState;
//保留1个字节 //保留1个字节
private byte retain1; private byte retain1;
//地图版本号,2个字节 //地图版本号,2个字节
private short mapVersion; private short mapVersion;
//保留4个字节 //保留4个字节
private byte[] retain2; private byte[] retain2;
//累计行驶里程单位 m,8个字节 //累计行驶里程单位 m,8个字节
private double cumulativeMileage; private double cumulativeMileage;
//本次运行时间单位 ms,8个字节 //本次运行时间单位 ms,8个字节
private double currentRunTime; private double currentRunTime;
//累计运行时间单位 ms,8个字节 //累计运行时间单位 ms,8个字节
private double cumulativeRunTime; private double cumulativeRunTime;
//机器人定位状态,1个字节 //机器人定位状态,1个字节
private byte robotLocalizationState; public byte robotLocalizationState;
//保留3个字节 //保留3个字节
private byte[] retain3; private byte[] retain3;
//地图数量U32,4个字节 //地图数量U32,4个字节
private Integer mapNum; private Integer mapNum;
//当前地图名称,64字节 //当前地图名称,64字节
private String currentMapName; private String currentMapName;
//置信度,4个字节 //置信度,4个字节
private float confidence; private float confidence;
//保留4个字节 //保留4个字节
private byte[] retain4; private byte[] retain4;
public QueryRobotRunStatusRsp(byte[] src) { public QueryRobotRunStatusRsp(byte[] src) {
this.temp = ByteUtils.bytesToDouble(src, 0); this.temp = ByteUtils.bytesToDouble(src, 0);
this.currentX = ByteUtils.bytesToDouble(src, 8); this.currentX = ByteUtils.bytesToDouble(src, 8);
this.currentY = ByteUtils.bytesToDouble(src, 16); this.currentY = ByteUtils.bytesToDouble(src, 16);
this.currentAngle = ByteUtils.bytesToDouble(src, 24); this.currentAngle = ByteUtils.bytesToDouble(src, 24);
this.battery = ByteUtils.bytesToDouble(src, 32); this.battery = ByteUtils.bytesToDouble(src, 32);
this.isBlocked = src[40]; this.isBlocked = src[40];
this.isCharging = src[41]; this.isCharging = src[41];
this.runMode = src[42]; this.runMode = src[42];
this.mapLoadState = src[43]; this.mapLoadState = src[43];
this.currentTargetId = ByteUtils.bytesToInt(src, 44); this.currentTargetId = ByteUtils.bytesToInt(src, 44);
this.forwardSpeed = ByteUtils.bytesToDouble(src, 48); this.forwardSpeed = ByteUtils.bytesToDouble(src, 48);
this.turnSpeed = ByteUtils.bytesToDouble(src, 56); this.turnSpeed = ByteUtils.bytesToDouble(src, 56);
this.batteryVoltage = ByteUtils.bytesToDouble(src, 64); this.batteryVoltage = ByteUtils.bytesToDouble(src, 64);
this.electricCurrent = ByteUtils.bytesToDouble(src, 72); this.electricCurrent = ByteUtils.bytesToDouble(src, 72);
this.taskState = src[80]; this.taskState = src[80];
this.retain1 = src[81]; this.retain1 = src[81];
this.mapVersion = ByteUtils.bytesToShortLitt(src, 82); this.mapVersion = ByteUtils.bytesToShortLitt(src, 82);
this.retain2 = ByteUtils.copyBytes(src, 84, 4); this.retain2 = ByteUtils.copyBytes(src, 84, 4);
this.cumulativeMileage = ByteUtils.bytesToDouble(src, 88); this.cumulativeMileage = ByteUtils.bytesToDouble(src, 88);
this.currentRunTime = ByteUtils.bytesToDouble(src, 96); this.currentRunTime = ByteUtils.bytesToDouble(src, 96);
this.cumulativeRunTime = ByteUtils.bytesToDouble(src, 104); this.cumulativeRunTime = ByteUtils.bytesToDouble(src, 104);
this.robotLocalizationState = src[112]; this.robotLocalizationState = src[112];
this.retain3 = ByteUtils.copyBytes(src, 113, 3); this.retain3 = ByteUtils.copyBytes(src, 113, 3);
this.mapNum = ByteUtils.bytesToInt(src, 116); this.mapNum = ByteUtils.bytesToInt(src, 116);
this.currentMapName = ByteUtils.bytesToString(src, 120, 64); this.currentMapName = ByteUtils.bytesToString(src, 120, 64);
this.confidence = ByteUtils.bytesToFloat(src, 184); this.confidence = ByteUtils.bytesToFloat(src, 184);
this.retain4 = ByteUtils.copyBytes(src, 188, 4); this.retain4 = ByteUtils.copyBytes(src, 188, 4);
} }
@Override @Override
public String toString() { public String toString() {
return "QueryRobotRunStatusRsp{" + return "QueryRobotRunStatusRsp{" +
"temp=" + temp + "temp=" + temp +
", currentX=" + currentX + ", currentX=" + currentX +
", currentY=" + currentY + ", currentY=" + currentY +
", currentAngle=" + currentAngle + ", currentAngle=" + currentAngle +
", battery=" + battery + ", battery=" + battery +
", isBlocked=" + isBlocked + ", isBlocked=" + isBlocked +
", isCharging=" + isCharging + ", isCharging=" + isCharging +
", runMode=" + runMode + ", runMode=" + runMode +
", mapLoadState=" + mapLoadState + ", mapLoadState=" + mapLoadState +
", currentTargetId=" + currentTargetId + ", currentTargetId=" + currentTargetId +
", forwardSpeed=" + forwardSpeed + ", forwardSpeed=" + forwardSpeed +
", turnSpeed=" + turnSpeed + ", turnSpeed=" + turnSpeed +
", batteryVoltage=" + batteryVoltage + ", batteryVoltage=" + batteryVoltage +
", electricCurrent=" + electricCurrent + ", electricCurrent=" + electricCurrent +
", taskState=" + taskState + ", taskState=" + taskState +
", retain1=" + retain1 + ", retain1=" + retain1 +
", mapVersion=" + mapVersion + ", mapVersion=" + mapVersion +
", retain2=" + Arrays.toString(retain2) + ", retain2=" + Arrays.toString(retain2) +
", cumulativeMileage=" + cumulativeMileage + ", cumulativeMileage=" + cumulativeMileage +
", cuttentRunTime=" + currentRunTime + ", cuttentRunTime=" + currentRunTime +
", cumulativeRunTime=" + cumulativeRunTime + ", cumulativeRunTime=" + cumulativeRunTime +
", robotLocalizationState=" + robotLocalizationState + ", robotLocalizationState=" + robotLocalizationState +
", retain3=" + Arrays.toString(retain3) + ", retain3=" + Arrays.toString(retain3) +
", mapNum=" + mapNum + ", mapNum=" + mapNum +
", currentMapName='" + currentMapName + '\'' + ", currentMapName='" + currentMapName + '\'' +
", confidence=" + confidence + ", confidence=" + confidence +
", retain4=" + Arrays.toString(retain4) + ", retain4=" + Arrays.toString(retain4) +
'}'; '}';
} }
} }

View File

@ -4,86 +4,90 @@ package org.opentcs.kc.udp.agv.param.rsp;
import org.opentcs.kc.common.byteutils.ByteUtils; import org.opentcs.kc.common.byteutils.ByteUtils;
import org.opentcs.kc.udp.agv.param.AgvEventHeader; import org.opentcs.kc.udp.agv.param.AgvEventHeader;
/**
* @Desc: "通用的数据传输底层类"
* @Author: caixiang
* @DATE: 2022/10/18 16:22
*/
public class RcvEventPackage { public class RcvEventPackage {
private boolean isOk; private boolean isOk;
private byte[] value; private byte[] value;
private String content; private String content;
// header size = 授权码16 + 报文头12(包括报文数据长度2个字节+保留2个字节) = 28 // header size = 授权码16 + 报文头12(包括报文数据长度2个字节+保留2个字节) = 28
private AgvEventHeader header; private AgvEventHeader header;
private byte[] dataBytes; private byte[] dataBytes;
private String getContent(byte i) { private String getContent(byte i) {
if(i==0x00){ if (i == 0x00) {
return "成功执行"; return "成功执行";
}else if(i==0x01){ }
return "执行失败,原因未知"; else if (i == 0x01) {
}else if(i==0x02){ return "执行失败,原因未知";
return "服务码错误"; }
}else if(i==0x03){ else if (i == 0x02) {
return "命令码错误"; return "服务码错误";
}else if(i==0x04){ }
return "报文头部错误"; else if (i == 0x03) {
}else if(i==0x80){ return "命令码错误";
return "无法执行命令,因为当前车辆导航状态与命令冲突"; }
}else if(i==0xFF){ else if (i == 0x04) {
return "协议授权码错误"; return "报文头部错误";
}else { }
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() { public RcvEventPackage(byte executionCode, byte[] value) {
return isOk; 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 void setValue(byte[] value) { public RcvEventPackage() {
this.value = value; }
}
public byte[] getValue() { public boolean isOk() {
return value; return isOk;
} }
public String getContent() { public void setValue(byte[] value) {
return content; this.value = value;
} }
public AgvEventHeader getHeader() { public byte[] getValue() {
return header; return value;
} }
public byte[] getDataBytes() { public String getContent() {
return dataBytes; return content;
} }
@Override public AgvEventHeader getHeader() {
public String toString() { return header;
return "RcvPackage{" + }
"isOk=" + isOk +
", value=" + value + public byte[] getDataBytes() {
", content='" + content + '\'' + return dataBytes;
'}'; }
}
@Override
public String toString() {
return "RcvPackage{" +
"isOk=" + isOk +
", value=" + value +
", content='" + content + '\'' +
'}';
}
} }

View File

@ -9,6 +9,13 @@ import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket; import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.channel.socket.nio.NioDatagramChannel;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Set;
import org.opentcs.access.KernelServicePortal;
import org.opentcs.access.rmi.KernelServicePortalBuilder;
import org.opentcs.common.GuestUserCredentials;
import org.opentcs.components.kernel.services.VehicleService;
import org.opentcs.data.model.Vehicle;
import org.opentcs.kc.common.Package; import org.opentcs.kc.common.Package;
import org.opentcs.kc.common.byteutils.ByteUtil; import org.opentcs.kc.common.byteutils.ByteUtil;
import org.opentcs.kc.syn.AsyncFuture; import org.opentcs.kc.syn.AsyncFuture;
@ -21,192 +28,265 @@ import org.opentcs.kc.udp.agv.param.rsp.RcvEventPackage;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2022/1/15 13:01
*/
public enum UDPClient { public enum UDPClient {
//如果要配置多个链接 local1 local2 .... 这样排下去好了 //如果要配置多个链接 local1 local2 .... 这样排下去好了
//localAGV("agv1","192.168.0.211",17804,55678), localAGV("50", "192.168.124.124", 17804, 55678),
localAGV("agv1","127.0.0.1",17804,55678), ;
;
private String name; // 服务端用户名+密码+地址+端口
private String host; private String SERVICE_USER = GuestUserCredentials.USER;
//默认 0 port private String SERVICE_PWD = GuestUserCredentials.PASSWORD;
private Integer port; private String SERVICE_HOST = GuestUserCredentials.IP;
private Integer bindPort; private Integer SERVICE_PORT = GuestUserCredentials.PORT;
private Bootstrap bootstrap;
private NioEventLoopGroup group; private String name;
private String host;
//默认 0 port
private Integer port;
private Integer bindPort;
private Bootstrap bootstrap;
private NioEventLoopGroup group;
private Channel conn; private Channel conn;
private boolean isOnline; private boolean isOnline;
private static final Logger logger = LoggerFactory.getLogger("AGVLOGGER"); // private static final Logger logger = LoggerFactory.getLogger("AGVLOGGER");
private static final Logger logger = LoggerFactory.getLogger(UDPClient.class);
// ==== 连接节点配置信息 ===== 开始
// ==== 连接节点配置信息 ===== 结束
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;
}
UDPClient() { /**
* 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);
}
}
//coreSize 是线程池的数量 public void subscribeKC(RcvEventPackage rcv, byte[] body) {
UDPClient(String name, String host, Integer port, Integer bindPort) { if (rcv.isOk()) {
this.name = name; KernelServicePortal servicePortal = new KernelServicePortalBuilder(
this.host = host; SERVICE_USER, SERVICE_PWD
this.port = port; ).build();
this.conn = null; servicePortal.login(SERVICE_HOST, SERVICE_PORT);
this.bindPort = bindPort; VehicleService vehicleService = servicePortal.getVehicleService();
initClient(); Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, name);
vehicleService.sendCommAdapterMessage(vehicle.getReference(), body);
servicePortal.logout();
}
else {
System.out.println();
System.out.println("subscribe received transationId : " + "isok:" + rcv.isOk());
}
}
public void subscribe0xB0(RcvEventPackage rcv, byte[] body) {
if (rcv.isOk()) {
this.achieveSub0xB0(body);
}
else {
System.out.println();
System.out.println("subscribe0xB0 received transationId : " + "isok:" + rcv.isOk());
} }
}
public String getHost(){ public void subscribe0xAF(RcvEventPackage rcv, byte[] body) {
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;
}
/**
* --线程安全的--
* if (rcv.isOk()) {
* 如果返回null就代表出现了异常并且尝试了 retryMax 次数并且尝试重置连接 this.achieveSub0xAF(body);
* */ }
public RcvEventPackage send(AgvEvent event) { else {
try { System.out.println();
//sendread 报文 System.out.println("subscribe0xAF received transationId : " + "isok:" + rcv.isOk());
//应该是ReadRequestFrame 继承 Packet 然后直接 Tio.bSend(this.conn, ReadRequestFrame) }
}
Package mbPackage = event.toBytes(); /**
AsyncFuture<Package> add = SendedList.add(mbPackage.getTransationId(),null); * 0xAF上报信息传入通讯适配器
*
* @param body 响应数据
*/
private void achieveSub0xAF(byte[] body) {
// QueryRobotStatusRsp queryRobotStatusRsp = new QueryRobotStatusRsp(rcv.getDataBytes());
KernelServicePortal servicePortal = new KernelServicePortalBuilder(
SERVICE_USER, SERVICE_PWD
).build();
servicePortal.login(SERVICE_HOST, SERVICE_PORT);
VehicleService vehicleService = servicePortal.getVehicleService();
Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, name);
vehicleService.sendCommAdapterMessage(vehicle.getReference(), body);
}
this.conn.writeAndFlush( /**
new DatagramPacket( * 0xB0上报信息传入通讯适配器
byteArrayToByteBuf(mbPackage.getBody()), *
new InetSocketAddress(this.host,this.port))) * @param body 响应数据
.sync(); */
private void achieveSub0xB0(byte[] body) {
Package aPackage = add.get(5000L, mbPackage.getTransationId()); // QueryCargoStatusRsp queryCargoStatusRsp = new QueryCargoStatusRsp(rcv.getDataBytes());
byte[] body = aPackage.getBody(); // System.out.println();
// System.out.println("received subscribe 0xB0 List : " + "isok:" + rcv.isOk());
// for (byte b : rcv.getValue()) {
// System.out.print(byteToHex(b) + " ");
// }
String errMsg = " [ AGVclient - send success ] [ "+name+" host: "+ this.host +" ]"+event.toString(); KernelServicePortal servicePortal = new KernelServicePortalBuilder(
logger.info(errMsg); SERVICE_USER, SERVICE_PWD
//注意这里的body 是整个 response结构包括 : 授权码 + header + body ).build();
return new RcvEventPackage(body[22],body); servicePortal.login(SERVICE_HOST, SERVICE_PORT);
}catch (Throwable e) { VehicleService vehicleService = servicePortal.getVehicleService();
//e.printStackTrace(); Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, name);
String errMsg = " [ AGVclient - Read ] [ "+name+" host: "+ this.host +" ] ( occur err ) errTime: "+" ; send errMsg : "+e.getMessage()+" ; event :"+event.toString();
logger.info(errMsg); if (vehicle == null) {
throw new RuntimeException(errMsg); throw new RuntimeException("vehicle name:" + name + ",does not exist");
}
} }
public void subscribe0xB0(RcvEventPackage rcv){ //将AGV控制器上报信息传入通讯适配器
if(name.equals("agv1")){ vehicleService.sendCommAdapterMessage(vehicle.getReference(), body);
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")){
//....
}
} servicePortal.logout();
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 这里要做以区分 public static void printInfo(AgvEvent agvEvent) {
System.out.println("sended transationId : " + agvEvent.getTransationIdString());
pipeline.addLast(new AgvUdpChannelInitializer(UDPClient.this)); for (byte b : agvEvent.toBytes().getBody()) {
} System.out.print(byteToHex(b) + " ");
});
try {
this.conn = this.bootstrap.bind(this.bindPort).sync().channel();
}catch (Exception e){
logger.info("AGV UDP Initial Exception : "+e.getMessage());
}
} }
}
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());
}
}
} }

View File

@ -9,11 +9,18 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import java.util.HashMap;
import java.util.Set;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.opentcs.access.KernelServicePortal;
import org.opentcs.access.rmi.KernelServicePortalBuilder;
import org.opentcs.components.kernel.services.PlantModelService;
import org.opentcs.components.kernel.services.VehicleService;
import org.opentcs.data.model.Vehicle;
/** /**
* Tests for {@link SameThreadExecutorService}. * Tests for {@link SameThreadExecutorService}.
@ -79,4 +86,46 @@ class SameThreadExecutorServiceTest {
verify(task).call(); verify(task).call();
} }
/*
* @Test
* void testVehicle() {
* //获取车辆对象代码
* KernelServicePortal servicePortal = new KernelServicePortalBuilder(
* GuestUserCredentials.USER, GuestUserCredentials.PASSWORD
* ).build();
* servicePortal.login(GuestUserCredentials.IP, GuestUserCredentials.PORT);
* // servicePortal.getPlantModelService().up
* PlantModelService plantModelService = servicePortal.getPlantModelService();
* VehicleService vehicleService = servicePortal.getVehicleService();
* Set<Vehicle> vehicles = vehicleService.fetchObjects(Vehicle.class);
* for (Vehicle vehicle : vehicles) {
* System.out.println("vehicle:" + vehicle);
* }
* }
*/
@Test
void testRemoveVehicle() {
HashMap<String, Object> map = new HashMap<>();
int energy = 52;
map.put("energy", energy);
long positionX = 3040;
map.put("positionX", positionX);
long positionY = -40;
map.put("positionY", positionY);
double positionAngle = 0.0;
map.put("positionAngle", positionAngle);
//向车辆对应的通讯适配器发送消息
KernelServicePortal servicePortal = new KernelServicePortalBuilder(
GuestUserCredentials.USER, GuestUserCredentials.PASSWORD
).build();
servicePortal.login(GuestUserCredentials.IP, GuestUserCredentials.PORT);
VehicleService vehicleService = servicePortal.getVehicleService();
Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, "50");
vehicleService.sendCommAdapterMessage(vehicle.getReference(), map);
}
} }

View File

@ -39,6 +39,10 @@ public class DefaultPropertySuggestions
keySuggestions.add(LoopbackAdapterConstants.PROPKEY_ACCELERATION); keySuggestions.add(LoopbackAdapterConstants.PROPKEY_ACCELERATION);
keySuggestions.add(LoopbackAdapterConstants.PROPKEY_DECELERATION); keySuggestions.add(LoopbackAdapterConstants.PROPKEY_DECELERATION);
keySuggestions.add(ObjectPropConstants.VEHICLE_DATA_TRANSFORMER); keySuggestions.add(ObjectPropConstants.VEHICLE_DATA_TRANSFORMER);
keySuggestions.add(LoopbackAdapterConstants.AGV_AUTHORIZE_CODE);
keySuggestions.add(LoopbackAdapterConstants.AGV_IP);
keySuggestions.add(LoopbackAdapterConstants.AGV_PORT);
keySuggestions.add(LoopbackAdapterConstants.POINT_TYPE);
} }
@Override @Override