update vehicle adapter

This commit is contained in:
wait 2025-04-18 11:06:14 +08:00
parent 17c28a5a7f
commit 4882a28c0a
81 changed files with 4660 additions and 4635 deletions

View File

@ -1,40 +0,0 @@
package org.opentcs.kcvehicle;
import com.google.inject.assistedinject.FactoryModuleBuilder;
import org.opentcs.customizations.kernel.KernelInjectionModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class KcCommAdapterModule extends KernelInjectionModule {
private static final Logger LOG = LoggerFactory.getLogger(KcCommAdapterModule.class);
@Override
protected void configure() {
//
// VirtualVehicleConfiguration configuration
// = getConfigBindingProvider().get(
// VirtualVehicleConfiguration.PREFIX,
// VirtualVehicleConfiguration.class
// );
//
//// KcVehicleConfiguration configuration
//// = getConfigBindingProvider().get(
//// KcVehicleConfiguration.PREFIX,
//// KcVehicleConfiguration.class
//// );
//
// if (!configuration.enable()) {
// LOG.info("KC driver disabled by configuration.");
// return;
// }
//
// bind(VirtualVehicleConfiguration.class)
// .toInstance(configuration);
//
// install(new FactoryModuleBuilder().build(LoopbackAdapterComponentsFactory.class));
//
// vehicleCommAdaptersBinder().addBinding().to(LoopbackCommunicationAdapterFactory.class);
}
}

View File

@ -2,4 +2,3 @@
# SPDX-License-Identifier: MIT
org.opentcs.virtualvehicle.LoopbackCommAdapterModule
org.opentcs.kcvehicle.KcCommAdapterModule

View File

@ -1,10 +0,0 @@
package org.opentcs.kcvehicle;
import org.opentcs.data.model.Vehicle;
import org.opentcs.kcvehicle.KcCommunicationAdapter;
public interface KcAdapterComponentsFactory {
// KcCommunicationAdapter createKcCommunicationAdapter(Vehicle vehicle);
KcCommunicationAdapter createKcCommunicationAdapter(Vehicle vehicle);
}

View File

@ -1,163 +0,0 @@
package org.opentcs.kcvehicle;
import com.google.inject.assistedinject.Assisted;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import jakarta.inject.Inject;
import java.util.concurrent.ScheduledExecutorService;
import org.opentcs.customizations.kernel.KernelExecutor;
import org.opentcs.data.model.Vehicle;
import org.opentcs.data.order.TransportOrder;
import org.opentcs.drivers.vehicle.BasicVehicleCommAdapter;
import org.opentcs.drivers.vehicle.MovementCommand;
import org.opentcs.drivers.vehicle.VehicleProcessModel;
import org.opentcs.kc.udp.Service.ConfirmRelocation;
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.x17.QueryRobotRunStatusRsp;
import org.opentcs.util.ExplainedBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class KcCommunicationAdapter extends BasicVehicleCommAdapter {
private static final Logger LOG = LoggerFactory.getLogger(KcCommunicationAdapter.class);
/**
* This instance's configuration.
*/
private final KcVehicleConfiguration configuration;
/**
* 单个仿真步骤的时间 毫秒
*/
private static final int SIMULATION_PERIOD = 100;
/**
* Creates a new instance.
*/
@Inject
public KcCommunicationAdapter(
KcVehicleConfiguration configuration,
@Assisted
Vehicle vehicle,
@KernelExecutor
ScheduledExecutorService kernelExecutor
) {
super(
new VehicleProcessModel(vehicle),
configuration.commandQueueCapacity(),
configuration.rechargeOperation(),
kernelExecutor
);
this.configuration = configuration;
}
// @Inject
// public KcCommunicationAdapter(Vehicle vehicle) {
// super(new VehicleProcessModel(vehicle), 1, "Recharge");
// }
@Override
public void sendCommand(MovementCommand cmd)
throws IllegalArgumentException {
}
@Override
protected void connectVehicle() {
//initAGV();
getProcessModel().setCommAdapterConnected(true);
}
@Override
protected void disconnectVehicle() {
getProcessModel().setCommAdapterConnected(false);
}
@Override
protected boolean isVehicleConnected() {
return getProcessModel().isCommAdapterConnected();
}
@Nonnull
@Override
public ExplainedBoolean canProcess(
@Nonnull
TransportOrder order
) {
return null;
}
@Override
public void onVehiclePaused(boolean paused) {
}
@Override
public void processMessage(
@Nullable
Object message
) {
}
/**
* 初始化AGV
* 步骤:
* 1调度软件启动机器人启动无顺序要求
* 2等待调度系统以及机器人控制器启动完成调度系统启动即向机器人发送状态查询查询成功即为启动完成
* 3调度软件发送订阅信令至机器人表明订阅机器人状态信息或载货状态机器人接收到订阅信令会依据订阅要求推送订阅信息度软件需要根据订阅信令中上报持续时间提前刷新机器人推送的上报持续时间
* 4调度软件持续监控机器人实时状态
* 5导航初始化
*/
private void initAGV() {
//0xAF获取AGV状态
QueryRobotStatusRsp qryRobotStatusRsp = QryRobotStatus.command();
//开启AGV订阅监听 (0xAF & 0xB0)
SubRobotStatue.command();
SubCargoStatus.command();
//1 切换定位为手动模式命令码0x03变量NaviControl 修改为0
SwitchManualMode.command();
LocationStatusInfo locationStatusInfo = qryRobotStatusRsp.locationStatusInfo;
double agvX = locationStatusInfo.globalX;
double agvY = locationStatusInfo.globalY;
double agvAngle = locationStatusInfo.absoluteDirecAngle;
//3 查询机器人运行状态命令码0x17等待机器人定位状态为定位完成
QueryRobotRunStatusRsp qryRobotRunStatusRsp = QryRobotRunStatus.command();
if (qryRobotRunStatusRsp.robotLocalizationState == 0) {
//2 执行机器人手动定位 命令码0x14
ManualPosition.command(agvX, agvY, agvAngle);
throw new RuntimeException("AGV定位失败执行手动定位中");
} else if (qryRobotRunStatusRsp.robotLocalizationState == 2) {
throw new RuntimeException("AGV定位中");
}
//4 确认初始位置命令码0x1F
ConfirmRelocation.commnd();
//5 切换成自动模式命令码0x03变量NaviControl 修改为1
SwitchAutomaticMode.command();
//打开通讯适配器连接
getProcessModel().setCommAdapterConnected(true);
}
private int getSimulationTimeStep() {
return (int) (SIMULATION_PERIOD * configuration.simulationTimeFactor());
}
}

View File

@ -1,15 +0,0 @@
package org.opentcs.kcvehicle;
import org.opentcs.drivers.vehicle.VehicleCommAdapterDescription;
public class KcCommunicationAdapterDescription extends VehicleCommAdapterDescription {
@Override
public String getDescription() {
return "KC_ADAPTER";
}
@Override
public boolean isSimVehicleCommAdapter() {
return false;
}
}

View File

@ -1,61 +0,0 @@
package org.opentcs.kcvehicle;
import static java.util.Objects.requireNonNull;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import jakarta.inject.Inject;
import org.opentcs.data.model.Vehicle;
import org.opentcs.drivers.vehicle.VehicleCommAdapter;
import org.opentcs.drivers.vehicle.VehicleCommAdapterDescription;
import org.opentcs.drivers.vehicle.VehicleCommAdapterFactory;
import org.opentcs.drivers.vehicle.VehicleProcessModel;
public class KcCommunicationAdapterFactory implements VehicleCommAdapterFactory {
private final KcAdapterComponentsFactory adapterFactory;
@Inject
public KcCommunicationAdapterFactory(KcAdapterComponentsFactory componentsFactory) {
this.adapterFactory = requireNonNull(componentsFactory, "KC_componentsFactory_NULL");
}
@Override
public VehicleCommAdapterDescription getDescription() {
return new KcCommunicationAdapterDescription();
}
@Override
public boolean providesAdapterFor(
@Nonnull
Vehicle vehicle
) {
return false;
}
@Nullable
@Override
public VehicleCommAdapter getAdapterFor(
@Nonnull
Vehicle vehicle
) {
return adapterFactory.createKcCommunicationAdapter(vehicle);
// return adapterFactory.createKcCommunicationAdapter(vehicle);
}
@Override
public void initialize() {
}
@Override
public boolean isInitialized() {
return false;
}
@Override
public void terminate() {
}
}

View File

@ -1,77 +0,0 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kcvehicle;
import org.opentcs.configuration.ConfigurationEntry;
import org.opentcs.configuration.ConfigurationPrefix;
/**
* Provides methods to configure to {@link KcCommunicationAdapter}.
*/
@ConfigurationPrefix(KcVehicleConfiguration.PREFIX)
public interface KcVehicleConfiguration {
/**
* This configuration's prefix.
*/
String PREFIX = "kcvehicle";
@ConfigurationEntry(
type = "Boolean",
description = "Whether to enable to register/enable the loopback driver.",
changesApplied = ConfigurationEntry.ChangesApplied.ON_APPLICATION_START,
orderKey = "0_enable"
)
boolean enable();
@ConfigurationEntry(
type = "Integer",
description = "The adapter's command queue capacity.",
changesApplied = ConfigurationEntry.ChangesApplied.ON_NEW_PLANT_MODEL,
orderKey = "1_attributes_1"
)
int commandQueueCapacity();
@ConfigurationEntry(
type = "String",
description = "The string to be treated as a recharge operation.",
changesApplied = ConfigurationEntry.ChangesApplied.ON_NEW_PLANT_MODEL,
orderKey = "1_attributes_2"
)
String rechargeOperation();
@ConfigurationEntry(
type = "Double",
description = "The rate at which the vehicle recharges in percent per second.",
changesApplied = ConfigurationEntry.ChangesApplied.INSTANTLY,
orderKey = "1_attributes_3"
)
double rechargePercentagePerSecond();
@ConfigurationEntry(
type = "Double",
description = {
"The simulation time factor.",
"1.0 is real time, greater values speed up simulation."
},
changesApplied = ConfigurationEntry.ChangesApplied.INSTANTLY,
orderKey = "2_behaviour_1"
)
double simulationTimeFactor();
@ConfigurationEntry(
type = "Integer",
description = {"The virtual vehicle's length in mm when it's loaded."},
changesApplied = ConfigurationEntry.ChangesApplied.INSTANTLY,
orderKey = "2_behaviour_2"
)
int vehicleLengthLoaded();
@ConfigurationEntry(
type = "Integer",
description = {"The virtual vehicle's length in mm when it's unloaded."},
changesApplied = ConfigurationEntry.ChangesApplied.INSTANTLY,
orderKey = "2_behaviour_3"
)
int vehicleLengthUnloaded();
}

View File

@ -7,6 +7,7 @@ import static java.util.Objects.requireNonNull;
import com.google.inject.assistedinject.Assisted;
import jakarta.inject.Inject;
import java.beans.PropertyChangeEvent;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
@ -14,11 +15,14 @@ import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.opentcs.common.LoopbackAdapterConstants;
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;
@ -44,6 +48,7 @@ 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.virtualvehicle.VelocityController.WayEntry;
import org.slf4j.Logger;
@ -113,25 +118,32 @@ public class LoopbackCommunicationAdapter
*/
private boolean initialized;
/**
* 上报截止时间
* 0xAF上报截止时间.
*/
private long deadline;
private long sub0xafDeadline;
/**
* 上报间隔时间/ms
* 0xB0上报截止时间.
*/
private long intervalTime;
// /**
// * AGV IP
// */
// private final String IP;
// /**
// * AGV 端口
// */
// private final int PORT;
// /**
// * AGV AUTHORIZE_CODE
// */
// private final String AUTHORIZE_CODE;
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.
@ -193,41 +205,52 @@ public class LoopbackCommunicationAdapter
@Override
public void propertyChange(PropertyChangeEvent evt) {
//调用父类的 propertyChange 方法处理事件
super.propertyChange(evt);
//如果事件源不是 LoopbackVehicleModel 类型直接返回
if (!((evt.getSource()) instanceof LoopbackVehicleModel)) {
return;
}
//如果事件属性名为 LOAD_HANDLING_DEVICES
if (Objects.equals(
evt.getPropertyName(),
VehicleProcessModel.Attribute.LOAD_HANDLING_DEVICES.name()
)) {
if (!getProcessModel().getLoadHandlingDevices().isEmpty()
&& getProcessModel().getLoadHandlingDevices().get(0).isFull()) {
//检查负载处理设备是否为空且第一个设备是否满载更新负载状态为 FULL 并设置车辆长度为加载状态下的长度
loadState = LoadState.FULL;
getProcessModel().setBoundingBox(
getProcessModel().getBoundingBox().withLength(configuration.vehicleLengthLoaded())
);
}
else {
//否则更新负载状态为 EMPTY 并设置车辆长度为未加载状态下的长度
loadState = LoadState.EMPTY;
getProcessModel().setBoundingBox(
getProcessModel().getBoundingBox().withLength(configuration.vehicleLengthUnloaded())
);
}
}
//如果事件属性名为 SINGLE_STEP_MODE
if (Objects.equals(
evt.getPropertyName(),
LoopbackVehicleModel.Attribute.SINGLE_STEP_MODE.name()
)) {
// When switching from single step mode to automatic mode and there are commands to be
// processed, ensure that we start/continue processing them.
//如果单步模式关闭待处理命令队列非空且模拟未运行则启动车辆模拟
if (!getProcessModel().isSingleStepModeEnabled()
&& !getSentCommands().isEmpty()
&& !isSimulationRunning) {
//标记模拟正在运行
isSimulationRunning = true;
//提交任务到线程池执行队列中的第一个命令
((ExecutorService) getExecutor()).submit(
() -> startVehicleSimulation(getSentCommands().peek())
() -> startVehicle(getSentCommands().peek())
);
}
}
@ -261,6 +284,7 @@ public class LoopbackCommunicationAdapter
System.out.println(cmd);
System.out.println("send cmd print start");
// SubRobotStatue.command();
//订单ID
String orderName = cmd.getTransportOrder().getName();
@ -283,29 +307,40 @@ public class LoopbackCommunicationAdapter
System.out.println("sourcePoint:" + sourcePoint);
//下发终点
String destinationPoint = cmd.getStep().getDestinationPoint().getName();
System.out.println("destinationPoint:" + destinationPoint);
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);
//拼接起点终点字符串转为Integer类型获取唯一pathID
Integer pathID = Integer.parseInt(sourcePoint + destinationPoint);
System.out.println("pathID:" + pathID);
System.out.println("send cmd print end");
//订阅0xAF
// sub0xAF();
//AGV控制器执行命令
HybridNavigation.command("1", sourcePoint, destinationPoint, operation);
HybridNavigation.command(orderName, sourcePoint, destinationPointName, operation);
// Start the simulation task if we're not in single step mode and not simulating already.
//检查当前车辆模型是否处于单步模式且未运行若满足条件则设置运行状态为true
if (!getProcessModel().isSingleStepModeEnabled()
&& !isSimulationRunning) {
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()) {
((ExecutorService) getExecutor()).submit(() -> startVehicleSimulation(cmd));
((ExecutorService) getExecutor()).submit(() -> startVehicle(cmd));
}
else {
((ExecutorService) getExecutor()).submit(
() -> startVehicleSimulation(getSentCommands().peek())
() -> startVehicle(getSentCommands().peek())
);
}
}
@ -318,49 +353,13 @@ public class LoopbackCommunicationAdapter
@Override
public void processMessage(Object message) {
if (message instanceof String) {
//测试使用
getProcessModel().setEnergyLevel(66);
System.out.println(message);
}
else if (message instanceof QueryCargoStatusRsp) {
//0xB0响应结果
QueryCargoStatusRsp queryCargoStatusRsp = (QueryCargoStatusRsp) message;
if (queryCargoStatusRsp.isCargo == 0) {
this.loadState = LoadState.EMPTY;
} else {
this.loadState = LoadState.FULL;
}
}
else if (message instanceof QueryRobotStatusRsp) {
System.out.println("test success");
//0xAF响应结果
QueryRobotStatusRsp queryRobotStatusRsp = (QueryRobotStatusRsp) message;
//电量
float batteryPercentage = queryRobotStatusRsp.batteryStatusInfo.batteryPercentage;
getProcessModel().setEnergyLevel(((int)batteryPercentage * 100));
//充电状态
byte chargingState = queryRobotStatusRsp.batteryStatusInfo.chargingState;
if (chargingState == 1) {
getProcessModel().setState(Vehicle.State.CHARGING);
}
//设置车辆位置(最后一次通过的点)
// String lastPointName = queryRobotStatusRsp.locationStatusInfo.lastPassPointId.toString();
// getProcessModel().setPosition(lastPointName);
////设置车辆姿势(官方弃用设置车辆精确位置)
long positionX = (long)queryRobotStatusRsp.locationStatusInfo.globalX;
long positionY = (long)queryRobotStatusRsp.locationStatusInfo.globalY;
Triple triple = new Triple(positionX, positionY, 0);
double positionAngle = queryRobotStatusRsp.locationStatusInfo.absoluteDirecAngle;
getProcessModel().setPose(new Pose(triple, positionAngle));
if (message instanceof byte[]) {
updateVehicleModel(message);
}
else if (message instanceof HashMap<?, ?>) {
HashMap<?,?> msg = ((HashMap<?, ?>) message);
//todo 测试代码----成功
HashMap<?, ?> msg = (HashMap<?, ?>) message;
getProcessModel().setEnergyLevel((int) msg.get("energy"));
getProcessModel().setState(Vehicle.State.EXECUTING);
@ -371,21 +370,83 @@ public class LoopbackCommunicationAdapter
double positionAngle = (double) msg.get("positionAngle");
getProcessModel().setPose(new Pose(triple, positionAngle));
}
}
//上报自动续订操作
// renewalSubscribe();
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 renewalSubscribe() {
private void renewalSubscribe0xAF() {
Date now = new Date();
if (now.getTime() + intervalTime >= deadline) {
//开启AGV订阅监听 (0xAF & 0xB0)
SubRobotStatue.command();
SubCargoStatus.command();
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();
}
}
@ -446,11 +507,8 @@ public class LoopbackCommunicationAdapter
@Override
protected synchronized void connectVehicle() {
String ip = requireNonNull(vehicle.getProperties().get(LoopbackAdapterConstants.AGV_IP), "AGV IP NOT NULL");
Integer port = Integer.parseInt(requireNonNull(vehicle.getProperties().get(LoopbackAdapterConstants.AGV_PORT), "AGV PORT NOT NULL"));
String authorizeCode = requireNonNull(vehicle.getProperties().get(LoopbackAdapterConstants.AGV_AUTHORIZE_CODE), "AGV AUTHORIZE_CODE NOT NULL");
getProcessModel().setCommAdapterConnected(true);
// initAGV();
// getProcessModel().setCommAdapterConnected(true);
initAGV();
}
@Override
@ -491,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) {
LOG.debug("Starting vehicle simulation for command: {}", command);
Step step = command.getStep();
@ -529,12 +617,54 @@ public class LoopbackCommunicationAdapter
: 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.
*
* @param command The command to simulate.
*/
private void movementSimulation(MovementCommand command) {
//检查当前车辆模型的速度控制器是否有路径条目若无则直接返回
if (!getProcessModel().getVelocityController().hasWayEntries()) {
return;
}
@ -543,7 +673,9 @@ public class LoopbackCommunicationAdapter
getProcessModel().getVelocityController().advanceTime(getSimulationTimeStep());
WayEntry currentWayEntry = getProcessModel().getVelocityController().getCurrentWayEntry();
//if we are still on the same way entry then reschedule to do it again
//获取当前路径条目并推进时间步长检查是否仍处于同一路径条目
if (prevWayEntry == currentWayEntry) {
//若是则重新调度当前方法以继续模拟
getExecutor().schedule(
() -> movementSimulation(command),
SIMULATION_PERIOD,
@ -553,6 +685,7 @@ public class LoopbackCommunicationAdapter
else {
//if the way enties are different then we have finished this step
//and we can move on.
//若否更新车辆位置为上一路径条目的目标点并根据命令是否有操作决定进入操作模拟或完成命令并模拟下一个命令
getProcessModel().setPosition(prevWayEntry.getDestPointName());
LOG.debug("Movement simulation finished.");
if (!command.hasEmptyOperation()) {
@ -570,8 +703,18 @@ public class LoopbackCommunicationAdapter
}
}
/**
* 执行移动命令的操作部分
*
* @param command 要执行的命令
*/
private void operationExec(MovementCommand command) {
}
/**
* Simulate the operation part of a movement command.
* 模拟移动命令的操作部分
*
* @param command The command to simulate.
* @param timePassed The amount of time passed since starting the simulation.
@ -626,6 +769,7 @@ public class LoopbackCommunicationAdapter
/**
* Simulate recharging the vehicle.
* 模拟为车辆充电
*
* @param rechargePosition The vehicle position where the recharge simulation was started.
* @param rechargePercentage The recharge percentage of the vehicle while it is charging.
@ -671,14 +815,43 @@ public class LoopbackCommunicationAdapter
+ (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) {
//Set the vehicle state to idle
if (getSentCommands().size() <= 1 && getUnsentCommands().isEmpty()) {
getProcessModel().setState(Vehicle.State.IDLE);
}
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());
// HybridNavigation.delUniqueOrderID(command);
}
else {
LOG.warn(
@ -690,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() {
if (getSentCommands().isEmpty() || getProcessModel().isSingleStepModeEnabled()) {
LOG.debug("Vehicle simulation is done.");
@ -727,47 +916,133 @@ public class LoopbackCommunicationAdapter
*/
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");
}
//设置订阅时间和上报间隔
Date now = new Date();
deadline = now.getTime() + 1000;
intervalTime = 100;
//开启0xAF订阅
sub0xAF();
//开启AGV订阅监听 (0xAF & 0xB0)
SubRobotStatue.command();
SubCargoStatus.command();
//开启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;
//3 查询机器人运行状态命令码0x17等待机器人定位状态为定位完成
QueryRobotRunStatusRsp qryRobotRunStatusRsp = QryRobotRunStatus.command();
if (qryRobotRunStatusRsp.robotLocalizationState == 0) {
//2 执行机器人手动定位 命令码0x14
ManualPosition.command(agvX, agvY, agvAngle);
throw new RuntimeException("AGV定位失败执行手动定位中");
//3 查询机器人运行状态命令码0x17等待机器人定位状态为定位完成
System.out.println("=================---initAGV_0x17");
QueryRobotRunStatusRsp qryRobotRunStatusRsp = QryRobotRunStatus.command();
} else if (qryRobotRunStatusRsp.robotLocalizationState == 2) {
throw new RuntimeException("AGV定位中");
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;
}
/**
* 设置单步模式已启用.
* @param singleStepModeEnabled 启用单步模式
* @return LoopbackVehicleModelTO
*/
public LoopbackVehicleModelTO setSingleStepModeEnabled(boolean singleStepModeEnabled) {
this.singleStepModeEnabled = singleStepModeEnabled;
return this;
@ -67,6 +72,11 @@ public class LoopbackVehicleModelTO
return loadOperation;
}
/**
* 设置装载操作.
* @param loadOperation 状态
* @return LoopbackVehicleModelTO
*/
public LoopbackVehicleModelTO setLoadOperation(String loadOperation) {
this.loadOperation = loadOperation;
return this;
@ -76,6 +86,11 @@ public class LoopbackVehicleModelTO
return unloadOperation;
}
/**
* 设置卸载操作.
* @param unloadOperation 状态
* @return LoopbackVehicleModelTO
*/
public LoopbackVehicleModelTO setUnloadOperation(String unloadOperation) {
this.unloadOperation = unloadOperation;
return this;
@ -85,6 +100,11 @@ public class LoopbackVehicleModelTO
return operatingTime;
}
/**
* 设置运行时间.
* @param operatingTime 时间戳
* @return LoopbackVehicleModelTO
*/
public LoopbackVehicleModelTO setOperatingTime(int operatingTime) {
this.operatingTime = operatingTime;
return this;
@ -94,6 +114,11 @@ public class LoopbackVehicleModelTO
return maxAcceleration;
}
/**
* 设置最大加速度.
* @param maxAcceleration
* @return LoopbackVehicleModelTO
*/
public LoopbackVehicleModelTO setMaxAcceleration(int maxAcceleration) {
this.maxAcceleration = maxAcceleration;
return this;
@ -103,6 +128,11 @@ public class LoopbackVehicleModelTO
return maxDeceleration;
}
/**
* 设置最大减速.
* @param maxDeceleration
* @return LoopbackVehicleModelTO
*/
public LoopbackVehicleModelTO setMaxDeceleration(int maxDeceleration) {
this.maxDeceleration = maxDeceleration;
return this;
@ -112,6 +142,11 @@ public class LoopbackVehicleModelTO
return maxFwdVelocity;
}
/**
* 设置最大前驱速度.
* @param maxFwdVelocity
* @return LoopbackVehicleModelTO
*/
public LoopbackVehicleModelTO setMaxFwdVelocity(int maxFwdVelocity) {
this.maxFwdVelocity = maxFwdVelocity;
return this;
@ -121,6 +156,11 @@ public class LoopbackVehicleModelTO
return maxRevVelocity;
}
/**
* 设置最大速度.
* @param maxRevVelocity
* @return LoopbackVehicleModelTO
*/
public LoopbackVehicleModelTO setMaxRevVelocity(int maxRevVelocity) {
this.maxRevVelocity = maxRevVelocity;
return this;
@ -130,6 +170,11 @@ public class LoopbackVehicleModelTO
return vehiclePaused;
}
/**
* 将车辆设置为暂停.
* @param vehiclePaused
* @return LoopbackVehicleModelTO
*/
public LoopbackVehicleModelTO setVehiclePaused(boolean vehiclePaused) {
this.vehiclePaused = vehiclePaused;
return this;

View File

@ -18,7 +18,7 @@ public interface GuestUserCredentials {
/**
* 主机IP
*/
String IP = "192.168.0.106";
String IP = "192.168.124.111";
/**
* 内核开放端口
*/

View File

@ -53,5 +53,9 @@ public interface LoopbackAdapterConstants {
* AGV 端口
*/
String AGV_PORT = "AGV:PORT";
/**
* 定制点位类型1=WMS请求点位
*/
String POINT_TYPE = "tcs:point";
}

View File

@ -56,18 +56,19 @@ public class CaffeineUtil {
}
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 {
}
else {
if (uuid.get() >= 32000) {
cacheUUID.put("UUID", new AtomicInteger(1));
return ByteUtils.intToBytes(1);
}else {
}
else {
return ByteUtils.intToBytes(uuid.incrementAndGet());
}
}
@ -79,11 +80,13 @@ public class CaffeineUtil {
//transationId 从1 开始0留给心跳变量这样就固定报文了
cacheUUID.put("AGVUUID", new AtomicInteger(1));
return ByteUtils.intToBytesS(1);
}else {
}
else {
if (agvuuid.get() >= 32000) {
cacheUUID.put("AGVUUID", new AtomicInteger(1));
return ByteUtils.intToBytesS(1);
}else {
}
else {
return ByteUtils.intToBytesS(agvuuid.incrementAndGet());
}
}

View File

@ -12,7 +12,8 @@ public interface Const {
public static final String SERVER = "127.0.0.1";
public static void main(String[] args) {
String s = "123, 34, 104, 101, 97, 100, 101, 114, 34, 58, 123, 34, 116, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 73, 100, 34, 58, 34, 50, 48, 50, 52, 48, 49, 48, 50, 49, 53, 52, 48, 50, 55, 95, 51, 100, 56, 49, 99, 34, 44, 34, 109, 101, 115, 115, 97, 103, 101, 84, 121, 112, 101, 34, 58, 51, 44, 34, 109, 101, 115, 115, 97, 103, 101, 78, 97, 109, 101, 34, 58, 34, 72, 101, 97, 114, 116, 66, 101, 97, 116, 34, 44, 34, 115, 101, 110, 100, 84, 105, 109, 101, 115, 116, 97, 109, 112, 34, 58, 34, 50, 48, 50, 52, 45, 48, 49, 45, 48, 50, 32, 49, 53, 58, 52, 48, 58, 50, 55, 34, 125, 44, 34, 98, 111, 100, 121, 34, 58, 34, 49, 34, 44, 34, 114, 101, 116, 117, 114, 110, 115, 34, 58, 110, 117, 108, 108, 125";
String s
= "123, 34, 104, 101, 97, 100, 101, 114, 34, 58, 123, 34, 116, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 73, 100, 34, 58, 34, 50, 48, 50, 52, 48, 49, 48, 50, 49, 53, 52, 48, 50, 55, 95, 51, 100, 56, 49, 99, 34, 44, 34, 109, 101, 115, 115, 97, 103, 101, 84, 121, 112, 101, 34, 58, 51, 44, 34, 109, 101, 115, 115, 97, 103, 101, 78, 97, 109, 101, 34, 58, 34, 72, 101, 97, 114, 116, 66, 101, 97, 116, 34, 44, 34, 115, 101, 110, 100, 84, 105, 109, 101, 115, 116, 97, 109, 112, 34, 58, 34, 50, 48, 50, 52, 45, 48, 49, 45, 48, 50, 32, 49, 53, 58, 52, 48, 58, 50, 55, 34, 125, 44, 34, 98, 111, 100, 121, 34, 58, 34, 49, 34, 44, 34, 114, 101, 116, 117, 114, 110, 115, 34, 58, 110, 117, 108, 108, 125";
String[] split = s.split(",");
System.out.println(split.length);
}

View File

@ -3,11 +3,6 @@ package org.opentcs.kc.common;
import java.util.Arrays;
/**
* @Desc: "通用的数据传输底层类"
* @Author: caixiang
* @DATE: 2022/10/18 16:22
*/
public class Package {
private byte[] body;

View File

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

View File

@ -1,11 +1,6 @@
package org.opentcs.kc.common;
/**
* @Desc: "通用的数据传输底层类"
* @Author: caixiang
* @DATE: 2022/10/18 16:22
*/
public class RcvPackage {
private boolean isOk;
@ -17,6 +12,7 @@ public class RcvPackage {
this.value = value;
this.content = content;
}
public RcvPackage() {
}
@ -37,7 +33,6 @@ public class RcvPackage {
}
@Override
public String toString() {
return "RcvPackage{" +

View File

@ -6,16 +6,8 @@ import java.time.format.DateTimeFormatter;
import java.util.Random;
import java.util.UUID;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2022/10/24 16:27
*/
public class Utils {
/**
* java生成随机数字15位数
* @return
*/
public static String getTransationId() {
String val = String.valueOf(System.currentTimeMillis());
Random random = new Random();
@ -31,18 +23,15 @@ public class Utils {
*
* @return
*/
public static String getUUID(int len)
{
if(0 >= len)
{
public static String getUUID(int len) {
if (0 >= len) {
return null;
}
String uuid = getUUID();
StringBuffer str = new StringBuffer();
for (int i = 0; i < len; i++)
{
for (int i = 0; i < len; i++) {
str.append(uuid.charAt(i));
}
@ -55,23 +44,21 @@ public class Utils {
*
* @return
*/
public static String getUUID()
{
public static String getUUID() {
return UUID.randomUUID().toString().replace("-", "");
}
/*
type:
1.返回的是这种格式2021-08-16 15:00:05
2.返回的是这种格式20210816150021
* 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 {
}
else {
return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
}
}

View File

@ -4,11 +4,6 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2022/4/1 17:32
*/
// 对数字和字节进行转换 假设数据存储是以大端模式存储的
// byte: 字节类型 占8位二进制 00000000
// char: 字符类型 占2个字节 16位二进制 byte[0] byte[1]
@ -52,7 +47,7 @@ public class ByteUtil {
/**
*
* 字符串转成16个字节的byte[] 不足16个字节前面填充0
* */
*/
public static byte[] stringTo16Byte(String input) {
byte[] originalBytes = input.getBytes(StandardCharsets.UTF_8);
ByteBuffer buffer = ByteBuffer.allocate(16);
@ -84,7 +79,8 @@ public class ByteUtil {
if (ByteOrder.LITTLE_ENDIAN == byteOrder) {
//小端模式数据的高字节保存在内存的高地址中而数据的低字节保存在内存的低地址中
return (short) (bytes[0] & 0xff | (bytes[1] & 0xff) << Byte.SIZE);
} else {
}
else {
return (short) (bytes[1] & 0xff | (bytes[0] & 0xff) << Byte.SIZE);
}
}
@ -113,7 +109,8 @@ public class ByteUtil {
if (ByteOrder.LITTLE_ENDIAN == byteOrder) {
b[0] = (byte) (shortValue & 0xff);
b[1] = (byte) ((shortValue >> Byte.SIZE) & 0xff);
} else {
}
else {
b[1] = (byte) (shortValue & 0xff);
b[0] = (byte) ((shortValue >> Byte.SIZE) & 0xff);
}
@ -145,7 +142,8 @@ public class ByteUtil {
(bytes[1] & 0xFF) << 8 | //
(bytes[2] & 0xFF) << 16 | //
(bytes[3] & 0xFF) << 24; //
} else {
}
else {
return bytes[3] & 0xFF | //
(bytes[2] & 0xFF) << 8 | //
(bytes[1] & 0xFF) << 16 | //
@ -183,7 +181,8 @@ public class ByteUtil {
(byte) ((intValue >> 24) & 0xFF) //
};
} else {
}
else {
return new byte[]{ //
(byte) ((intValue >> 24) & 0xFF), //
(byte) ((intValue >> 16) & 0xFF), //
@ -197,7 +196,8 @@ public class ByteUtil {
/**
* long转byte数组<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 longValue long值
* @return byte数组
@ -209,7 +209,8 @@ public class ByteUtil {
/**
* long转byte数组<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 longValue long值
* @param byteOrder 端序
@ -222,7 +223,8 @@ public class ByteUtil {
result[i] = (byte) (longValue & 0xFF);
longValue >>= Byte.SIZE;
}
} else {
}
else {
for (int i = (result.length - 1); i >= 0; i--) {
result[i] = (byte) (longValue & 0xFF);
longValue >>= Byte.SIZE;
@ -234,7 +236,8 @@ public class ByteUtil {
/**
* byte数组转long<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值
@ -246,7 +249,8 @@ public class ByteUtil {
/**
* byte数组转long<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数组
* @param byteOrder 端序
@ -259,7 +263,8 @@ public class ByteUtil {
values <<= Byte.SIZE;
values |= (bytes[i] & 0xff);
}
} else {
}
else {
for (int i = 0; i < Long.BYTES; i++) {
values <<= Byte.SIZE;
values |= (bytes[i] & 0xff);
@ -283,7 +288,8 @@ public class ByteUtil {
/**
* double转byte数组<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 doubleValue double值
* @param byteOrder 端序
@ -336,13 +342,17 @@ public class ByteUtil {
public static byte[] numberToBytes(Number number, ByteOrder byteOrder) {
if (number instanceof Double) {
return doubleToBytes((Double) number, byteOrder);
} else if (number instanceof Long) {
}
else if (number instanceof Long) {
return longToBytes((Long) number, byteOrder);
} else if (number instanceof Integer) {
}
else if (number instanceof Integer) {
return intToBytes((Integer) number, byteOrder);
} else if (number instanceof Short) {
}
else if (number instanceof Short) {
return shortToBytes((Short) number, byteOrder);
} else {
}
else {
return doubleToBytes(number.doubleValue(), byteOrder);
}
}

View File

@ -26,8 +26,10 @@ public class ByteUtils {
System.arraycopy(src, start, dest, 0, length);
return dest;
}
/**
* float 类型的数值按照小端模式转换为字节数组
*
* @param value 要转换的 float
* @return 转换后的字节数组
*/
@ -41,8 +43,10 @@ public class ByteUtils {
}
return bytes;
}
/**
* 将字节数组中从指定偏移位置开始的指定长度的字节转换为字符串
*
* @param src 字节数组
* @param offset 起始偏移量
* @param length 要转换的字节长度
@ -64,7 +68,8 @@ public class ByteUtils {
System.arraycopy(src, offset, subArray, 0, length);
// 使用 UTF-8 编码将字节数组转换为字符串
return new String(subArray, "UTF-8");
} catch (Exception e) {
}
catch (Exception e) {
e.printStackTrace();
return "";
}
@ -117,6 +122,7 @@ public class ByteUtils {
/**
* 将字节数组按照小端模式转换为 short 类型
*
* @param src 字节数组
* @param offset 起始偏移量
* @return 转换后的 short
@ -161,6 +167,7 @@ public class ByteUtils {
}
return invert(arr);
}
public static Boolean toBoolean(byte[] bytes) {
if (bytes.length == 0) {
return null;
@ -169,7 +176,8 @@ public class ByteUtils {
byte[] byteArrayAndInvert = toByteArrayAndInvert(bytes[0]);
if (byteArrayAndInvert[0] == 1) {
return true;
}else {
}
else {
return false;
}
}
@ -177,7 +185,8 @@ public class ByteUtils {
public static Boolean toBoolean(byte bytes) {
if (bytes == 1) {
return true;
}else {
}
else {
return false;
}
}
@ -187,7 +196,8 @@ public class ByteUtils {
Date date = null;
try {
date = dateFormat.parse(timeParam); // 指定日期
}catch (Exception e){
}
catch (Exception e) {
throw new ParseDataException("ByteUtils.addDate error:", e);
}
@ -214,13 +224,14 @@ public class ByteUtils {
/**
* 1个字节byte[] 转成有符号的Integer
* */
*/
public static Integer toInt(byte bytes) {
return Integer.valueOf(Byte.toString(bytes));
}
/**
* 1个字节byte[] 转成无符号的Integer
* */
*/
public static Integer toUInt(byte bytes) {
int i = Byte.toUnsignedInt(bytes);
return Integer.valueOf(i);
@ -230,7 +241,7 @@ public class ByteUtils {
/**
* 2个字节byte[] 转成有符号的Integer
* 默认大端
* */
*/
public static Integer toInt(byte byte1, byte byte2) {
byte[] bytes = new byte[2];
bytes[0] = byte1;
@ -238,10 +249,11 @@ public class ByteUtils {
return Integer.valueOf(Short.toString(ByteUtil.bytesToShort(bytes, ByteOrder.BIG_ENDIAN)));
}
/**
* 2个字节byte[] 转成无符号的Integer
* 默认大端
* */
*/
public static Integer toUInt(byte byte1, byte byte2) {
byte[] bytes = new byte[2];
bytes[0] = byte1;
@ -254,7 +266,7 @@ public class ByteUtils {
/**
* 4个字节byte[] 转成有符号的Integer
* 默认大端
* */
*/
public static Integer toInt(byte byte1, byte byte2, byte byte3, byte byte4) {
byte[] bytes = new byte[4];
bytes[0] = byte1;
@ -263,10 +275,11 @@ public class ByteUtils {
bytes[3] = byte4;
return ByteUtil.bytesToInt(bytes, ByteOrder.BIG_ENDIAN);
}
/**
* 4个字节byte[] 转成无符号的Long因为如果首位为1 Integer就不满足长度了
* 默认大端
* */
*/
public static Long toUInt(byte byte1, byte byte2, byte byte3, byte byte4) {
byte[] bytes = new byte[8];
bytes[0] = 0;
@ -285,15 +298,16 @@ public class ByteUtils {
*
* 4个字节byte[] 转成有符号的Double
* 默认大端
* */
*/
public static Float forReal(byte[] bytes) {
return Float.intBitsToFloat(toInt(bytes[0], bytes[1], bytes[2], bytes[3]));
}
/**
*
* 8个字节byte[] 转成有符号的Double
* 默认大端
* */
*/
public static Double forLReal(byte[] bytes) {
return ByteUtil.bytesToDouble(bytes, ByteOrder.BIG_ENDIAN);
}
@ -302,10 +316,11 @@ public class ByteUtils {
*
* 8个字节byte[] 转成有符号的Double
* 默认大端
* */
*/
public static Double lrealbytesToDouble(byte[] bytes) {
return ByteUtil.bytesToDouble(bytes, ByteOrder.BIG_ENDIAN);
}
public static List<Double> toLrealArray(byte[] bytes) {
List<Double> list = new ArrayList<>();
int i = 0;
@ -315,10 +330,12 @@ public class ByteUtils {
}
return list;
}
public static byte[] lrealToBytes(double doubleValue) {
return ByteUtil.doubleToBytes(doubleValue, ByteOrder.BIG_ENDIAN);
//return double2Bytes(doubleValue);
}
public static byte[] lrealArrayToBytes(List<Double> doubleValue) {
List<Byte> bytes = new ArrayList<>();
for (Double b : doubleValue) {
@ -344,6 +361,7 @@ public class ByteUtils {
}
return byteToChar(b);
}
public static Character toChar(byte b) {
return byteToChar(b);
}
@ -356,7 +374,7 @@ public class ByteUtils {
/**
* return null 代表返回传入参数不正确str 取的length太小
* */
*/
// public static String toStr(byte[] b) {
// Integer length = Byte.toUnsignedInt(b[1]);
// if(length>(b.length-2)){
@ -446,6 +464,7 @@ public class ByteUtils {
return array;
}
/**
* desc : 获取字节 最高位的符号位
* return
@ -518,8 +537,6 @@ public class ByteUtils {
//bool array to byte array
// public st atic List<Boolean> toBoolArray(byte[] b) throws UnsupportedEncodingException {
// List<Boolean> res = new ArrayList<>();
// for(int i=0;i<b.length;i++){
@ -547,7 +564,7 @@ public class ByteUtils {
//toWord
/**
* 默认word => 有符号的整形
* */
*/
public static List<Integer> toWordArray(byte[] b) {
List<Integer> res = new ArrayList<>();
int i = 0;
@ -563,7 +580,7 @@ public class ByteUtils {
//toDWord
/**
* 默认dword => 有符号的整形
* */
*/
public static List<Integer> toDWordArray(byte[] b) {
List<Integer> res = new ArrayList<>();
int i = 0;
@ -575,15 +592,17 @@ public class ByteUtils {
}
return res;
}
/**
*
* 4个字节byte[] 转成有符号的Double
* 默认大端
* */
*/
public static Float realbytesToFloat(byte[] bytes) {
return Float.intBitsToFloat(toInt(bytes[0], bytes[1], bytes[2], bytes[3]));
}
public static List<Float> toRealArray(byte[] bytes) {
List<Float> res = new ArrayList<>();
int i = 0;
@ -598,6 +617,7 @@ public class ByteUtils {
public static byte[] realToBytes(Float f) {
return invert(float2byte(f));
}
public static byte[] realArrayToBytes(List<Float> list) {
List<Byte> res = new ArrayList<>();
for (Float f : list) {
@ -650,7 +670,7 @@ public class ByteUtils {
//toUInt
/**
* USInt 无符号整形 1个字节 = Integer
* */
*/
public static List<Integer> toUSIntArray(byte[] b) {
List<Integer> res = new ArrayList<>();
for (int i = 0; i < b.length; i++) {
@ -658,9 +678,10 @@ public class ByteUtils {
}
return res;
}
/**
* UInt 无符号整形 2个字节 = Integer
* */
*/
public static List<Integer> toUIntArray(byte[] b) {
List<Integer> res = new ArrayList<>();
int i = 0;
@ -672,9 +693,10 @@ public class ByteUtils {
}
return res;
}
/**
* UDInt 无符号整形 4个字节 = Long
* */
*/
public static List<Long> toUDIntArray(byte[] b) {
List<Long> res = new ArrayList<>();
int i = 0;
@ -689,7 +711,7 @@ public class ByteUtils {
/**
* SInt 无符号整形 1个字节 = Integer
* */
*/
public static List<Integer> toSIntArray(byte[] b) {
List<Integer> res = new ArrayList<>();
for (int i = 0; i < b.length; i++) {
@ -697,9 +719,10 @@ public class ByteUtils {
}
return res;
}
/**
* Int 无符号整形 2个字节 = Integer
* */
*/
public static List<Integer> toIntArray(byte[] b) {
List<Integer> res = new ArrayList<>();
int i = 0;
@ -711,9 +734,10 @@ public class ByteUtils {
}
return res;
}
/**
* DInt 无符号整形 4个字节 = Integer
* */
*/
public static List<Integer> toDIntArray(byte[] b) {
List<Integer> res = new ArrayList<>();
int i = 0;
@ -729,16 +753,17 @@ public class ByteUtils {
/**
* sint(1个字节) = byte[]
* 默认大端模式
* */
*/
public static byte[] sintToBytes(Integer i) {
byte[] res = new byte[1];
res[0] = Byte.valueOf(i.toString());
return res;
}
/**
* sintArray(1个字节) = byte[]
*
* */
*/
public static byte[] sintArrayToBytes(int[] sintArray) {
byte[] res = new byte[sintArray.length];
for (int i = 0; i < sintArray.length; i++) {
@ -751,7 +776,7 @@ public class ByteUtils {
/**
* int(2个字节) = byte[]
* 默认大端模式
* */
*/
public static byte[] intToBytes(Integer i) {
Number shortNumber = Short.valueOf(i.toString());
return ByteUtil.numberToBytes(shortNumber, ByteOrder.BIG_ENDIAN);
@ -777,10 +802,9 @@ public class ByteUtils {
}
/**
* intArray(2个字节) = byte[]
* */
*/
public static byte[] intArrayToBytes(int[] intArray) {
byte[] res = new byte[intArray.length * 2];
for (int i = 0; i < intArray.length; i++) {
@ -794,7 +818,7 @@ public class ByteUtils {
/**
* int(2个字节) = byte[]
* 默认大端模式
* */
*/
public static byte[] dintToBytes(Integer i) {
Number intNumber = i;
return ByteUtil.numberToBytes(intNumber, ByteOrder.BIG_ENDIAN);
@ -802,7 +826,7 @@ public class ByteUtils {
/**
* intArray(2个字节) = byte[]
* */
*/
public static byte[] dintArrayToBytes(int[] dintArray) {
byte[] res = new byte[dintArray.length * 4];
for (int i = 0; i < dintArray.length; i++) {
@ -819,7 +843,7 @@ public class ByteUtils {
/**
* sint(1个字节) = byte[]
* 默认大端模式
* */
*/
public static byte[] usintToBytes(Integer i) {
if (i < 0) {
return null;
@ -828,6 +852,7 @@ public class ByteUtils {
res[0] = Byte.valueOf(i.toString());
return res;
}
public static byte usintTo1Byte(Integer i) {
if (i < 0 || i >= 256) {
return 0x00;
@ -838,7 +863,7 @@ public class ByteUtils {
/**
* usintArrayToBytes = byte[]
* 默认大端模式
* */
*/
public static byte[] usintArrayToBytes(int[] usintArray) {
byte[] res = new byte[usintArray.length];
for (int i = 0; i < usintArray.length; i++) {
@ -855,7 +880,7 @@ public class ByteUtils {
/**
* int(2个字节) = byte[]
* 默认大端模式
* */
*/
public static byte[] uintToBytes(Integer i) {
if (i < 0) {
return null;
@ -869,7 +894,8 @@ public class ByteUtils {
ByteBuffer buffer = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN);
buffer.putInt(value);
return buffer.array();
}else {
}
else {
ByteBuffer buffer = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(value);
return buffer.array();
@ -893,7 +919,7 @@ public class ByteUtils {
/**
* int(2个字节) = byte[]
* 默认大端模式
* */
*/
public static byte[] udintToBytes(Integer i) {
if (i < 0) {
return null;
@ -905,13 +931,14 @@ public class ByteUtils {
/**
* long(8个字节) = byte[]
* 默认大端模式
* */
*/
public static byte[] longToBytes(Long i) {
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
buffer.putLong(i);
byte[] array = buffer.array();
return array;
}
public static byte[] longArrayToBytes(List<Long> longs) {
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES * longs.size());
for (Long i : longs) {
@ -931,11 +958,10 @@ public class ByteUtils {
}
return buffer.getLong();
}
/**
* byte[] ==> List<Long>(8个字节)
* @param source
* @param source 来源
* @param littleEndian 输入数组是否小端模式
* @return
*/
public static List<Long> toLongArray(byte[] source, boolean littleEndian) {
List<Long> res = new ArrayList<>();
@ -954,7 +980,6 @@ public class ByteUtils {
}
public static byte[] udintArrayToBytes(int[] udintArray) {
byte[] res = new byte[udintArray.length * 4];
for (int i = 0; i < udintArray.length; i++) {
@ -973,7 +998,7 @@ public class ByteUtils {
/**
* boolean = byte[]
* 默认大端模式
* */
*/
public static byte[] boolToBytes(Boolean bool) {
byte[] res = new byte[2];
//之所以这样是为了撑大 newValue数组因为 modbus tcp 最小单元 就是1个word(2 byte).
@ -981,21 +1006,24 @@ public class ByteUtils {
if (bool) {
res[0] = 1;
return res;
}else {
}
else {
res[0] = 0;
return res;
}
}
/**
* booleanArray = byte[]
* */
*/
public static byte[] booleanArrayToBytes(boolean[] boolArray) {
byte[] res = new byte[boolArray.length];
for (int i = 0; i < boolArray.length; i++) {
boolean b = boolArray[i];
if (b) {
res[i] = 1;
}else {
}
else {
res[i] = 0;
}
}
@ -1025,7 +1053,8 @@ public class ByteUtils {
Byte aByte = fromBooleanArray(reverse(input));
res[z] = aByte;
z++;
}else {
}
else {
for (int i = 0; i < boolLength; i++) {
input[i] = queue.poll();
}
@ -1040,14 +1069,15 @@ public class ByteUtils {
/**
* charArray = byte[]
* */
*/
public static byte[] charArrayToBytes(char[] charArray) {
byte[] res = new byte[charArray.length];
for (int i = 0; i < charArray.length; i++) {
byte[] bytes = charToByte(charArray[i]);
if (bytes[0] != 0) {
return null;
}else {
}
else {
res[i] = bytes[1];
}
}
@ -1055,13 +1085,10 @@ public class ByteUtils {
}
/**
* byte = byte[]
* 默认大端模式
* */
*/
// public static byte[] setbytes(Byte bytes) {
// byte[] res = new byte[1];
// res[0] = bytes;
@ -1078,14 +1105,14 @@ public class ByteUtils {
* word = byte[]
* 默认大端模式
* tip: 目前只支持 带符号的十进制 == word(2个字节)
* */
*/
public static byte[] wordToBytes(Short word) {
return ByteUtil.shortToBytes(word, ByteOrder.BIG_ENDIAN);
}
/**
* wordArray = byte[]
* */
*/
public static byte[] wordArrayToBytes(short[] shortArray) {
byte[] res = new byte[shortArray.length * 2];
for (int i = 0; i < shortArray.length; i++) {
@ -1100,14 +1127,14 @@ public class ByteUtils {
* dword = byte[]
* 默认大端模式
* tip: 目前只支持 带符号的十进制 == word(4个字节)
* */
*/
public static byte[] dwordToBytes(Integer dword) {
return ByteUtil.intToBytes(dword, ByteOrder.BIG_ENDIAN);
}
/**
* dwordArray = byte[]
* */
*/
public static byte[] dwordArrayToBytes(int[] intArray) {
byte[] res = new byte[intArray.length * 4];
for (int i = 0; i < intArray.length; i++) {
@ -1123,10 +1150,11 @@ public class ByteUtils {
/**
* char(1字节) = byte[]
*
* */
*/
public static byte[] charToBytes(Character c) {
return String.valueOf(c).getBytes();
}
public static byte[] wcharToBytes(Character c) {
return charToByte(c);
}
@ -1134,7 +1162,7 @@ public class ByteUtils {
//如果plc中没有设置 strSize,那么 这个字符串变量的 最大长度默认是 254字节就是-2
/**
* 这个函数就用于 非str类型转bytes使用string类型转bytes 用strToBytes(String s,Integer strSize) 这个方法
* */
*/
public static byte[] strToBytes(String s) {
byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
byte[] res = new byte[bytes.length + 2];
@ -1145,6 +1173,7 @@ public class ByteUtils {
}
return res;
}
public static byte[] strArrayToBytes(List<String> strList) {
List<Byte> byteList = new ArrayList<>();
for (String s : strList) {
@ -1163,6 +1192,7 @@ public class ByteUtils {
/**
* 将字符串转换为指定字节长度的字节数组
*
* @param strValue 待转换的字符串
* @param byteSize 指定的字节长度
* @return 转换后的字节数组
@ -1191,7 +1221,7 @@ public class ByteUtils {
* 一般来说 plc中 string变量都是会设置 长度的 var1 = String[18],这里的strSize就是18
* 返回
* 返回的字节流是包含2个头字节的也就是 strSize+2 个长度的字节流
* */
*/
public static byte[] strToBytes(String s, Integer strSize) {
if (s == null || (strSize <= 0)) {
return null;
@ -1205,7 +1235,8 @@ public class ByteUtils {
for (int i = 0; i < strSize; i++) {
res[i + 2] = bytes[i];
}
}else {
}
else {
res[1] = Integer.valueOf(bytes.length).byteValue();
for (int i = 0; i < bytes.length; i++) {
res[i + 2] = bytes[i];
@ -1224,6 +1255,7 @@ public class ByteUtils {
}
return intToBytes(a);
}
public static byte[] remainderForBool(Integer i) {
int a = i / 16;
int z = i % 16;
@ -1251,12 +1283,9 @@ public class ByteUtils {
* 参数
* desc : 把字符串数组 ==> byte[] [x,x,..,..,..,..,... x,x,..,..,..,..,... ...]
* array : 就是需要被写入plc的内容,, array.length 就是你要写入数组的长度 注意array.length必须小于plc中设置的 数组变量 length
* strSize : 就是数组中某个变量的长度 比如 String str = new String[18] 这里的18就是strSize,但是在博图偏移量地址上可以发现它已经把你+2了,就是你设置最大string长度18 但偏移量=18+2=20;;
* 而上面的array.length是 List<String> list = new ArrayList<>; 的list.length
* 返回
* byte[] 就是整个数组字符串的 总的字节流
*
* */
* strSize : 就是数组中某个变量的长度 比如 String str = new String[18]
* 这里的18就是strSize,但是在博图偏移量地址上可以发现它已经把你+2了,就是你设置最大string长度18 但偏移量=18+2=20;;
*/
public static byte[] strArrayToBytes(String[] array, Integer strSize) {
//str0 [18, 17, 51, 48, 49, 49, 48, 48, 49, 50, 49, 48, 53, 51, 48, 49, 49, 49, 53]
//str1 [18, 18, 51, 48, 49, 49, 48, 48, 49, 50, 49, 48, 53, 51, 48, 49, 49, 49, 53, 49]
@ -1274,7 +1303,6 @@ public class ByteUtils {
}
//for private
private static byte[] charToByte(char c) {
@ -1283,10 +1311,12 @@ public class ByteUtils {
b[1] = (byte) (c & 0xFF);
return b;
}
private static char byteToChar(byte[] b) {
char c = (char) (((b[0] & 0xFF) << 8) | (b[1] & 0xFF));
return c;
}
private static char byteToChar(byte b) {
char c = (char) (b & 0xFF);
return c;
@ -1294,7 +1324,7 @@ public class ByteUtils {
/**
* 4个字节 ==> float小端模式
* */
*/
public static float bytesToFloat(byte[] src, int offset) {
// int i = ((src[offset] & 0xFF) << 24) | ((src[offset + 1] & 0xFF) << 16) | ((src[offset + 2] & 0xFF) << 8) | (src[offset + 3] & 0xFF);
// return Float.intBitsToFloat(i);
@ -1306,7 +1336,7 @@ public class ByteUtils {
/**
* 4个字节 ==> int小端模式
* */
*/
public static int bytesToInt(byte[] src, int offset) {
//return ((src[offset] & 0xFF) << 24) | ((src[offset + 1] & 0xFF) << 16) | ((src[offset + 2] & 0xFF) << 8) | (src[offset + 3] & 0xFF);

View File

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

View File

@ -1,10 +1,5 @@
package org.opentcs.kc.common.byteutils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2024-05-08 11:02
*/
public class StringArrayStruc {
private String[] content;
private Integer strSize;

View File

@ -5,15 +5,13 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @Author: 蔡翔
* @Date: 2019/11/6 14:11
* @Version 1.0
*
* 重点
* AsyncFuture 这个类就是票据 刚拿到这个票据是没有信息的当done == true 的时候这个票据 上就自动有信息了
* 这个结果类设计的比较神奇
* 重点.
* 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 volatile boolean done = false;
@ -23,6 +21,7 @@ public class AsyncFuture<Object> implements Future<Object> {
public AsyncFuture(Object oldRequest) {
this.oldRequest = oldRequest;
}
public AsyncFuture() {
}
@ -37,12 +36,14 @@ public class AsyncFuture<Object> implements Future<Object> {
@Override
public Object get(Long timeout) throws Exception {
public Object get(Long timeout)
throws Exception {
return null;
}
@Override
public Object get(Long timeout, String transationId) throws Exception {
public Object get(Long timeout, String transationId)
throws Exception {
synchronized (this) {
//其实有 synchronize就相当于有一个阻塞队列当有线程执行了wait 方法就会把执行wait的这个线程给加入wait<Thread> 队列
//当有线程执行notify方法的时候就会往这个队列中取出一个或者多个Thread,取出来以后就能执行后续代码了

View File

@ -1,14 +1,11 @@
package org.opentcs.kc.syn;
/**
* @Author: 蔡翔
* @Date: 2019/11/6 13:49
* @Version 1.0
*/
public interface Future<MQMessage> {
//别人调用我的时候我先给他们返回一个结果
MQMessage get(Long timeout) throws Exception;
MQMessage get(Long timeout)
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;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2021/8/17 14:35
*/
public class Main {
// 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.Package;
/**
* @Desc: "MES端 发送远程指令列表"
* @Author: caixiang
* @DATE: 2021/8/17 14:14
*/
public class SendedList {
// private static HashMap<String, AsyncFuture<MBPackage>> list = new HashMap<>();
// public static synchronized AsyncFuture<MQMessage> get(String transitionId){
@ -35,7 +29,9 @@ public class SendedList {
@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);
AsyncFuture<Package> mqMessageAsyncFuture = (AsyncFuture<Package>) CaffeineUtil.get(
transitionId
);
if (mqMessageAsyncFuture == null) {
return false;
}
@ -46,6 +42,4 @@ public class SendedList {
}
}

View File

@ -29,6 +29,7 @@ public class LogConst {
"这便,晨光微好,花开静好……"};
private final static Random r = new Random();
public static String getLogInfo() {
return LOG_INFOS[r.nextInt(LOG_INFOS.length - 1)];
}

View File

@ -32,18 +32,24 @@ public class LogEventBroadcaster {
.handler(new LogEventEncoder(remoteAddress));
}
public void run() throws Exception {
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()));
ch.writeAndFlush(
new LogMsg(
null, ++count,
LogConst.getLogInfo()
)
);
try {
//休眠 2 如果被中断则退出循环
Thread.sleep(2000);
} catch (InterruptedException e) {
}
catch (InterruptedException e) {
Thread.interrupted();
break;
}
@ -54,15 +60,19 @@ public class LogEventBroadcaster {
group.shutdownGracefully();
}
public static void main(String[] args) throws Exception {
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));
new InetSocketAddress(
"255.255.255.255",
LogConst.MONITOR_SIDE_PORT
)
);
try {
System.out.println("广播服务启动");
broadcaster.run();

View File

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

View File

@ -18,7 +18,9 @@ import java.util.List;
* 作者DarkKIng
* 类说明编码将实际的日志实体类编码为DatagramPacket
*/
public class LogEventEncoder extends MessageToMessageEncoder<LogMsg> {
public class LogEventEncoder
extends
MessageToMessageEncoder<LogMsg> {
private final InetSocketAddress remoteAddress;
//LogEventEncoder 创建了即将被发送到指定的 InetSocketAddress
@ -28,8 +30,11 @@ public class LogEventEncoder extends MessageToMessageEncoder<LogMsg> {
}
@Override
protected void encode(ChannelHandlerContext channelHandlerContext,
LogMsg logMsg, List<Object> out) throws Exception {
protected void encode(
ChannelHandlerContext channelHandlerContext,
LogMsg logMsg, List<Object> out
)
throws Exception {
byte[] msg = logMsg.getMsg().getBytes(CharsetUtil.UTF_8);
//容量的计算两个long型+消息的内容+分割符
ByteBuf buf = channelHandlerContext.alloc()

View File

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

View File

@ -6,12 +6,6 @@ import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import java.net.InetSocketAddress;
/**
* @Desc: ""
* @Author: caixiang ( Server 接受消息 )
* @DATE: 2024/12/15 10:57
*/
public class LogEventMonitor {
private final EventLoopGroup group;
private final Bootstrap bootstrap;
@ -47,16 +41,19 @@ public class LogEventMonitor {
group.shutdownGracefully();
}
public static void main(String[] args) throws Exception {
public static void main(String[] args)
throws Exception {
//构造一个新的 UdpAnswerSide并指明监听端口
LogEventMonitor monitor = new LogEventMonitor(
new InetSocketAddress(LogConst.MONITOR_SIDE_PORT));
new InetSocketAddress(LogConst.MONITOR_SIDE_PORT)
);
try {
//绑定本地监听端口
Channel channel = monitor.bind();
System.out.println("UdpAnswerSide running");
channel.closeFuture().sync();
} finally {
}
finally {
monitor.stop();
}
}

View File

@ -29,8 +29,10 @@ public final class LogMsg {
}
//用于传出消息的构造函数
public LogMsg(InetSocketAddress source, long msgId,
String msg) {
public LogMsg(
InetSocketAddress source, long msgId,
String msg
) {
this(source, msg, msgId, System.currentTimeMillis());
}

View File

@ -3,12 +3,13 @@ package org.opentcs.kc.udp;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
//import org.opentcs.kc.udp.agv.param.AgvEvent;
import org.opentcs.kc.common.byteutils.ByteUtils;
import org.opentcs.kc.udp.agv.param.AgvEvent;
import org.opentcs.kc.udp.agv.param.AgvEventConstant;
import org.opentcs.kc.udp.agv.param.function.af.QueryRobotStatusRsp;
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.function.navigation.*;
import org.opentcs.kc.udp.agv.param.function.read.ReadParam;
import org.opentcs.kc.udp.agv.param.function.read.ReadStrValue;
@ -17,12 +18,11 @@ import org.opentcs.kc.udp.agv.param.function.write.WriteParam;
import org.opentcs.kc.udp.agv.param.function.write.WriteStrValue;
import org.opentcs.kc.udp.agv.param.function.write.WriteValueMember;
import org.opentcs.kc.udp.agv.param.function.x14.RobotSetPosition;
import org.opentcs.kc.udp.agv.param.function.x17.QueryRobotRunStatusRsp;
import org.opentcs.kc.udp.agv.param.rsp.RcvEventPackage;
import org.opentcs.kc.udp.io.UDPClient;
import org.opentcs.kc.udp.agv.param.AgvEvent;
/**
*
* AGV启动
* 0xAF(查询机器人状态) 👌
* 0xB1(下发订阅信令) 👌
@ -33,26 +33,27 @@ import org.opentcs.kc.udp.agv.param.AgvEvent;
* 0x1F(确认初始位置) 👌
* 运行
* 0xAE(导航控制导航点控制) 👌
*
*
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/1/17 16:25
*/
public class KCCommandDemo {
public static void main(String [] args) throws Exception{
public static void main(String[] args)
throws Exception {
// {
// //0xAF(查询机器人状态)
// AgvEvent agvEvent = queryStatus();
// printInfo(agvEvent);
// RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
// if(rcv.isOk()){
// QueryRobotStatusRsp queryRobotStatusRsp = new QueryRobotStatusRsp(rcv.getDataBytes());
// System.out.println();
// System.out.println("received transationId : "+ "isok:"+rcv.isOk());
// for (byte b:rcv.getValue()){
// System.out.print(byteToHex(b)+" ");
// }
// System.out.println();
// System.out.println("---------------------");
// 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());
@ -86,6 +87,10 @@ public class KCCommandDemo {
// if(rcv.isOk()){
// System.out.println();
// System.out.println("received transationId : "+ "isok:"+rcv.isOk());
// for (byte b:rcv.getDataBytes()) {
// System.out.print(byteToHex(b)+" ");
// }
//
// SubscribeRsp subscribeRsp = new SubscribeRsp(rcv.getDataBytes());
// if(subscribeRsp.isOk()){
// //...
@ -142,26 +147,26 @@ public class KCCommandDemo {
// }
// {
// //0x14(手动定位)
// AgvEvent agvEvent = manualLocation();
// printInfo(agvEvent);
// RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
// if(rcv.isOk()){
// System.out.println();
// System.out.println("received "+ "isok:"+rcv.isOk()+" dataBytes:");
// printInfo(rcv);
// if(rcv.isOk()){
// //get and parse value
// System.out.println("0x14 ok");
// }else {
// System.out.println("0x14 failed");
// }
// }else {
// System.out.println();
// System.out.println("received transationId : "+ "isok:"+rcv.isOk());
// }
// }
{
//0x14(手动定位)
AgvEvent agvEvent = manualLocation();
printInfo(agvEvent);
RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
if(rcv.isOk()){
System.out.println();
System.out.println("received "+ "isok:"+rcv.isOk()+" dataBytes:");
printInfo(rcv);
if(rcv.isOk()){
//get and parse value
System.out.println("0x14 ok");
}else {
System.out.println("0x14 failed");
}
}else {
System.out.println();
System.out.println("received transationId : "+ "isok:"+rcv.isOk());
}
}
// {
@ -170,33 +175,40 @@ public class KCCommandDemo {
// 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)+" ");
// }
// System.out.println();
// System.out.println("print databytes:-----=======");
// for (byte c:rcv.getDataBytes()){
// System.out.print(byteToHex(c)+" ");
// }
// //QueryCargoStatusRsp queryCargoStatusRsp = new QueryCargoStatusRsp(rcv.getDataBytes());
// QueryRobotRunStatusRsp queryRobotRunStatusRsp = new QueryRobotRunStatusRsp(rcv.getDataBytes());
// System.out.println(queryRobotRunStatusRsp.toString());
//
// }else {
// System.out.println();
// System.out.println("received transationId : "+ "isok:"+rcv.isOk());
// }
// }
{
//0x1F(确认初始位置)
AgvEvent agvEvent = confirmInitialPosition();
printInfo(agvEvent);
RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
if(rcv.isOk()){
System.out.println("0x1F ok");
}else {
System.out.println();
System.out.println("0x1F fail");
System.out.println("received transationId : "+ "isok:"+rcv.isOk());
}
}
// {
// //0x1F(确认初始位置)
// AgvEvent agvEvent = confirmInitialPosition();
// printInfo(agvEvent);
// RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
// if (rcv.isOk()) {
// System.out.println("0x1F ok");
// }
// else {
// System.out.println();
// System.out.println("0x1F fail");
// System.out.println("received transationId : " + "isok:" + rcv.isOk());
// }
// }
// {
// //0xAE(导航控制导航点控制)
@ -246,7 +258,7 @@ public class KCCommandDemo {
* 指令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<>();
@ -268,12 +280,14 @@ public class KCCommandDemo {
* 指令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));
WriteValueMember valueMember1 = new WriteValueMember(
Short.valueOf("0"), Short.valueOf("4"), ByteUtils.uintToBytes(3, ByteOrder.LITTLE_ENDIAN)
);
valueMemberList.add(valueMember1);
List<WriteStrValue> strValueList = new ArrayList<>();
@ -287,14 +301,12 @@ public class KCCommandDemo {
}
/**
* 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;
@ -305,7 +317,7 @@ public class KCCommandDemo {
* 指令0xAE
* author: caixiang
* date: 2025/1/17 16:25
* */
*/
public static AgvEvent navigationControl() {
AgvEvent agvEvent = new AgvEvent(AgvEventConstant.CommandCode_QUERY_ROBOT_STATUS);
//TODO 构建
@ -313,34 +325,69 @@ public class KCCommandDemo {
//构建point
Action[] pointActions1 = new Action[]{
new Action(ActionSet.stop0x01, (byte) 0x00, 1, ActionSet.stop0x01_paramsize, ActionSet.stop0x01(orderId, (byte) 0x01))
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))
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))
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)
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(
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))
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) ,
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);
NavigationParam navigationParam = new NavigationParam(
1, 1, ByteUtils.usintTo1Byte(points.length), ByteUtils.usintTo1Byte(points.length - 1),
points, paths
);
agvEvent.setBody(navigationParam.toBytes());
return agvEvent;
@ -351,7 +398,7 @@ public class KCCommandDemo {
* 指令0x1F
* author: caixiang
* date: 2025/1/17 16:25
* */
*/
public static AgvEvent confirmInitialPosition() {
AgvEvent agvEvent = new AgvEvent(AgvEventConstant.CommandCode_CONFIRM_ROBOT_POSITION);
return agvEvent;
@ -362,7 +409,7 @@ public class KCCommandDemo {
* 指令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;
@ -382,7 +429,7 @@ public class KCCommandDemo {
* 指令0xB0
* author: caixiang
* date: 2025/1/17 16:25
* */
*/
public static AgvEvent checkCargoStatus() {
AgvEvent agvEvent = new AgvEvent(AgvEventConstant.CommandCode_QUERY_CARRY_STATUS);
return agvEvent;
@ -393,14 +440,18 @@ public class KCCommandDemo {
* 指令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) 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());
AgvEvent agvEvent = new AgvEvent(
AgvEventConstant.CommandCode_ISSUE_SUBSCRIPTION, subscribeParam.toBytes()
);
return agvEvent;
}

View File

@ -3,11 +3,6 @@ package org.opentcs.kc.udp.Service;
import org.opentcs.kc.udp.agv.param.AgvEvent;
import org.opentcs.kc.udp.agv.param.rsp.RcvEventPackage;
/**
* @author xzh
* @date 2025/2/21
* @desc 命令基类
*/
public class BaseCommand {
public static String byteToHex(byte b) {

View File

@ -5,19 +5,16 @@ import org.opentcs.kc.udp.agv.param.AgvEventConstant;
import org.opentcs.kc.udp.agv.param.rsp.RcvEventPackage;
import org.opentcs.kc.udp.io.UDPClient;
/**
* @author xzh
* @date 2025/2/21
* @desc 确认位置 0x1F
*/
public class ConfirmRelocation extends BaseCommand{
public class ConfirmRelocation
extends
BaseCommand {
/**
* decs: 确认机器人位置
* 指令0x1F
* author: caixiang
* date: 2025/1/17 16:25
* */
* 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;
@ -30,7 +27,8 @@ public class ConfirmRelocation extends BaseCommand{
RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
if (rcv.isOk()) {
System.out.println("0x1F ok");
}else {
}
else {
System.out.println();
System.out.println("0x1F fail");
System.out.println("received transationId : " + "isok:" + rcv.isOk());

View File

@ -1,5 +1,7 @@
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;
@ -11,7 +13,9 @@ 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{
public class HybridNavigation
extends
BaseCommand {
/**
* 测试模式true=开启测试false=关闭测试
@ -38,6 +42,14 @@ public class HybridNavigation extends BaseCommand{
* 用于定位段在整个任务中的位置目的是区分同一个段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;
/**
@ -45,45 +57,37 @@ public class HybridNavigation extends BaseCommand{
* 指令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_QUERY_ROBOT_STATUS);
*/
public static AgvEvent navigationControl(
Integer sourcePoint, Integer destinationPoint, Integer pathID, String operation
) {
AgvEvent agvEvent = new AgvEvent(AgvEventConstant.CommandCode_MIXED_ISSUANCE_TASK);
//TODO 构建
Integer orderId = orderID;
// 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))
// };
Integer oldPointSerialNum = pointSerialNum;
pointSerialNum += 2;
Point[] points = new Point[]{
new Point(oldPointSerialNum, sourcePoint, 1f, (byte)0x00, ByteUtils.usintTo1Byte(pointActions1.length),pointActions1),
new Point(pointSerialNum, destinationPoint, 1f, (byte)0x00, ByteUtils.usintTo1Byte(pointActions2.length),pointActions2)
// new Point(4, 3, 1f, (byte)0x00, ByteUtils.usintTo1Byte(pointActions3.length),pointActions3)
new Point(
oldPointSerialNum, sourcePoint, 0f, (byte) 0x00, ByteUtils.usintTo1Byte(0), null
),
new Point(
pointSerialNum, destinationPoint, 0f, (byte) 0x00, ByteUtils.usintTo1Byte(0), null
)
};
//构建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(pathSerialNum,pathID,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) ,
new Path(
pathSerialNum, pathID, 0f, (byte) 0x00, (byte) 0x01, ByteUtils.usintTo1Byte(0), 1f, 1f, null
),
};
NavigationParam navigationParam = new NavigationParam(orderID,taskKey,ByteUtils.usintTo1Byte(points.length),ByteUtils.usintTo1Byte(points.length-1),points,paths);
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;
@ -96,16 +100,24 @@ public class HybridNavigation extends BaseCommand{
* @param destinationPoint 下发终点
* @param operation 执行操作
*/
public static void command(String orderName, String sourcePoint, String destinationPoint, String operation) {
if (TEST_MODEL) {
return;
}
public static void command(
String orderName, String sourcePoint, String destinationPoint, String operation
) {
// if (TEST_MODEL) {
// return;
// }
Integer newOrderName = Integer.parseInt(orderName);
Integer newOrderName = getUniqueOrderID(orderName);
Integer newSourcePoint = Integer.parseInt(sourcePoint);
Integer newDestinationPoint = Integer.parseInt(destinationPoint);
//拼接起点终点字符串转为Integer类型获取唯一pathID
Integer pathID = Integer.parseInt(sourcePoint + destinationPoint);
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)) {
//切换订单重置参数
@ -118,7 +130,8 @@ public class HybridNavigation extends BaseCommand{
RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
if (rcv.isOk()) {
System.out.println("0xAE ok");
}else {
}
else {
System.out.println();
System.out.println("0xAE fail");
System.out.println("received transationId : " + "isok:" + rcv.isOk());
@ -127,6 +140,7 @@ public class HybridNavigation extends BaseCommand{
/**
* 初始化参数
*
* @param newOrderName 新的订单ID
*/
private static void initParams(Integer newOrderName) {
@ -136,4 +150,34 @@ public class HybridNavigation extends BaseCommand{
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

@ -7,12 +7,9 @@ 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;
/**
* @author xzh
* @date 2025/2/21
* @desc 手动定位 0x14
*/
public class ManualPosition extends BaseCommand{
public class ManualPosition
extends
BaseCommand {
public static AgvEvent manualLocation(double agvX, double agvY, double agvAngle) {
AgvEvent agvEvent = new AgvEvent(AgvEventConstant.CommandCode_ROBOT_SET_POSITION);
@ -34,10 +31,12 @@ public class ManualPosition extends BaseCommand{
if (rcv.isOk()) {
//get and parse value
System.out.println("0x14 ok");
}else {
}
else {
System.out.println("0x14 failed");
}
}else {
}
else {
System.out.println();
System.out.println("received transationId : " + "isok:" + rcv.isOk());
throw new RuntimeException("0x14 failed");

View File

@ -6,19 +6,16 @@ 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;
/**
* @author xzh
* @date 2025/2/21
* @desc 获取AGV运行状态
*/
public class QryRobotRunStatus extends BaseCommand{
public class QryRobotRunStatus
extends
BaseCommand {
/**
* decs: 查询机器人运行状态
* 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;
@ -31,7 +28,9 @@ public class QryRobotRunStatus extends BaseCommand{
RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
if (rcv.isOk()) {
//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();
System.out.println("received transationId : " + "isok:" + rcv.isOk());
@ -39,7 +38,8 @@ public class QryRobotRunStatus extends BaseCommand{
System.out.print(byteToHex(b) + " ");
}
return queryRobotRunStatusRsp;
}else {
}
else {
System.out.println();
System.out.println("received transationId : " + "isok:" + rcv.isOk());
throw new RuntimeException("0x17 fail");

View File

@ -6,20 +6,17 @@ 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;
/**
* @author xzh
* @date 2025/2/21
* @desc 查询机器人状态
*/
public class QryRobotStatus
extends BaseCommand {
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;
@ -31,14 +28,20 @@ public class QryRobotStatus
printInfo(agvEvent);
RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
if(rcv.isOk()){
QueryRobotStatusRsp queryRobotStatusRsp = new QueryRobotStatusRsp(rcv.getDataBytes());
System.out.println();
System.out.println("received transationId : "+ "isok:"+rcv.isOk());
return queryRobotStatusRsp;
// for (byte b:rcv.getValue()){
// System.out.print(byteToHex(b)+" ");
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)+" ");
// }
}else {
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

@ -13,31 +13,33 @@ 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;
/**
* @author xzh
* @date 2025/2/21
* @desc 订阅载货状态
*/
public class SubCargoStatus extends BaseCommand{
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) 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());
AgvEvent agvEvent = new AgvEvent(
AgvEventConstant.CommandCode_ISSUE_SUBSCRIPTION, subscribeParam.toBytes()
);
return agvEvent;
}
public static void command()
{
public static void command() {
// 0xB1(订阅信息)
AgvEvent agvEvent = issueSubscribe();
printInfo(agvEvent);
@ -49,24 +51,26 @@ public class SubCargoStatus extends BaseCommand{
SubscribeRsp subscribeRsp = new SubscribeRsp(rcv.getDataBytes());
if (subscribeRsp.isOk()) {
//...
}else {
}
else {
//...
}
}else {
}
else {
System.out.println();
System.out.println("received transationId : " + "isok:" + rcv.isOk());
}
}
//订阅执行操作
public void subscribe0xB0Operate(String name,QueryCargoStatusRsp queryCargoStatusRsp)
{
public void subscribe0xB0Operate(String name, QueryCargoStatusRsp queryCargoStatusRsp) {
// Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, name);
//修改载货状态
if (queryCargoStatusRsp.isCargo == 0) {
//未载货
} else if (queryCargoStatusRsp.isCargo == 1) {
}
else if (queryCargoStatusRsp.isCargo == 1) {
//载货
}
}

View File

@ -10,27 +10,29 @@ 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;
/**
* @author xzh
* @date 2025/2/21
* @desc 订阅机器人状态
*/
public class SubRobotStatue extends BaseCommand {
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) 100,1000);
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());
AgvEvent agvEvent = new AgvEvent(
AgvEventConstant.CommandCode_ISSUE_SUBSCRIPTION, subscribeParam.toBytes()
);
return agvEvent;
}
@ -46,10 +48,12 @@ public class SubRobotStatue extends BaseCommand {
SubscribeRsp subscribeRsp = new SubscribeRsp(rcv.getDataBytes());
if (subscribeRsp.isOk()) {
//...
}else {
}
else {
//...
}
}else {
}
else {
System.out.println();
System.out.println("received transationId : " + "isok:" + rcv.isOk());
}

View File

@ -12,24 +12,23 @@ 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;
/**
* @author xzh
* @date 2025/2/21
* @desc 切换自动模式
*/
public class SwitchAutomaticMode extends BaseCommand{
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));
WriteValueMember valueMember1 = new WriteValueMember(
Short.valueOf("1"), Short.valueOf("4"), ByteUtils.uintToBytes(3, ByteOrder.LITTLE_ENDIAN)
);
valueMemberList.add(valueMember1);
List<WriteStrValue> strValueList = new ArrayList<>();
@ -54,10 +53,12 @@ public class SwitchAutomaticMode extends BaseCommand{
if (rcv.isOk()) {
//get and parse value
System.out.println("write ok");
}else {
}
else {
System.out.println("write failed");
}
}else {
}
else {
System.out.println();
System.out.println("received transationId : " + "isok:" + rcv.isOk());
}

View File

@ -12,24 +12,23 @@ 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;
/**
* @author xzh
* @date 2025/2/21
* @desc 切换手动模式
*/
public class SwitchManualMode extends BaseCommand{
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));
WriteValueMember valueMember1 = new WriteValueMember(
Short.valueOf("0"), Short.valueOf("4"), ByteUtils.uintToBytes(3, ByteOrder.LITTLE_ENDIAN)
);
valueMemberList.add(valueMember1);
List<WriteStrValue> strValueList = new ArrayList<>();
@ -54,10 +53,12 @@ public class SwitchManualMode extends BaseCommand{
if (rcv.isOk()) {
//get and parse value
System.out.println("write ok");
}else {
}
else {
System.out.println("write failed");
}
}else {
}
else {
System.out.println();
System.out.println("received transationId : " + "isok:" + rcv.isOk());
}

View File

@ -7,7 +7,9 @@ import org.opentcs.kc.udp.io.UDPClient;
/**
* 作者蔡翔
*/
public class AgvUdpChannelInitializer extends ChannelInitializer<NioDatagramChannel> {
public class AgvUdpChannelInitializer
extends
ChannelInitializer<NioDatagramChannel> {
private UDPClient client;
public AgvUdpChannelInitializer(UDPClient client) {
@ -17,7 +19,8 @@ public class AgvUdpChannelInitializer extends ChannelInitializer<NioDatagramChan
@Override
protected void initChannel(NioDatagramChannel channel) throws Exception {
protected void initChannel(NioDatagramChannel channel)
throws Exception {
// Modbus
// 在管道中添加我们自己的接收数据实现方法
//todo 到时候这里改成 FixedLengthFrameDecoder 资料https://www.cnblogs.com/java-chen-hao/p/11571229.html

View File

@ -11,12 +11,9 @@ import org.opentcs.kc.udp.agv.param.rsp.RcvEventPackage;
import org.opentcs.kc.udp.io.UDPClient;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2024-06-06 10:45
*/
public class AgvUdpDecode extends SimpleChannelInboundHandler<DatagramPacket> {
public class AgvUdpDecode
extends
SimpleChannelInboundHandler<DatagramPacket> {
private UDPClient client;
@ -38,7 +35,9 @@ public class AgvUdpDecode extends SimpleChannelInboundHandler<DatagramPacket> {
int dataLength = in.getShortLE(24);
if (dataLength < 0) {
throw new Exception("bodyLength [" + dataLength + "] is not right, remote:" + ctx.channel().remoteAddress());
throw new Exception(
"bodyLength [" + dataLength + "] is not right, remote:" + ctx.channel().remoteAddress()
);
}
int neededLength = HEADER_SIZE + dataLength;
@ -49,7 +48,8 @@ public class AgvUdpDecode extends SimpleChannelInboundHandler<DatagramPacket> {
if (isDataEnough < 0) {
// 不够消息体长度(剩下的buffe组不了消息体),重新去组包
throw new Exception("readed bytes < content length");
}else {
}
else {
//todo这里重写subscribe 的逻辑,注意要区分是 订阅的还是 主动请求的
//组包成功
@ -61,19 +61,18 @@ public class AgvUdpDecode extends SimpleChannelInboundHandler<DatagramPacket> {
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 ){
client.subscribe0xAF(new RcvEventPackage(body[22],body));
}else if(commandCode == (byte)0xB0){
client.subscribe0xB0(new RcvEventPackage(body[22],body));
}else {
if (commandCode == (byte) 0xAF || commandCode == (byte) 0xB0) {
client.subscribeKC(new RcvEventPackage(body[22], body), body);
}
else {
SendedList.set(uuid, mbPackage);
}
}else {
}
else {
SendedList.set(uuid, mbPackage);
}
}

View File

@ -8,12 +8,12 @@ import org.opentcs.kc.common.byteutils.ByteUtil;
// import org.opentcs.kc.common.byteutils.ByteUtil;
// import org.opentcs.kc.common.byteutils.ByteUtil;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2024/12/27 15:59
*/
public class AgvEvent extends AgvEventHeader implements IAgvEvent, Serializable {
public class AgvEvent
extends
AgvEventHeader
implements
IAgvEvent,
Serializable {
private byte[] bodyLength;
private byte[] retain;
private byte[] body;
@ -27,6 +27,7 @@ public class AgvEvent extends AgvEventHeader implements IAgvEvent, Serializable
retain = new byte[]{0x00, 0x00};
body = new byte[]{};
}
public AgvEvent(Byte commandCode, byte[] body) {
super(commandCode);
//初始化

View File

@ -1,13 +1,10 @@
package org.opentcs.kc.udp.agv.param;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2024/12/27 10:19
*/
public class AgvEventConstant {
public static final byte[] AuthorizationCode = new byte[]{ (byte)0xd4 , (byte)0x97 , (byte)0x44 , (byte)0x9c , (byte)0xcb , (byte)0xcf , (byte)0x0b , (byte)0x4c , (byte)0x95 , (byte)0x51 , (byte)0xd8 , (byte)0x61 , (byte)0x70 , (byte)0xf1 , (byte)0xe7 , (byte)0x94};
public static final byte[] 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;

View File

@ -6,11 +6,6 @@ import java.util.List;
import org.opentcs.kc.common.CaffeineUtil;
import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2024-06-07 13:29
*/
public class AgvEventHeader {

View File

@ -1,10 +1,12 @@
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.byteutils.ByteUtil;
public interface IAgvEvent {
//read sended
public Package toBytes();
//write sended
public Package toBytes(Object newValue);

View File

@ -2,11 +2,6 @@ package org.opentcs.kc.udp.agv.param.function.af;
import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/1/21 10:03
*/
public class AbnormalEventStatusInfo {
private byte[] src;
//事件码,2个字节

View File

@ -2,11 +2,6 @@ package org.opentcs.kc.udp.agv.param.function.af;
import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/1/21 10:38
*/
public class ActionInfo {
private byte[] src;
//动作 ID,4个字节

View File

@ -2,11 +2,6 @@ package org.opentcs.kc.udp.agv.param.function.af;
import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/1/20 16:28
*/
public class BatteryStatusInfo {
//src
private byte[] src;
@ -20,6 +15,7 @@ public class BatteryStatusInfo {
public byte chargingState;
//预留,7个字节
public byte[] remain;
public BatteryStatusInfo(byte[] src) {
this.src = src;
this.batteryPercentage = ByteUtils.bytesToFloat(src, 0);

View File

@ -2,11 +2,6 @@ package org.opentcs.kc.udp.agv.param.function.af;
import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/1/20 14:56
*/
public class LocationStatusInfo {
private byte[] src;
//4个字节, 全局x,单位 m

View File

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

View File

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

View File

@ -1,13 +1,9 @@
package org.opentcs.kc.udp.agv.param.function.af;
import java.util.ArrayList;
import java.util.List;
import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/1/20 14:55
*/
public class QueryRobotStatusRsp {
private byte[] src;
public byte abnormal_size;
@ -22,6 +18,9 @@ public class QueryRobotStatusRsp {
public List<ActionInfo> actionInfoList;
public QueryRobotStatusRsp(byte[] src) {
if (src == null) {
throw new RuntimeException("method_QueryRobotStatusRsp_params_src_is_NUll");
}
this.src = src;
this.abnormal_size = src[0];
this.action_size = src[1];
@ -36,13 +35,29 @@ public class QueryRobotStatusRsp {
this.batteryStatusInfo = new BatteryStatusInfo(ByteUtils.copyBytes(src, 56 + taskByteSize, 20));
if (this.abnormal_size > 0) {
if (this.abnormalEventStatusInfoList == null) {
this.abnormalEventStatusInfoList = new ArrayList<>();
}
for (int i = 0; i < this.abnormal_size; i++) {
this.abnormalEventStatusInfoList.add(new AbnormalEventStatusInfo(ByteUtils.copyBytes(src,56+taskByteSize+20+12*i,12)));
this.abnormalEventStatusInfoList.add(
new AbnormalEventStatusInfo(
ByteUtils.copyBytes(src, 56 + taskByteSize + 20 + 12 * i, 12)
)
);
}
}
if (this.action_size > 0) {
if (this.actionInfoList == null) {
this.actionInfoList = new ArrayList<>();
}
for (int i = 0; i < this.action_size; i++) {
this.actionInfoList.add(new ActionInfo(ByteUtils.copyBytes(src,56+taskByteSize+20+12*this.abnormal_size+12*i,12)));
this.actionInfoList.add(
new ActionInfo(
ByteUtils.copyBytes(
src, 56 + taskByteSize + 20 + 12 * this.abnormal_size + 12 * i, 12
)
)
);
}
}

View File

@ -2,11 +2,6 @@ package org.opentcs.kc.udp.agv.param.function.af;
import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/1/20 15:22
*/
public class RunningStatusInfo {
private byte[] src;
//4个字节, 轴x 速度,单位 m/s

View File

@ -2,11 +2,6 @@ package org.opentcs.kc.udp.agv.param.function.af;
import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/1/20 15:22
*/
public class TaskStatusInfo {
private byte[] src;
//订单 ID
@ -41,9 +36,10 @@ public class TaskStatusInfo {
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));
this.pathStatusInfo[i] = new PathStateSequence(
ByteUtils.copyBytes(src, 12 + pointStatusNum * 8 + 8 * pointStatusNum + 8 * i, 8)
);
}
}
}
}

View File

@ -2,11 +2,6 @@ package org.opentcs.kc.udp.agv.param.function.b0;
import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/1/23 13:34
*/
public class QueryCargoStatusRsp {
//src
private byte[] src;

View File

@ -2,11 +2,6 @@ package org.opentcs.kc.udp.agv.param.function.b1;
import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/1/23 14:30
*/
public class SubscribeInfo {
//站控协议命令码2个字节
public byte[] commandCode;

View File

@ -4,16 +4,12 @@ import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/1/23 14:23
*/
public class SubscribeParam {
private byte[] src;
private List<SubscribeInfo> subscribeInfoList;
//uuid,64个字节
private byte[] uuid;
public SubscribeParam(List<SubscribeInfo> subscribeInfoList) {
this.subscribeInfoList = subscribeInfoList;
this.uuid = generate64ByteUUID();

View File

@ -2,11 +2,6 @@ package org.opentcs.kc.udp.agv.param.function.b1;
import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/2/5 14:21
*/
public class SubscribeRsp {
//源数组
private byte[] src;
@ -16,6 +11,7 @@ public class SubscribeRsp {
private byte errCode;
//reserved 3个字节
private byte[] reserved;
public SubscribeRsp(byte[] src) {
this.src = src;
this.uuid = ByteUtils.copyOfRange(src, 0, 64);
@ -26,7 +22,8 @@ public class SubscribeRsp {
public boolean isOk() {
if (this.errCode == 0) {
return true;
}else {
}
else {
return false;
}
}

View File

@ -4,11 +4,6 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/2/8 10:05
*/
public class Action {
//动作类型,2个字节
private byte[] actionType;
@ -26,7 +21,9 @@ public class Action {
private byte[] paramContent;
public Action(byte actionType, byte actionParallel, Integer actionId, Integer paramSize, byte[] paramContent) {
public Action(
byte actionType, byte actionParallel, Integer actionId, Integer paramSize, byte[] paramContent
) {
this.actionType = new byte[]{actionType, (byte) 0x00};
this.actionParallel = actionParallel;
this.actionRetain = (byte) 0x00;

View File

@ -4,11 +4,6 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/2/10 8:56
*/
public class ActionSet {
public static void main(String[] args) {
byte[] stop = stop0x01(1, (byte) 0x01);
@ -56,9 +51,11 @@ public class ActionSet {
* desc:叉齿升降
* 支持的命令0xAE 0xB2
*
* */
*/
public static byte[] forkliftElevation0x12(float lifitingHeight,byte hightMean,byte moveType, byte taskOperationType){
public static byte[] forkliftElevation0x12(
float lifitingHeight, byte hightMean, byte moveType, byte taskOperationType
) {
ByteBuf byteBuf = Unpooled.buffer(8);
byteBuf.writeBytes(ByteUtils.floatToBytes(lifitingHeight));
byteBuf.writeByte(hightMean);

View File

@ -4,11 +4,6 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: "0xAE 导航参数"
* @Author: caixiang
* @DATE: 2024/12/30 15:44
*/
public class NavigationParam {
//订单 ID,4个字节 ,Integer,从1开始依次累加
private byte[] orderId;
@ -25,7 +20,9 @@ public class NavigationParam {
//任务中路径信息结构体长度 pathNum
private Path[] paths;
public NavigationParam(Integer orderId, Integer taskKey, byte pointNum, byte pathNum, Point[] points, Path[] paths) {
public NavigationParam(
Integer orderId, Integer taskKey, byte pointNum, byte pathNum, Point[] points, Path[] paths
) {
this.orderId = ByteUtils.intToBytes(orderId, 1);
this.taskKey = ByteUtils.intToBytes(taskKey, 1);
this.pointNum = pointNum;

View File

@ -4,11 +4,6 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/2/8 9:39
*/
public class Path {
//序列号,4个字节,Integer,用于定位段在整个任务中的位置 1 开始奇数递增 135......point 2 4 6这样path 和point就可以结合起来了以区分同一个段 ID 在任务中是否多次出现
private byte[] serialNum;
@ -36,7 +31,10 @@ public class Path {
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(
Integer serialNum, Integer pathId, float angle, byte isSpecifyAngle, byte drivePose,
byte pathActionSize, float maxSpeed, float maxAngularSpeed, Action[] actions
) {
this.serialNum = ByteUtils.intToBytes(serialNum, 1);
this.pathId = ByteUtils.intToBytes(pathId, 1);
this.angle = ByteUtils.floatToBytes(angle);
@ -62,9 +60,11 @@ public class Path {
byteBuf.writeBytes(maxSpeed);
byteBuf.writeBytes(maxAngularSpeed);
byteBuf.writeBytes(pathRetain2);
if (actions != null) {
for (Action action : actions) {
byteBuf.writeBytes(action.toBytes());
}
}
int i = byteBuf.writerIndex();
byte[] bytes = new byte[i];
byteBuf.readBytes(bytes);

View File

@ -4,11 +4,6 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/2/8 9:39
*/
public class Point {
//序列号,4个字节,Integer,用于定位点在整个任务中的位置 0 开始偶数递增 0246......以区分同一个点 ID 在任务中是否多次出现
private byte[] serialNum;
@ -25,13 +20,17 @@ public class Point {
//任务中点上动作结构体,这里数组的长度是 pointActionSize 长度
private Action[] actions;
public Point(Integer serialNum, Integer pointId, float angle, byte isSpecifyAngle, byte pointActionSize, Action[] actions) {
public Point(
Integer serialNum, Integer pointId, float angle, byte isSpecifyAngle, byte pointActionSize,
Action[] actions
) {
this.serialNum = ByteUtils.intToBytes(serialNum, 1);
this.pointId = ByteUtils.intToBytes(pointId, 1);
this.angle = ByteUtils.floatToBytes(angle);
this.isSpecifyAngle = isSpecifyAngle;
this.pointActionSize = pointActionSize;
this.pointRetain = new byte[]{(byte)0x00, (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00};
this.pointRetain = new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00};
this.actions = actions;
}
@ -43,9 +42,11 @@ public class Point {
byteBuf.writeByte(isSpecifyAngle);
byteBuf.writeByte(pointActionSize);
byteBuf.writeBytes(pointRetain);
if (actions != null) {
for (Action action : actions) {
byteBuf.writeBytes(action.toBytes());
}
}
int i = byteBuf.writerIndex();
byte[] bytes = new byte[i];
byteBuf.readBytes(bytes);

View File

@ -4,16 +4,12 @@ import java.util.ArrayList;
import java.util.List;
import org.opentcs.kc.common.byteutils.ByteUtil;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2024/12/30 15:44
*/
public class ReadParam {
private byte valueNum;
private byte[] retain;
private byte[] valueId;
private byte[] readStrValues;
public ReadParam(byte[] valueId, List<ReadStrValue> rvalues) {
// 这里的valueId 也是 header里面的 transationId
valueNum = ByteUtil.intTo1Byte(rvalues.size());

View File

@ -2,11 +2,6 @@ package org.opentcs.kc.udp.agv.param.function.read;
import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/2/5 14:55
*/
public class ReadRsp {
//源数组
private byte[] src;
@ -30,7 +25,8 @@ public class ReadRsp {
public boolean isOk() {
if (valueByteLength <= 0) {
return false;
}else {
}
else {
return true;
}
}

View File

@ -4,17 +4,13 @@ import java.util.ArrayList;
import java.util.List;
import org.opentcs.kc.common.byteutils.ByteUtil;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2024/12/30 15:46
*/
public class ReadStrValue {
//变量名16个字节( 以这个变量名来定位变量的)
private byte[] varName;
//成员变量数量4个字节
private byte[] memberVarNum;
private byte[] memberList;
public ReadStrValue(String varName, Integer memberVarNum, List<ReadValueMember> memberList) {
this.varName = ByteUtil.stringTo16Byte(varName);
this.memberVarNum = ByteUtil.intToBytes(memberVarNum);
@ -27,6 +23,7 @@ public class ReadStrValue {
this.memberList[i] = bytes.get(i);
}
}
public ReadStrValue(String varName) {
this.varName = ByteUtil.stringTo16Byte(varName);
this.memberVarNum = ByteUtil.intToBytes(0);

View File

@ -4,11 +4,6 @@ import java.util.ArrayList;
import java.util.List;
import org.opentcs.kc.common.byteutils.ByteUtil;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2024/12/30 15:49
*/
public class ReadValueMember {
//偏移值就是以这个变量的起始点为基准偏移几个字节来读取数组里的后面几个变量基本上用于数组变量单体变量用不着 2个字节
private byte[] offsetValue;

View File

@ -5,11 +5,6 @@ import io.netty.buffer.Unpooled;
import java.util.List;
import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/2/5 16:41
*/
public class WriteParam {
//变量数量 1个字节 ,最大只支持 15 个变量
private byte valueNum;
@ -17,6 +12,7 @@ public class WriteParam {
private byte[] retain;
//这里的length 就是 valueNum
private List<WriteStrValue> values;
public WriteParam(Integer valueNum, List<WriteStrValue> values) {
this.valueNum = ByteUtils.usintTo1Byte(valueNum);
this.retain = new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00};

View File

@ -5,11 +5,6 @@ import io.netty.buffer.Unpooled;
import java.util.List;
import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/2/5 16:43
*/
public class WriteStrValue {
//变量名 16个字节
private String valueName;

View File

@ -4,11 +4,6 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/2/5 16:44
*/
public class WriteValueMember {
//变量成员偏移 2个字节
private byte[] valueOffset;
@ -16,6 +11,7 @@ public class WriteValueMember {
private byte[] valueLength;
//变量成员值 4个字节
private byte[] newValue;
public WriteValueMember(Short valueOffset, Short valueLength, byte[] newValue) {
this.valueOffset = ByteUtils.shortToBytes(valueOffset);
this.valueLength = ByteUtils.shortToBytes(valueLength);

View File

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

View File

@ -3,11 +3,6 @@ package org.opentcs.kc.udp.agv.param.function.x17;
import java.util.Arrays;
import org.opentcs.kc.common.byteutils.ByteUtils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2025/2/7 9:43
*/
public class QueryRobotRunStatusRsp {
//本体温度 ,8个字节 ,double
private double temp;

View File

@ -4,11 +4,6 @@ package org.opentcs.kc.udp.agv.param.rsp;
import org.opentcs.kc.common.byteutils.ByteUtils;
import org.opentcs.kc.udp.agv.param.AgvEventHeader;
/**
* @Desc: "通用的数据传输底层类"
* @Author: caixiang
* @DATE: 2022/10/18 16:22
*/
public class RcvEventPackage {
private boolean isOk;
@ -22,19 +17,26 @@ public class RcvEventPackage {
private String getContent(byte i) {
if (i == 0x00) {
return "成功执行";
}else if(i==0x01){
}
else if (i == 0x01) {
return "执行失败,原因未知";
}else if(i==0x02){
}
else if (i == 0x02) {
return "服务码错误";
}else if(i==0x03){
}
else if (i == 0x03) {
return "命令码错误";
}else if(i==0x04){
}
else if (i == 0x04) {
return "报文头部错误";
}else if(i==0x80){
}
else if (i == 0x80) {
return "无法执行命令,因为当前车辆导航状态与命令冲突";
}else if(i==0xFF){
}
else if (i == 0xFF) {
return "协议授权码错误";
}else {
}
else {
return "未知错误";
}
@ -45,12 +47,14 @@ public class RcvEventPackage {
this.isOk = true;
this.header = new AgvEventHeader(ByteUtils.copyBytes(value, 0, 28));
this.dataBytes = ByteUtils.copyBytes(value, 28, value.length - 28);
}else {
}
else {
this.isOk = false;
}
this.value = value;
this.content = getContent(executionCode);
}
public RcvEventPackage() {
}

View File

@ -9,6 +9,7 @@ import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Set;
import org.opentcs.access.KernelServicePortal;
import org.opentcs.access.rmi.KernelServicePortalBuilder;
@ -27,21 +28,17 @@ import org.opentcs.kc.udp.agv.param.rsp.RcvEventPackage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2022/1/15 13:01
*/
public enum UDPClient {
//如果要配置多个链接 local1 local2 .... 这样排下去好了
localAGV("1","192.168.0.211",17804,55678),
//local("127.0.0.1",502,true),
localAGV("50", "192.168.124.124", 17804, 55678),
;
// 服务端地址+端口
private String SERVICE_HOST = "192.168.0.123";
private Integer SERVICE_PORT = 1099;
// 服务端用户名+密码+地址+端口
private String SERVICE_USER = GuestUserCredentials.USER;
private String SERVICE_PWD = GuestUserCredentials.PASSWORD;
private String SERVICE_HOST = GuestUserCredentials.IP;
private Integer SERVICE_PORT = GuestUserCredentials.PORT;
private String name;
private String host;
@ -55,9 +52,8 @@ public enum UDPClient {
private Channel conn;
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);
// ==== 连接节点配置信息 ===== 开始
@ -77,29 +73,33 @@ public enum UDPClient {
}
public String getHost() {
return this.host;
}
public String getName() {
return this.name;
}
public void setIsOnline(boolean isOnline) {
this.isOnline = isOnline;
}
/**
* desc : 判断此链接 健康状况
* return
* true : 此tcp连接 正常
* false : 此tcp连接 异常
* */
*/
public boolean isOnline() {
return isOnline;
}
public void close() {
//手动关闭连接会出发InActivite 事件
group.shutdownGracefully();
}
public static ByteBuf byteArrayToByteBuf(byte[] byteArray) {
// 使用Unpooled类创建ByteBuf
ByteBuf byteBuf = Unpooled.buffer(byteArray.length);
@ -107,12 +107,13 @@ public enum UDPClient {
byteBuf.writeBytes(byteArray);
return byteBuf;
}
/**
* --线程安全的--
*
*
* 如果返回null就代表出现了异常并且尝试了 retryMax 次数并且尝试重置连接
* */
*/
public RcvEventPackage send(AgvEvent event) {
try {
//sendread 报文
@ -124,53 +125,66 @@ public enum UDPClient {
this.conn.writeAndFlush(
new DatagramPacket(
byteArrayToByteBuf(mbPackage.getBody()),
new InetSocketAddress(this.host,this.port)))
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();
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) {
}
catch (Throwable e) {
//e.printStackTrace();
String errMsg = " [ AGVclient - Read ] [ "+name+" host: "+ this.host +" ] ( occur err ) errTime: "+" ; send errMsg : "+e.getMessage()+" ; event :"+event.toString();
String errMsg = " [ AGVclient - Read ] [ " + name + " host: " + this.host
+ " ] ( occur err ) errTime: " + " ; send errMsg : " + e.getMessage()
+ " ; event :" + event.toString();
logger.info(errMsg);
throw new RuntimeException(errMsg);
}
}
public void subscribe0xB0(RcvEventPackage rcv){
public void subscribeKC(RcvEventPackage rcv, byte[] body) {
if (rcv.isOk()) {
if(name.equals("1")){
this.achieveSub0xB0(name, rcv);
}else if(name.equals("2")){
this.achieveSub0xB0(name, rcv);
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);
servicePortal.logout();
}
else {
System.out.println();
System.out.println("subscribe received transationId : " + "isok:" + rcv.isOk());
}
}
} else {
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 void subscribe0xAF(RcvEventPackage rcv){
public void subscribe0xAF(RcvEventPackage rcv, byte[] body) {
if (rcv.isOk()) {
if (name.equals("1")) {
this.achieveSub0xAF(name, rcv);
} else if (name.equals("2")) {
this.achieveSub0xAF(name, rcv);
this.achieveSub0xAF(body);
}
} else {
else {
System.out.println();
System.out.println("subscribe0xAF received transationId : " + "isok:" + rcv.isOk());
}
@ -178,49 +192,47 @@ public enum UDPClient {
/**
* 0xAF上报信息传入通讯适配器
* @param vehicleName 车辆名称
* @param rcv 响应数据
*
* @param body 响应数据
*/
private void achieveSub0xAF(String vehicleName ,RcvEventPackage rcv){
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)+" ");
}
KernelServicePortal servicePortal = new KernelServicePortalBuilder(GuestUserCredentials.USER, GuestUserCredentials.PASSWORD).build();
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, vehicleName);
//将AGV控制器上报信息传入通讯适配器
vehicleService.sendCommAdapterMessage(vehicle.getReference(), queryRobotStatusRsp);
servicePortal.logout();
Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, name);
vehicleService.sendCommAdapterMessage(vehicle.getReference(), body);
}
/**
* 0xB0上报信息传入通讯适配器
* @param vehicleName 车辆名称
* @param rcv 响应数据
*
* @param body 响应数据
*/
private void achieveSub0xB0(String vehicleName ,RcvEventPackage rcv){
private void achieveSub0xB0(byte[] 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)+" ");
}
// 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) + " ");
// }
KernelServicePortal servicePortal = new KernelServicePortalBuilder(GuestUserCredentials.USER, GuestUserCredentials.PASSWORD).build();
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, vehicleName);
Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, name);
if (vehicle == null) {
throw new RuntimeException("vehicle name:" + name + ",does not exist");
}
//将AGV控制器上报信息传入通讯适配器
vehicleService.sendCommAdapterMessage(vehicle.getReference(), queryCargoStatusRsp);
vehicleService.sendCommAdapterMessage(vehicle.getReference(), body);
servicePortal.logout();
}
@ -231,6 +243,7 @@ public enum UDPClient {
System.out.print(byteToHex(b) + " ");
}
}
public static String byteToHex(byte b) {
// 将byte转换为无符号整数
int unsignedByte = b & 0xFF;
@ -242,9 +255,10 @@ public enum UDPClient {
}
return hexString;
}
/**
* decs: 在项目启动的时候初始化 后面就不会初始化了
* */
*/
private void initClient() {
//NioEventLoopGroup Bootstrap 这些资源其实是不用 release的因为全局共用一份的你释放了 下次还得再new
NioEventLoopGroup group = new NioEventLoopGroup();
@ -254,7 +268,8 @@ public enum UDPClient {
.channel(NioDatagramChannel.class)
.handler(new ChannelInitializer<NioDatagramChannel>() {
@Override
protected void initChannel(NioDatagramChannel ch) throws Exception {
protected void initChannel(NioDatagramChannel ch)
throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//0 代表禁用 readerIdleTime 事件的监听
//这两个是固定的
@ -267,7 +282,8 @@ public enum UDPClient {
});
try {
this.conn = this.bootstrap.bind(this.bindPort).sync().channel();
}catch (Exception e){
}
catch (Exception e) {
logger.info("AGV UDP Initial Exception : " + e.getMessage());
}
}

View File

@ -86,38 +86,44 @@ class SameThreadExecutorServiceTest {
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 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 = 54;
int energy = 52;
map.put("energy", energy);
long positionX = -100;
long positionX = 3040;
map.put("positionX", positionX);
long positionY = 6510;
long positionY = -40;
map.put("positionY", positionY);
double positionAngle = 0.0;
map.put("positionAngle", positionAngle);
//向车辆对应的通讯适配器发送消息
KernelServicePortal servicePortal = new KernelServicePortalBuilder(GuestUserCredentials.USER,GuestUserCredentials.PASSWORD).build();
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, "2");
Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, "50");
vehicleService.sendCommAdapterMessage(vehicle.getReference(), map);
}

View File

@ -42,6 +42,7 @@ public class DefaultPropertySuggestions
keySuggestions.add(LoopbackAdapterConstants.AGV_AUTHORIZE_CODE);
keySuggestions.add(LoopbackAdapterConstants.AGV_IP);
keySuggestions.add(LoopbackAdapterConstants.AGV_PORT);
keySuggestions.add(LoopbackAdapterConstants.POINT_TYPE);
}
@Override