diff --git a/opentcs-commadapter-loopback/src/main/java/org/opentcs/virtualvehicle/LoopbackCommunicationAdapter.java b/opentcs-commadapter-loopback/src/main/java/org/opentcs/virtualvehicle/LoopbackCommunicationAdapter.java index 9c5a7eb..4dde75c 100644 --- a/opentcs-commadapter-loopback/src/main/java/org/opentcs/virtualvehicle/LoopbackCommunicationAdapter.java +++ b/opentcs-commadapter-loopback/src/main/java/org/opentcs/virtualvehicle/LoopbackCommunicationAdapter.java @@ -30,6 +30,7 @@ import org.opentcs.drivers.vehicle.SimVehicleCommAdapter; import org.opentcs.drivers.vehicle.VehicleCommAdapter; import org.opentcs.drivers.vehicle.VehicleProcessModel; import org.opentcs.drivers.vehicle.management.VehicleProcessModelTO; +import org.opentcs.manage.entity.ActionStatus; import org.opentcs.manage.entity.AgvInfo; import org.opentcs.manage.entity.AgvInfoParams; import org.opentcs.manage.entity.AgvStatus; @@ -106,6 +107,10 @@ public class LoopbackCommunicationAdapter * 记录当前车辆位置 */ private static String CURRENT_POS; + /** + * 订单任务key + */ + private int TASK_KEY = 1; /** * Creates a new instance. @@ -212,6 +217,7 @@ public class LoopbackCommunicationAdapter if (isEnabled()) { return; } + LOG.info("Loopback comm adapter is being enabled: {}", getName()); super.enable(); } @@ -220,6 +226,7 @@ public class LoopbackCommunicationAdapter if (!isEnabled()) { return; } + LOG.info("Loopback comm adapter is being disable: {}", getName()); super.disable(); } @@ -232,12 +239,13 @@ public class LoopbackCommunicationAdapter public synchronized void sendCommand(MovementCommand cmd) { requireNonNull(cmd, "cmd"); - //下发起点 String sourcePoint = null; if (cmd.getStep().getSourcePoint() != null) { + //下发起点不为空 sourcePoint = cmd.getStep().getSourcePoint().getName(); + //下发AGV移动指令 - ExecuteMove.sendCmd(cmd, getSerialNum()); + ExecuteMove.sendCmd(getProcessModel().getName(), cmd, getSerialNum()); } // Start the simulation task if we're not in single step mode and not simulating already. @@ -249,6 +257,8 @@ public class LoopbackCommunicationAdapter ORDER_NAME = cmd.getTransportOrder().getName(); //下发起点 CURRENT_POS = sourcePoint; + //当前执行taskKey + TASK_KEY = 1; // The command is added to the sent queue after this method returns. Therefore // we have to explicitly start the simulation like this. @@ -275,9 +285,9 @@ public class LoopbackCommunicationAdapter if (message instanceof AgvInfo agvInfo) { //通讯适配器车辆模型更新 handleCallbacks(agvInfo.getParams()); - } else if (message instanceof AgvStatus agvStatus) { + } else if (message instanceof ActionStatus actionStatus) { //自动管理通讯适配器状态和适配器动作执行状态 - handleAdapterAuthEnable(agvStatus); + handleActionStatus(actionStatus); } } @@ -385,12 +395,14 @@ public class LoopbackCommunicationAdapter */ private void startVehicleExec(MovementCommand command) { LOG.debug("VEHICLE: {} BEGINS TO EXECUTE THE COMMAND: {}", getProcessModel().getName(),command); - Step step = command.getStep(); getProcessModel().setState(Vehicle.State.EXECUTING); + Step step = command.getStep(); if (step.getPath() == null) { actionExec(command); } else { + ExecuteMove.setExecTaskKey(getProcessModel().getName(), TASK_KEY); + TASK_KEY++; //todo 移动 movementExec(command); } @@ -418,7 +430,7 @@ public class LoopbackCommunicationAdapter ACTION_STATUS = true; //下发动作 - ExecuteAction.sendCmd(command.getOperation(), getSerialNum()); + ExecuteAction.sendCmd(getProcessModel().getName(), command.getOperation(), getSerialNum()); // 结束动作 finishCmd(command); @@ -769,7 +781,7 @@ public class LoopbackCommunicationAdapter if (!Objects.equals(getProcessModel().getPosition(), params.getPoint().toString())) { getProcessModel().setPosition(params.getPoint().toString()); //更新最终经过点 } - getProcessModel().setPose(new Pose(new Triple(Math.round(params.getX()), Math.round(params.getY()), 0), params.getAngle())); + getProcessModel().setPose(new Pose(new Triple(Math.round(params.getX() * 1000), Math.round(params.getY() * 1000), 0), params.getAngle())); } else { //最后经过点为0,应该是车辆重启过。车辆关机时应该正常对点位进行交管,防止车辆碰撞。 //todo 可能需要实现原点自动定位,暂时不做处理 @@ -793,15 +805,9 @@ public class LoopbackCommunicationAdapter return serialNum; } - private void handleAdapterAuthEnable(AgvStatus agvStatus) { - if (agvStatus.getActionStatus()) { + private void handleActionStatus(ActionStatus actionStatus) { + if (actionStatus.getStatus()) { ACTION_STATUS = false; } - - if (agvStatus.getStatus()) { - enable(); - } else { - disable(); - } } } diff --git a/opentcs-common/build.gradle b/opentcs-common/build.gradle index ef28c79..1f9c0b8 100644 --- a/opentcs-common/build.gradle +++ b/opentcs-common/build.gradle @@ -33,6 +33,8 @@ dependencies { implementation 'io.moquette:moquette-broker:0.15' // MQTT 客户端 implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5' + //JTS + implementation 'org.locationtech.jts:jts-core:1.18.2' } processResources.doLast { diff --git a/opentcs-common/src/main/java/org/opentcs/charge/ChargeStrategy.java b/opentcs-common/src/main/java/org/opentcs/charge/ChargeStrategy.java index 02ebdab..076c315 100644 --- a/opentcs-common/src/main/java/org/opentcs/charge/ChargeStrategy.java +++ b/opentcs-common/src/main/java/org/opentcs/charge/ChargeStrategy.java @@ -44,13 +44,13 @@ public class ChargeStrategy { return; } - //如果 (电池为放电状态) - if (batteryStatus == 0) { + if (batteryStatus == 0) { //放电状态 + //判断是否执行充电 - } else { //释放充电中的车辆 + } else { //充电状态 + //判断是否释放车辆 } } - } diff --git a/opentcs-common/src/main/java/org/opentcs/communication/http/HttpClient.java b/opentcs-common/src/main/java/org/opentcs/communication/http/HttpClient.java index e133ae0..ba57f3e 100644 --- a/opentcs-common/src/main/java/org/opentcs/communication/http/HttpClient.java +++ b/opentcs-common/src/main/java/org/opentcs/communication/http/HttpClient.java @@ -2,11 +2,16 @@ package org.opentcs.communication.http; import com.alibaba.fastjson.JSON; import java.io.IOException; +import java.util.ArrayList; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; +import org.opentcs.communication.http.dto.BaseRequestTo; +import org.opentcs.communication.http.dto.kc.move.Path; +import org.opentcs.communication.http.dto.kc.move.Point; +import org.opentcs.communication.http.dto.kc.move.RequestAe; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,7 +26,7 @@ public class HttpClient { private final OkHttpClient client; - private final String url = "http://192.168.124.121:2005"; +// private final String url = "http://192.168.124.121:2005"; public HttpClient() { this.client = new OkHttpClient(); @@ -31,7 +36,7 @@ public class HttpClient { * 发送 POST 请求 * @return ApiResponse 对象 */ - public String sendCommand(Object data) { + public String sendCommand(String url, Object data) { String jsonBody = JSON.toJSONString(data); System.out.println("SENDING POST REQUEST data:" + jsonBody); LOG.debug("SENDING POST REQUEST data: {}", jsonBody); @@ -166,67 +171,49 @@ public class HttpClient { // httpClient.sendCommand(baseRequestTo); // } -// { //0xAE -// RequestAe ae = new RequestAe(); -// ae.setOrderId(1); -// ae.setTaskKey(1); -// ae.setPointSize(2); -// ae.setPathSize(1); -// -// ArrayList pointList = new ArrayList<>(); -// for (int i = 0; i < ae.getPointSize(); i++) { -// Point point = new Point(); -// if (i == 0) { -// point.setPointId(1); -// point.setPointSerialNumber(0); -// point.setType(0); -// point.setIsFrontAngle(0); -// point.setFrontAngle(0); -// point.setPointActionSize(0); -// point.setPointActionList(new ArrayList<>()); -// } else { -// point.setPointId(2); -// point.setPointSerialNumber(2); -// point.setType(1); -// point.setIsFrontAngle(0); -// point.setFrontAngle(0); -// point.setPointActionSize(1); -// for (int j = 0; j < point.getPointActionSize(); j++) { -// //实际代码中修改为实际逻辑 -// point.setPointActionList(new ArrayList<>(Arrays.asList("LOAD"))); -// } -// } -// pointList.add(point); -// } -// -// ArrayList pathList = new ArrayList<>(); -// for (int i = 0; i < ae.getPathSize(); i++) { -// Path path = new Path(); -// path.setPathId(12); -// path.setPathSerialNumber(1); -// path.setFixedAngle(0); -// path.setIsFixedAngle(0); -// path.setDrivePosture(1); -// path.setPathActionSize(0); -// path.setMaximumSpeed(1); -// path.setMaximumAngularVelocity(1); -// for (int j = 0; j < path.getPathActionSize(); j++) { -// path.setPathActionList(new ArrayList<>(Arrays.asList("test"))); -// } -// -// if (path.getPathActionSize() == 0) { -// path.setPathActionList(new ArrayList<>()); -// } -// -// pathList.add(path); -// } -// -// ae.setPoint(pointList); -// ae.setPath(pathList); -// -// BaseRequestTo baseRequestTo = new BaseRequestTo(KcCmdEnum.getValueByKey(0xAE), "OPENTCS", "KC-CTRL", ae); -// -// httpClient.sendCommand(baseRequestTo); -// } + { //0xAE + RequestAe ae = new RequestAe(); + ae.setWms_order_id(1); + ae.setOrder_id(1); + ae.setTask_key(1); + ae.setPoint_size(2); + ae.setPath_size(1); + + ArrayList pointList = new ArrayList<>(); + for (int i = 0; i < ae.getPoint_size(); i++) { + Point point = new Point(); + + if (i == 0) { + point.setPoint_id(1); + point.setPoint_serial(0); + } else { + point.setPoint_id(2); + point.setPoint_serial(2); + } + point.setPoint_angle(0); + + pointList.add(point); + } + ae.setPoint_info(pointList); + + ArrayList pathList = new ArrayList<>(); + for (int i = 0; i < ae.getPath_size(); i++) { + Path path = new Path(); + + path.setPath_id(12); + path.setPath_serial(1); + path.setPath_angle(0); + path.setDriver_pose(1); + path.setMax_speed(1); + path.setMax_angle_speed(1); + + pathList.add(path); + } + ae.setPath_info(pathList); + + BaseRequestTo baseRequestTo = new BaseRequestTo(2, "OPENTCS", "KC-CTRL", 1, "2025-6-6 10:00:00", ae); + + httpClient.sendCommand("http://192.168.124.121:2005", baseRequestTo); + } } } diff --git a/opentcs-common/src/main/java/org/opentcs/communication/http/dto/BaseRequestTo.java b/opentcs-common/src/main/java/org/opentcs/communication/http/dto/BaseRequestTo.java index 492f3e6..e31d3af 100644 --- a/opentcs-common/src/main/java/org/opentcs/communication/http/dto/BaseRequestTo.java +++ b/opentcs-common/src/main/java/org/opentcs/communication/http/dto/BaseRequestTo.java @@ -1,6 +1,5 @@ package org.opentcs.communication.http.dto; -import jakarta.annotation.Nonnull; import lombok.Data; /** @@ -12,33 +11,30 @@ import lombok.Data; public class BaseRequestTo { public BaseRequestTo( - @Nonnull Integer type, - @Nonnull String sender, - @Nonnull String receiver, - @Nonnull Integer serial_num, - @Nonnull String time, - @Nonnull Object data + Integer type, + String sender, + String receiver, + Integer serial_num, + String time, + Object params ) { this.type = type; this.sender = sender; this.receiver = receiver; - this.data = data; + this.serial_num = serial_num; + this.time = time; + this.params = params; } - @Nonnull private Integer type; - @Nonnull private String sender; - @Nonnull private String receiver; - @Nonnull private Integer serial_num; - @Nonnull private String time; - private Object data; + private Object params; } diff --git a/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/cmdb2/RequestB2.java b/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/cmdb2/RequestB2.java deleted file mode 100644 index 4e77e5a..0000000 --- a/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/cmdb2/RequestB2.java +++ /dev/null @@ -1,70 +0,0 @@ -package org.opentcs.communication.http.dto.kc.cmdb2; - -import java.util.ArrayList; - -/** - * @author xuzhiheng - * @date 2025/5/14 - * @desc 实体类 - */ -public class RequestB2 { - - /** - * 动作类型-》填写动作命令码 - */ - private Integer actionType; - /** - * 执行动作并行方式: 0x00:为移动和动作间都可并行, 0x01:为动作间可以并行,不能移动, 0x02 只能执行当前动作 - */ - private Integer actionParallelManner; - /** - * 参数内容长度 - */ - private Integer paramSize; - /** - * 参数内容 - */ - private ArrayList paramData; - - public Integer getActionType() { - return actionType; - } - - public void setActionType(Integer actionType) { - this.actionType = actionType; - } - - public Integer getActionParallelManner() { - return actionParallelManner; - } - - public void setActionParallelManner(Integer actionParallelManner) { - this.actionParallelManner = actionParallelManner; - } - - public Integer getParamSize() { - return paramSize; - } - - public void setParamSize(Integer paramSize) { - this.paramSize = paramSize; - } - - public ArrayList getParamData() { - return paramData; - } - - public void setParamData(ArrayList paramData) { - this.paramData = paramData; - } - - @Override - public String toString() { - return "RequestB2{" + - "actionType=" + actionType + - ", actionParallelManner=" + actionParallelManner + - ", paramSize=" + paramSize + - ", paramData=" + paramData + - '}'; - } -} diff --git a/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/cmdb2/action/Action01.java b/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/cmdb2/action/Action01.java deleted file mode 100644 index 8618fb5..0000000 --- a/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/cmdb2/action/Action01.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.opentcs.communication.http.dto.kc.cmdb2.action; - -/** - * @author xuzhiheng - * @date 2025/5/14 - * @desc 实体类 - */ -public class Action01 { - - /** - * 是否立即停止移动: 0:机器人正常移动到点上停止(停不下来就移动到下一个点), 1:立刻停止(缓停); - */ - private Integer isStopImmediately; - - public Integer getIsStopImmediately() { - return isStopImmediately; - } - - public void setIsStopImmediately(Integer isStopImmediately) { - this.isStopImmediately = isStopImmediately; - } - - @Override - public String toString() { - return "Action01{" + - "isStopImmediately=" + isStopImmediately + - '}'; - } -} diff --git a/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/cmdb2/action/Action02.java b/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/cmdb2/action/Action02.java deleted file mode 100644 index 90503e7..0000000 --- a/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/cmdb2/action/Action02.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.opentcs.communication.http.dto.kc.cmdb2.action; - -/** - * @author xuzhiheng - * @date 2025/5/13 - * @desc 实体类 - */ -public class Action02 { - - /** - * 订单ID - */ - private Integer orderId; - /** - * 任务key - */ - private Integer taskKey; - - public Integer getOrderId() { - return orderId; - } - - public void setOrderId(Integer orderId) { - this.orderId = orderId; - } - - public Integer getTaskKey() { - return taskKey; - } - - public void setTaskKey(Integer taskKey) { - this.taskKey = taskKey; - } - - @Override - public String toString() { - return "Action02{" + - "orderId=" + orderId + - ", taskKey=" + taskKey + - '}'; - } -} diff --git a/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/cmdb2/action/Action03.java b/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/cmdb2/action/Action03.java deleted file mode 100644 index b2c62ce..0000000 --- a/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/cmdb2/action/Action03.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.opentcs.communication.http.dto.kc.cmdb2.action; - -/** - * @author xuzhiheng - * @date 2025/5/13 - * @desc 实体类 - */ -public class Action03 { - - /** - * 订单ID - */ - private Integer orderId; - /** - * 是否立即停止移动: 1 立刻停止(缓停);0 AGV 正常移动到点上停止(停不下来就移动到下一个点) - */ - private Integer isStopImmediately; - - public Integer getOrderId() { - return orderId; - } - - public void setOrderId(Integer orderId) { - this.orderId = orderId; - } - - public Integer getIsStopImmediately() { - return isStopImmediately; - } - - public void setIsStopImmediately(Integer isStopImmediately) { - this.isStopImmediately = isStopImmediately; - } - - @Override - public String toString() { - return "Action03{" + - "orderId=" + orderId + - ", isStopImmediately=" + isStopImmediately + - '}'; - } -} diff --git a/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/move/Path.java b/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/move/Path.java index 72f3e74..b55318a 100644 --- a/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/move/Path.java +++ b/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/move/Path.java @@ -26,7 +26,7 @@ public class Path { /** * 行驶姿态: 0x0 正走,0x1 倒走,0x02 左横移(支持横移类底盘进,行二维码导航时生效),0x03 右横移(支持横移类底盘进行二维码导航时生效) */ - private Integer drive_pose; + private Integer driver_pose; /** * 指定的目标最大速度 */ diff --git a/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/move/RequestAe.java b/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/move/RequestAe.java index b1f2609..b99cd65 100644 --- a/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/move/RequestAe.java +++ b/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/move/RequestAe.java @@ -1,6 +1,5 @@ package org.opentcs.communication.http.dto.kc.move; -import jakarta.annotation.Nonnull; import java.util.ArrayList; import lombok.Data; @@ -14,32 +13,32 @@ public class RequestAe { public RequestAe() {} + /** + * 订单ID wms_order_id + */ + private Integer wms_order_id; /** * 订单ID */ - @Nonnull - private Integer orderId; + private Integer order_id; /** * 任务ID task_key: 任务的唯一标识。与订单 ID 绑定,从 1 开始,当同一订单下发新的资源时加 1; */ - @Nonnull - private Integer taskKey; + private Integer task_key; /** * 点个数 point_size */ - @Nonnull - private Integer pointSize; + private Integer point_size; /** * 路径个数 path_size */ - @Nonnull - private Integer pathSize; + private Integer path_size; /** * 点信息结构 */ - private ArrayList point; + private ArrayList point_info; /** * 路线信息结构 */ - private ArrayList path; + private ArrayList path_info; } diff --git a/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/operation/RequestB2.java b/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/operation/RequestB2.java index c477bec..bd448c9 100644 --- a/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/operation/RequestB2.java +++ b/opentcs-common/src/main/java/org/opentcs/communication/http/dto/kc/operation/RequestB2.java @@ -13,13 +13,17 @@ public class RequestB2 { /** * 动作类型-》填写动作命令码 */ - private Integer actionType; + private Integer action_type; /** * 执行动作并行方式: 0x00:为移动和动作间都可并行, 0x01:为动作间可以并行,不能移动, 0x02 只能执行当前动作 */ - private Integer actionParallelManner; + private Integer action_parallel_manner; /** * 参数内容长度 */ - private Integer paramSize; + private Integer param_size; + /** + * 参数内容 + */ + private Object param_data; } diff --git a/opentcs-common/src/main/java/org/opentcs/communication/http/entity/OrderInfo.java b/opentcs-common/src/main/java/org/opentcs/communication/http/entity/OrderInfo.java new file mode 100644 index 0000000..731f722 --- /dev/null +++ b/opentcs-common/src/main/java/org/opentcs/communication/http/entity/OrderInfo.java @@ -0,0 +1,35 @@ +package org.opentcs.communication.http.entity; + +import lombok.Data; + +@Data +public class OrderInfo { + + /** + * 订单ID + */ + private Integer id; + /** + * 订单名称 + */ + private String name; + /** + * 订单状态: 1=执行中,2=暂停,3=撤销 + */ + private Integer state; + /** + * 任务唯一键 + */ + private Integer taskKey; + /** + * 点序列号 + * 用于定位点在整个任务中的位置。目的是区分同一个点ID是否在一个任务中出现多次。从0开始偶数递增,例如:0->2->4->6…… + */ + private Integer pointSerialNum; + /** + * 路线序列号 + * 用于定位段在整个任务中的位置。目的是区分同一个段ID是否在一个任务中出现多次。从1开始奇数递增,例如:1->3->5->7…… + */ + private Integer pathSerialNum; + +} diff --git a/opentcs-common/src/main/java/org/opentcs/communication/http/enums/Operations.java b/opentcs-common/src/main/java/org/opentcs/communication/http/enums/Operations.java new file mode 100644 index 0000000..037c02e --- /dev/null +++ b/opentcs-common/src/main/java/org/opentcs/communication/http/enums/Operations.java @@ -0,0 +1,19 @@ +package org.opentcs.communication.http.enums; + +public enum Operations { + + PAUSED(1), //暂停订单 + CONTINUE(2), //继续订单 + CANCEL(3) //撤销订单 + ; + + private final int type; + + Operations(int type) { + this.type = type; + } + + public int getType() { + return type; + } +} diff --git a/opentcs-common/src/main/java/org/opentcs/communication/http/enums/VehicleEnums.java b/opentcs-common/src/main/java/org/opentcs/communication/http/enums/VehicleEnums.java new file mode 100644 index 0000000..05b5380 --- /dev/null +++ b/opentcs-common/src/main/java/org/opentcs/communication/http/enums/VehicleEnums.java @@ -0,0 +1,30 @@ +package org.opentcs.communication.http.enums; + +public enum VehicleEnums { + + // 定义枚举常量(车辆名称),并传入对应的ip和port + v1("192.168.124.221", 2005), + v2("192.168.124.222", 2005) + ; + + // 枚举的属性 + private final String ip; + private final int port; + + VehicleEnums(String ip, int port) { + this.ip = ip; + this.port = port; + } + + + // 获取IP + public String getIp() { + return ip; + } + + // 获取端口 + public int getPort() { + return port; + } + +} diff --git a/opentcs-common/src/main/java/org/opentcs/communication/http/service/BaseService.java b/opentcs-common/src/main/java/org/opentcs/communication/http/service/BaseService.java new file mode 100644 index 0000000..d6cf91b --- /dev/null +++ b/opentcs-common/src/main/java/org/opentcs/communication/http/service/BaseService.java @@ -0,0 +1,12 @@ +package org.opentcs.communication.http.service; + +import org.opentcs.communication.http.enums.VehicleEnums; + +public class BaseService { + + public static String getUrl(String vehicleName) { + VehicleEnums vehicleEnums = VehicleEnums.valueOf(vehicleName); + return "http://" + vehicleEnums.getIp() + ":" + vehicleEnums.getPort(); + } + +} diff --git a/opentcs-common/src/main/java/org/opentcs/communication/http/service/ExecuteAction.java b/opentcs-common/src/main/java/org/opentcs/communication/http/service/ExecuteAction.java index d485dc0..7c1f176 100644 --- a/opentcs-common/src/main/java/org/opentcs/communication/http/service/ExecuteAction.java +++ b/opentcs-common/src/main/java/org/opentcs/communication/http/service/ExecuteAction.java @@ -5,14 +5,17 @@ import java.time.format.DateTimeFormatter; import org.opentcs.communication.http.HttpClient; import org.opentcs.communication.http.dto.BaseRequestTo; import org.opentcs.communication.http.dto.kc.action.RequestAction; +import org.opentcs.communication.http.enums.VehicleEnums; -public class ExecuteAction { +public class ExecuteAction extends BaseService { /** * 下发动作到平台 * @param action 动作 */ - public static void sendCmd(String action, Integer serialNum) { + public static void sendCmd(String vehicleName, String action, Integer serialNum) { + + String url = getUrl(vehicleName); // 获取当前时间 LocalDateTime now = LocalDateTime.now(); @@ -32,7 +35,7 @@ public class ExecuteAction { ); HttpClient httpClient = new HttpClient(); - httpClient.sendCommand(baseRequestTo); + httpClient.sendCommand(url, baseRequestTo); } } diff --git a/opentcs-common/src/main/java/org/opentcs/communication/http/service/ExecuteMove.java b/opentcs-common/src/main/java/org/opentcs/communication/http/service/ExecuteMove.java index 9e5eb1f..24900a9 100644 --- a/opentcs-common/src/main/java/org/opentcs/communication/http/service/ExecuteMove.java +++ b/opentcs-common/src/main/java/org/opentcs/communication/http/service/ExecuteMove.java @@ -3,62 +3,58 @@ package org.opentcs.communication.http.service; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; +import java.util.Objects; import org.opentcs.communication.http.HttpClient; import org.opentcs.communication.http.dto.BaseRequestTo; import org.opentcs.communication.http.dto.kc.move.Path; import org.opentcs.communication.http.dto.kc.move.Point; import org.opentcs.communication.http.dto.kc.move.RequestAe; +import org.opentcs.communication.http.entity.OrderInfo; import org.opentcs.drivers.vehicle.MovementCommand; -public class ExecuteMove { +public class ExecuteMove extends BaseService { /** * 订单名映射int类型数据. */ - private static final HashMap orderNameMap = new HashMap<>(); + private static final HashMap orderInfoMap = new HashMap<>(); /** - * 记录上次下发订单名称 + * 通讯适配器当前执行taskkey */ - private static String oldOrderName; - /** - * 当前订单名称对应唯一整数ID - */ - private static Integer currentOrderId = 0; - /** - * 任务ID - * 任务的唯一标识。与订单ID绑定,从1开始,当同一订单下发新的资源时加1;订单ID发生改变,任务KEY需要重新计数。 - */ - private static Integer taskKey; - /** - * 序列号 - * 用于定位点在整个任务中的位置。目的是区分同一个点ID是否在一个任务中出现多次。从0开始偶数递增,例如:0->2->4->6…… - */ - private static Integer pointSerialNum = 0; - /** - * 用于定位段在整个任务中的位置。目的是区分同一个段ID是否在一个任务中出现多次。从1开始奇数递增,例如:1->3->5->7…… - */ - private static Integer pathSerialNum = 1; + private static HashMap execTaskKeyMap = new HashMap<>(); - public static void sendCmd(MovementCommand cmd, Integer serialNum) + /** + * 发送移动指令 + * @param vehicleName 车辆名称 + * @param cmd 订单 + * @param serialNum 序列号 + */ + public static void sendCmd(String vehicleName, MovementCommand cmd, Integer serialNum) { + + String url = getUrl(vehicleName); + // 获取当前时间 LocalDateTime now = LocalDateTime.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); String time = now.format(formatter); - RequestAe ae = buildCommand(cmd); + RequestAe ae = buildCommand(vehicleName, cmd); BaseRequestTo baseRequestTo = new BaseRequestTo(2, "OPENTCS", "KC-CTRL", serialNum, time, ae); HttpClient httpClient = new HttpClient(); - httpClient.sendCommand(baseRequestTo); + httpClient.sendCommand(url, baseRequestTo); } - private static RequestAe buildCommand(MovementCommand cmd) + private static RequestAe buildCommand(String vehicleName, MovementCommand cmd) { + // 自动维护订单ID和任务key + getUniqueOrderID(vehicleName, cmd.getTransportOrder().getName()); + OrderInfo orderInfo = orderInfoMap.get(vehicleName); + //订单ID - int orderID = getUniqueOrderID(cmd.getTransportOrder().getName()); + int orderID = orderInfo.getId(); //下发起点 String sourcePoint = cmd.getStep().getSourcePoint().getName(); int newSourcePoint = Integer.parseInt(sourcePoint); @@ -74,87 +70,122 @@ public class ExecuteMove { pathID = Integer.parseInt(destinationPoint + sourcePoint); } + org.opentcs.data.model.Path pathInfo = cmd.getStep().getPath(); + int maxVelocity = 1; //获取最大速度 + int maxReverseVelocity = 1; //获取最大反向速度 + if (pathInfo != null) { + maxVelocity = pathInfo.getMaxVelocity(); //获取设置速度 + maxReverseVelocity = pathInfo.getMaxReverseVelocity(); + } + RequestAe ae = new RequestAe(); - ae.setOrderId(orderID); - ae.setTaskKey(taskKey); - ae.setPointSize(2); - ae.setPathSize(1); + ae.setWms_order_id(1); //todo 测试填写 + ae.setOrder_id(orderID); + orderInfo.setTaskKey(orderInfo.getTaskKey() + 1); + ae.setTask_key(orderInfo.getTaskKey()); + ae.setPoint_size(2); + ae.setPath_size(1); ArrayList pointList = new ArrayList<>(); - for (int i = 0; i < ae.getPointSize(); i++) { + for (int i = 0; i < ae.getPoint_size(); i++) { Point point = new Point(); if (i == 0) { point.setPoint_id(newSourcePoint); + point.setPoint_serial(orderInfo.getPointSerialNum()); + orderInfo.setPointSerialNum(orderInfo.getPointSerialNum() + 2); } else { point.setPoint_id(newDestination); + point.setPoint_serial(orderInfo.getPointSerialNum()); } - point.setPoint_serial(pointSerialNum); point.setPoint_angle(0); - pointSerialNum = pointSerialNum + 2; - pointList.add(point); } - ae.setPoint(pointList); + ae.setPoint_info(pointList); ArrayList pathList = new ArrayList<>(); - for (int i = 0; i < ae.getPathSize(); i++) { + for (int i = 0; i < ae.getPath_size(); i++) { Path path = new Path(); path.setPath_id(pathID); - path.setPath_serial(pathSerialNum); + path.setPath_serial(orderInfo.getPathSerialNum()); path.setPath_angle(0); - path.setDrive_pose(1); - path.setMax_speed(1f); - path.setMax_angle_speed(1f); + path.setDriver_pose(1); + path.setMax_speed(maxVelocity); + path.setMax_angle_speed(maxVelocity); - pathSerialNum = pathSerialNum + 2; + orderInfo.setPathSerialNum(orderInfo.getPathSerialNum() + 2); pathList.add(path); } - ae.setPath(pathList); + ae.setPath_info(pathList); return ae; } /** * 维护订单名对应int类型唯一ID--------todo 待优化:如果调度重启,控制器也需要重启。否则0xAE指令会因为重置订单ID和任务key下发失败。应改成wms数据库ID交互 - * @param orderName 订单名 + * @param vehicleName 车辆名称 + * @param orderName 订单名称 * @return Integer */ - private static int getUniqueOrderID(String orderName){ + private static void getUniqueOrderID(String vehicleName, String orderName){ - Integer orderId; - if (orderNameMap.containsKey(orderName)) { - //订单名已存在 - orderId = orderNameMap.get(orderName); - } else { //订单名不存在 - //初始化参数 - initParams(); + if (orderInfoMap.containsKey(vehicleName)) { + //当前车辆已存在集合中 + OrderInfo orderInfo = orderInfoMap.get(vehicleName); - //删除上次订单映射唯一ID - orderNameMap.remove(oldOrderName); + if (!Objects.equals(orderName, orderInfo.getName())) { + //订单名称不相等为执行下一个订单,初始化订单对应参数 + orderInfoMap.remove(vehicleName); - //更新记录订单名称 - oldOrderName = orderName; + Integer currentOrderId = orderInfo.getId() + 1; - // 创建对应映射 - orderId = currentOrderId; - currentOrderId++; - orderNameMap.put(orderName, orderId); + orderInfo.setId(currentOrderId); + orderInfo.setName(orderName); + orderInfo.setState(1); + orderInfo.setTaskKey(1); + orderInfo.setPointSerialNum(0); + orderInfo.setPathSerialNum(1); + } + } else { + //车辆不在集合中 + OrderInfo orderInfo = new OrderInfo(); + orderInfo.setId(1); + orderInfo.setName(orderName); + orderInfo.setState(1); + orderInfo.setTaskKey(1); + orderInfo.setPointSerialNum(0); + orderInfo.setPathSerialNum(1); + + orderInfoMap.put(vehicleName, orderInfo); } - - return orderId; } /** - * 初始化参数 + * 设置当前执行任务key */ - private static void initParams() { - taskKey = 1; - pointSerialNum = 0; - pathSerialNum = 1; + public static void setExecTaskKey(String vehicleName, Integer taskKey) { + if (execTaskKeyMap.containsKey(vehicleName)) { + execTaskKeyMap.put(vehicleName, taskKey); + } else { + execTaskKeyMap.put(vehicleName, 1); + } + } + + /** + * 获取任务key + */ + public static Integer getTaskKey(String vehicleName) { + return execTaskKeyMap.get(vehicleName); + } + + /** + * 获取当前订单ID + */ + public static Integer getOrderID(String vehicleName) { + return orderInfoMap.get(vehicleName).getId(); } } diff --git a/opentcs-common/src/main/java/org/opentcs/communication/http/service/ExecuteOperation.java b/opentcs-common/src/main/java/org/opentcs/communication/http/service/ExecuteOperation.java index 3ad8a9f..6edc5eb 100644 --- a/opentcs-common/src/main/java/org/opentcs/communication/http/service/ExecuteOperation.java +++ b/opentcs-common/src/main/java/org/opentcs/communication/http/service/ExecuteOperation.java @@ -1,7 +1,110 @@ package org.opentcs.communication.http.service; -public class ExecuteOperation { +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import org.opentcs.communication.http.HttpClient; +import org.opentcs.communication.http.dto.BaseRequestTo; +import org.opentcs.communication.http.dto.kc.operation.Action01; +import org.opentcs.communication.http.dto.kc.operation.Action02; +import org.opentcs.communication.http.dto.kc.operation.Action03; +import org.opentcs.communication.http.dto.kc.operation.RequestB2; +import org.opentcs.communication.http.enums.Operations; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +public class ExecuteOperation extends BaseService{ + /** + * This class's Logger. + */ + private static final Logger LOG = LoggerFactory.getLogger(ExecuteOperation.class); + + /** + * 暂停执行订单 + * @param vehicleName 车辆名称 + * @param serialNum 序列号 + */ + public static void pauseOrder(String vehicleName, Integer serialNum) { + RequestB2 b2 = new RequestB2(); + + b2.setAction_parallel_manner(2); + b2.setAction_type(Operations.PAUSED.getType()); + b2.setParam_size(4); + + Action01 action01 = new Action01(); + action01.setIsStopImmediately(1); + + b2.setParam_data(action01); + + sendCmd(vehicleName, b2, serialNum); + } + + /** + * 继续执行订单 + * @param vehicleName 车辆名称 + * @param serialNum 序列号 + */ + public static void continueOrder(String vehicleName, Integer serialNum) { + //获取订单ID + Integer orderId = ExecuteMove.getOrderID(vehicleName); + + //获取任务key + Integer taskKey = ExecuteMove.getTaskKey(vehicleName); + + RequestB2 b2 = new RequestB2(); + + b2.setAction_parallel_manner(2); + b2.setAction_type(Operations.CONTINUE.getType()); + b2.setParam_size(8); + + Action02 action02 = new Action02(); + action02.setOrderId(orderId); + action02.setTaskKey(taskKey); + + b2.setParam_data(action02); + + sendCmd(vehicleName, b2, serialNum); + } + + /** + * 撤销订单 + * @param vehicleName 车辆名称 + * @param serialNum 序列号 + */ + public static void cancelOrder(String vehicleName, Integer serialNum) { + + //获取订单ID + Integer orderId = ExecuteMove.getOrderID(vehicleName); + + RequestB2 b2 = new RequestB2(); + + b2.setAction_parallel_manner(2); + b2.setAction_type(Operations.CANCEL.getType()); + b2.setParam_size(8); + + Action03 action03 = new Action03(); + action03.setOrderId(orderId); + action03.setIsStopImmediately(1); + + b2.setParam_data(action03); + + sendCmd(vehicleName, b2, serialNum); + } + + public static void sendCmd(String vehicleName, Object data, Integer serialNum) + { + String url = getUrl(vehicleName); + + // 获取当前时间 + LocalDateTime now = LocalDateTime.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + String time = now.format(formatter); + +// RequestB2 b2 = buildCommand(operation, orderId, taskKey); + BaseRequestTo baseRequestTo = new BaseRequestTo(2, "OPENTCS", "KC-CTRL", serialNum, time, data); + + HttpClient httpClient = new HttpClient(); + httpClient.sendCommand(url, baseRequestTo); + } } diff --git a/opentcs-common/src/main/java/org/opentcs/communication/mqtt/BrokerInterceptor.java b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/BrokerInterceptor.java index 3514d69..6d37323 100644 --- a/opentcs-common/src/main/java/org/opentcs/communication/mqtt/BrokerInterceptor.java +++ b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/BrokerInterceptor.java @@ -22,13 +22,12 @@ import org.slf4j.LoggerFactory; public class BrokerInterceptor implements InterceptHandler { /** - * 客户端ID-定义为车辆名称 + * LOG */ - private String clientID; private static final Logger LOG = LoggerFactory.getLogger(BrokerInterceptor.class); @Override public String getID() { - return clientID; + return "BrokerInterceptor_" + hashCode(); } @Override @@ -46,17 +45,17 @@ public class BrokerInterceptor implements InterceptHandler { @Override public void onConnect(InterceptConnectMessage msg) { - LOG.info("[连接] 客户端ID: {} 连接到服务端", msg.getClientID()); + LOG.info("[connect] client ID: {} connect to server", msg.getClientID()); } @Override public void onDisconnect(InterceptDisconnectMessage msg) { - LOG.info("[断开] 客户端ID: {} 断开连接", msg.getClientID()); + LOG.info("[disconnect] client ID: {} disconnect", msg.getClientID()); } @Override public void onConnectionLost(InterceptConnectionLostMessage interceptConnectionLostMessage) { - + LOG.info("[lost] client ID: {} lost connection", interceptConnectionLostMessage.getClientID()); } @Override @@ -75,21 +74,19 @@ public class BrokerInterceptor implements InterceptHandler { // 必须保留引用计数,防止内存泄漏 payload.retain(); + + MqttBrokerServer mqttBrokerServer = new MqttBrokerServer(); + mqttBrokerServer.sendToClient("v1", MqttQoS.AT_LEAST_ONCE, "hello mqtt client"); } // 其他需要实现的接口方法(可以留空) - @Override public void onSubscribe(InterceptSubscribeMessage msg) {} - @Override public void onUnsubscribe(InterceptUnsubscribeMessage msg) {} - @Override public void onMessageAcknowledged(InterceptAcknowledgedMessage msg) {} - - /** - * 解析消息内容 - * @param msg InterceptPublishMessage - * @return String - */ - private String parsePayload(InterceptPublishMessage msg) { - byte[] payloadBytes = new byte[msg.getPayload().readableBytes()]; - msg.getPayload().getBytes(0, payloadBytes); - return new String(payloadBytes, StandardCharsets.UTF_8); + @Override public void onSubscribe(InterceptSubscribeMessage msg) { + LOG.info("[subscribe] client ID: {} subscribe topic: {}", msg.getClientID(), msg.getTopicFilter()); + } + @Override public void onUnsubscribe(InterceptUnsubscribeMessage msg) { + LOG.info("[unsubscribe] client ID: {} unsubscribe topic: {}", msg.getClientID(), msg.getTopicFilter()); + } + @Override public void onMessageAcknowledged(InterceptAcknowledgedMessage msg) { + LOG.info("[acknowledged] topic: {} acknowledged message: {}", msg.getTopic(), msg.getMsg()); } } diff --git a/opentcs-common/src/main/java/org/opentcs/communication/mqtt/MqttBrokerServer.java b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/MqttBrokerServer.java index b175912..4dd1537 100644 --- a/opentcs-common/src/main/java/org/opentcs/communication/mqtt/MqttBrokerServer.java +++ b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/MqttBrokerServer.java @@ -4,6 +4,7 @@ import io.moquette.broker.Server; import io.moquette.broker.config.IConfig; import io.moquette.broker.config.MemoryConfig; import io.moquette.broker.subscriptions.Topic; +import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.handler.codec.mqtt.MqttFixedHeader; import io.netty.handler.codec.mqtt.MqttMessageType; @@ -72,36 +73,47 @@ public class MqttBrokerServer { * @param message 消息 */ public void sendToClient(String clientId, MqttQoS qosLevel, String message) { -// LOG.info("Sending message to client: {}, MqttQoS: {}, message: {}", clientId, qosLevel, message); -// -// String topicPath = TopicBuilder.buildCommandTopic(clientId); -// Topic topic = new Topic(topicPath); -// -// MqttFixedHeader fixedHeader = new MqttFixedHeader( -// MqttMessageType.PUBLISH, // 消息类型 -// false, // 是否重复发送 -// qosLevel, // 服务质量等级: QoS 0 – 最多交付一次,可能丢失消息; QoS 1 – 至少交付一次,可以保证收到消息,但消息可能重复; QoS 2 – 只交付一次,使用 QoS 2 可以保证消息既不丢失也不重复,开销最高 -// false, // 是否保留消息 -// 0 // 剩余长度(自动计算) -// ); -// -// MqttPublishVariableHeader variableHeader = new MqttPublishVariableHeader( -// topic.toString(), // 主题 -// MqttQoS.AT_LEAST_ONCE.value() // 消息ID(QoS>0时需要) -// ); -// -// // 将消息内容转换为ByteBuf -// byte[] payload = message.getBytes(StandardCharsets.UTF_8); -// MqttPublishMessage publishMessage = new MqttPublishMessage( -// fixedHeader, -// variableHeader, -// Unpooled.wrappedBuffer(payload) -// ); -// -// // 发布消息到指定客户端 -// mqttServer.internalPublish( -// publishMessage, -// clientId // 指定接收客户端ID -// ); + LOG.info("Sending message to client: {}, MqttQoS: {}, message: {}", clientId, qosLevel, message); + + + String topicPath = TopicBuilder.buildCommandTopic(clientId); + Topic topic = new Topic(topicPath); + + MqttFixedHeader fixedHeader = new MqttFixedHeader( + MqttMessageType.PUBLISH, // 消息类型 + false, // 是否重复发送 + qosLevel, // 服务质量等级: QoS 0 – 最多交付一次,可能丢失消息; QoS 1 – 至少交付一次,可以保证收到消息,但消息可能重复; QoS 2 – 只交付一次,使用 QoS 2 可以保证消息既不丢失也不重复,开销最高 + false, // 是否保留消息 + 0 // 剩余长度(自动计算) + ); + + MqttPublishVariableHeader variableHeader = new MqttPublishVariableHeader( + topic.toString(), // 主题 + qosLevel.value() // 服务质量等级 + ); + + // 将消息内容转换为ByteBuf + byte[] payload = message.getBytes(StandardCharsets.UTF_8); + ByteBuf payloadBuf = Unpooled.copiedBuffer(payload); + MqttPublishMessage publishMessage = new MqttPublishMessage( + fixedHeader, + variableHeader, + payloadBuf + ); + + + try { + // 发布消息到指定客户端 + mqttServer.internalPublish( + publishMessage, + clientId // 指定接收客户端ID + ); + LOG.debug("Sent to {} ,topic {} QoS:{} message {}", + clientId, topic, qosLevel, message); + } finally { + if (!publishMessage.payload().isReadable()) { + publishMessage.payload().release(); // 确保释放 + } + } } } diff --git a/opentcs-common/src/main/java/org/opentcs/communication/mqtt/MqttClientExample.java b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/MqttClientExample.java index fc2dcc9..e9f313a 100644 --- a/opentcs-common/src/main/java/org/opentcs/communication/mqtt/MqttClientExample.java +++ b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/MqttClientExample.java @@ -35,8 +35,11 @@ public class MqttClientExample { // 发布消息 String content = "Hello, MQTT"; - client.publish(topic, content.getBytes(), qos, false); - System.out.println("Published message: " + content); + + for (int i = 0; i < 10; i++) { + client.publish(topic, content.getBytes(), qos, false); + Thread.sleep(300); + } // 等待接收消息 Thread.sleep(5000); diff --git a/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/BaseRequestTo.java b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/BaseRequestTo.java new file mode 100644 index 0000000..aa89703 --- /dev/null +++ b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/BaseRequestTo.java @@ -0,0 +1,44 @@ +package org.opentcs.communication.mqtt.entity; + +import jakarta.annotation.Nonnull; +import lombok.Data; + +/** + * @author xuzhiheng + * @date 2025/5/12 + * @desc 实体基类 + */ +@Data +public class BaseRequestTo { + + public BaseRequestTo( + @Nonnull Integer type, + @Nonnull String sender, + @Nonnull String receiver, + @Nonnull Integer serial_num, + @Nonnull String time, + @Nonnull Object data + ) { + this.type = type; + this.sender = sender; + this.receiver = receiver; + this.data = data; + } + + @Nonnull + private Integer type; + + @Nonnull + private String sender; + + @Nonnull + private String receiver; + + @Nonnull + private Integer serial_num; + + @Nonnull + private String time; + + private Object data; +} diff --git a/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/action/RequestAction.java b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/action/RequestAction.java new file mode 100644 index 0000000..b755f01 --- /dev/null +++ b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/action/RequestAction.java @@ -0,0 +1,16 @@ +package org.opentcs.communication.mqtt.entity.kc.action; + +import lombok.Data; + +/** + * @author xuzhiheng + */ +@Data +public class RequestAction { + + /** + * 动作 + */ + private String action; + +} diff --git a/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/move/Path.java b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/move/Path.java new file mode 100644 index 0000000..c19e066 --- /dev/null +++ b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/move/Path.java @@ -0,0 +1,37 @@ +package org.opentcs.communication.mqtt.entity.kc.move; + +import lombok.Data; + +/** + * @author xuzhiheng + * @date 2025/5/12 + * @desc 实体类 + */ +@Data +public class Path { + + /** + * 路径ID + */ + private Integer path_id; + /** + * 路径序列号: 从 1 开始奇数递增,例如:1->3->5->7…… + */ + private Integer path_serial; + /** + * 机器人固定角度: 0 = 不指定 + */ + private float path_angle; + /** + * 行驶姿态: 0x0 正走,0x1 倒走,0x02 左横移(支持横移类底盘进,行二维码导航时生效),0x03 右横移(支持横移类底盘进行二维码导航时生效) + */ + private Integer drive_pose; + /** + * 指定的目标最大速度 + */ + private float max_speed; + /** + * 指定的目标最大角速度 + */ + private float max_angle_speed; +} diff --git a/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/move/Point.java b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/move/Point.java new file mode 100644 index 0000000..d0097f7 --- /dev/null +++ b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/move/Point.java @@ -0,0 +1,25 @@ +package org.opentcs.communication.mqtt.entity.kc.move; + +import lombok.Data; + +/** + * @author xuzhiheng + * @date 2025/5/12 + * @desc 实体类 + */ +@Data +public class Point { + + /** + * 点ID + */ + private Integer point_id; + /** + * 点序列号: 从 0开始偶数递增,例如:0->2->4->6…… + */ + private Integer point_serial; + /** + * 角度:0=不指定角度 + */ + private double point_angle; +} diff --git a/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/move/RequestAe.java b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/move/RequestAe.java new file mode 100644 index 0000000..63ab891 --- /dev/null +++ b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/move/RequestAe.java @@ -0,0 +1,45 @@ +package org.opentcs.communication.mqtt.entity.kc.move; + +import jakarta.annotation.Nonnull; +import java.util.ArrayList; +import lombok.Data; + +/** + * @author xuzhiheng + * @date 2025/5/12 + * @desc 实体类 + */ +@Data +public class RequestAe { + + public RequestAe() {} + + /** + * 订单ID + */ + @Nonnull + private Integer orderId; + /** + * 任务ID task_key: 任务的唯一标识。与订单 ID 绑定,从 1 开始,当同一订单下发新的资源时加 1; + */ + @Nonnull + private Integer taskKey; + /** + * 点个数 point_size + */ + @Nonnull + private Integer pointSize; + /** + * 路径个数 path_size + */ + @Nonnull + private Integer pathSize; + /** + * 点信息结构 + */ + private ArrayList point; + /** + * 路线信息结构 + */ + private ArrayList path; +} diff --git a/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/operation/Action01.java b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/operation/Action01.java new file mode 100644 index 0000000..689cbf6 --- /dev/null +++ b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/operation/Action01.java @@ -0,0 +1,17 @@ +package org.opentcs.communication.mqtt.entity.kc.operation; + +import lombok.Data; + +/** + * @author xuzhiheng + * @date 2025/5/14 + * @desc 实体类 + */ +@Data +public class Action01 { + + /** + * 是否立即停止移动: 0:机器人正常移动到点上停止(停不下来就移动到下一个点), 1:立刻停止(缓停); + */ + private Integer isStopImmediately; +} diff --git a/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/operation/Action02.java b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/operation/Action02.java new file mode 100644 index 0000000..f0ae1e2 --- /dev/null +++ b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/operation/Action02.java @@ -0,0 +1,21 @@ +package org.opentcs.communication.mqtt.entity.kc.operation; + +import lombok.Data; + +/** + * @author xuzhiheng + * @date 2025/5/13 + * @desc 实体类 + */ +@Data +public class Action02 { + + /** + * 订单ID + */ + private Integer orderId; + /** + * 任务key + */ + private Integer taskKey; +} diff --git a/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/operation/Action03.java b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/operation/Action03.java new file mode 100644 index 0000000..4ba0ee7 --- /dev/null +++ b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/operation/Action03.java @@ -0,0 +1,21 @@ +package org.opentcs.communication.mqtt.entity.kc.operation; + +import lombok.Data; + +/** + * @author xuzhiheng + * @date 2025/5/13 + * @desc 实体类 + */ +@Data +public class Action03 { + + /** + * 订单ID + */ + private Integer orderId; + /** + * 是否立即停止移动: 1 立刻停止(缓停);0 AGV 正常移动到点上停止(停不下来就移动到下一个点) + */ + private Integer isStopImmediately; +} diff --git a/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/operation/RequestB2.java b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/operation/RequestB2.java new file mode 100644 index 0000000..421a65f --- /dev/null +++ b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/entity/kc/operation/RequestB2.java @@ -0,0 +1,25 @@ +package org.opentcs.communication.mqtt.entity.kc.operation; + +import lombok.Data; + +/** + * @author xuzhiheng + * @date 2025/5/14 + * @desc 实体类 + */ +@Data +public class RequestB2 { + + /** + * 动作类型-》填写动作命令码 + */ + private Integer actionType; + /** + * 执行动作并行方式: 0x00:为移动和动作间都可并行, 0x01:为动作间可以并行,不能移动, 0x02 只能执行当前动作 + */ + private Integer actionParallelManner; + /** + * 参数内容长度 + */ + private Integer paramSize; +} diff --git a/opentcs-common/src/main/java/org/opentcs/communication/mqtt/enums/Actions.java b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/enums/Actions.java new file mode 100644 index 0000000..37a1b9e --- /dev/null +++ b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/enums/Actions.java @@ -0,0 +1,29 @@ +package org.opentcs.communication.mqtt.enums; + +public enum Actions { + + //取请求 + PICK_UP_REQUEST, + //放请求 + RELEASE_REQUEST, + //充电 + CHARGE, + //取消充电 + CANCEL_CHARGE + ; + + /** + * 判断是否存在当前动作 + * @param value + * @return 存在返回true,不存在返回false + */ + public static boolean contains(String value) { + for (Actions actions : Actions.values()) { + if (actions.name().equals(value)) { + return true; + } + } + return false; + } + +} diff --git a/opentcs-common/src/main/java/org/opentcs/communication/mqtt/enums/KcActionCmdEnum.java b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/enums/KcActionCmdEnum.java new file mode 100644 index 0000000..32d1afd --- /dev/null +++ b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/enums/KcActionCmdEnum.java @@ -0,0 +1,75 @@ +package org.opentcs.communication.mqtt.enums; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author xuzhiheng + * @date 2025/5/14 + * @desc 将十进制value,绑定到小端模式16进制key + */ +public enum KcActionCmdEnum { + + CMD_ACTION_0x01(0x01, 1), //暂停车辆 + CMD_ACTION_0x02(0x02, 2), //恢复订单执行 + CMD_ACTION_0x03(0x03, 3); //取消任务 + + private Integer key; + private Integer value; + + KcActionCmdEnum(Integer key, Integer value) { + this.key = key; + this.value = value; + } + + public Integer getKey() { + return key; + } + + public void setKey(Integer key) { + this.key = key; + } + + public Integer getValue() { + return value; + } + + public void setValue(Integer value) { + this.value = value; + } + + // 静态缓存,提升查询性能 + private static final Map KEY_MAP = new HashMap<>(); + + static { + // 初始化时将所有枚举实例的键值存入Map + for (KcActionCmdEnum entry : values()) { + if (KEY_MAP.containsKey(entry.key)) { + throw new IllegalStateException("REPEATED KEY: " + entry.key); + } + KEY_MAP.put(entry.key, entry); + } + } + + /** + * 根据key获取对应的value + * @param key 要查找的键 + * @return 对应的值 + * @throws IllegalArgumentException 如果key不存在 + */ + public static Integer getValueByKey(Integer key) { + KcActionCmdEnum entry = KEY_MAP.get(key); + if (entry == null) { + throw new IllegalArgumentException("无效的key: " + key); + } + return entry.value; + } + + /** + * 根据key获取完整的枚举实例 + */ + public static KcActionCmdEnum getEntryByKey(Integer key) { + return KEY_MAP.get(key); + } + +} diff --git a/opentcs-common/src/main/java/org/opentcs/communication/mqtt/enums/KcCmdEnum.java b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/enums/KcCmdEnum.java new file mode 100644 index 0000000..3677f57 --- /dev/null +++ b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/enums/KcCmdEnum.java @@ -0,0 +1,86 @@ +package org.opentcs.communication.mqtt.enums; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author xuzhiheng + * @date 2025/5/14 + * @desc 将十进制value,绑定到小端模式16进制key + */ +public enum KcCmdEnum { + + CMD_0x02(0x02, 2), //读多变量 + CMD_0x03(0x03, 3), //写多变量 + CMD_0x14(0x14, 20), //执行机器人手动定位 + CMD_0x17(0x17, 23), //查询机器人运行状态 + CMD_0x1F(0x1F, 31), //确认初始位置 + CMD_0xAE(0xAE, 174), //导航指令 + CMD_0xAF(0xAF, 175), //获取AGV详细信息 + CMD_0xB0(0xB0, 176), //查询载货状态 + CMD_0xB2(0xB2, 178); //订阅AGV信息 + + /** + * 小端模式十六进制命令码 + */ + private Integer key; + /** + * 十进制命令码 + */ + private Integer value; + + KcCmdEnum(Integer key, Integer value) { + this.key = key; + this.value = value; + } + + public Integer getKey() { + return key; + } + + public void setKey(Integer key) { + this.key = key; + } + + public Integer getValue() { + return value; + } + + public void setValue(Integer value) { + this.value = value; + } + + // 静态缓存,提升查询性能 + private static final Map KEY_MAP = new HashMap<>(); + + static { + // 初始化时将所有枚举实例的键值存入Map + for (KcCmdEnum entry : values()) { + if (KEY_MAP.containsKey(entry.key)) { + throw new IllegalStateException("REPEATED KEY: " + entry.key); + } + KEY_MAP.put(entry.key, entry); + } + } + + /** + * 根据key获取对应的value + * @param key 要查找的键 + * @return 对应的值 + * @throws IllegalArgumentException 如果key不存在 + */ + public static Integer getValueByKey(Integer key) { + KcCmdEnum entry = KEY_MAP.get(key); + if (entry == null) { + throw new IllegalArgumentException("无效的key: " + key); + } + return entry.value; + } + + /** + * 根据key获取完整的枚举实例 + */ + public static KcCmdEnum getEntryByKey(Integer key) { + return KEY_MAP.get(key); + } +} diff --git a/opentcs-common/src/main/java/org/opentcs/communication/mqtt/service/ExecuteAction.java b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/service/ExecuteAction.java new file mode 100644 index 0000000..b6c24c4 --- /dev/null +++ b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/service/ExecuteAction.java @@ -0,0 +1,36 @@ +package org.opentcs.communication.mqtt.service; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import org.opentcs.communication.mqtt.entity.BaseRequestTo; +import org.opentcs.communication.mqtt.entity.kc.action.RequestAction; + +public class ExecuteAction { + + /** + * 下发动作到平台 + * @param action 动作 + */ + public static void sendCmd(String action, Integer serialNum) { + + // 获取当前时间 + LocalDateTime now = LocalDateTime.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + String time = now.format(formatter); + + RequestAction requestAction = new RequestAction(); + requestAction.setAction(action); + + BaseRequestTo baseRequestTo = new BaseRequestTo( + 4, + "OPENTCS", + "KC-CTRL", + serialNum, + time, + requestAction + ); + + //发送mqtt消息 + } + +} diff --git a/opentcs-common/src/main/java/org/opentcs/communication/mqtt/service/ExecuteMove.java b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/service/ExecuteMove.java new file mode 100644 index 0000000..239005c --- /dev/null +++ b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/service/ExecuteMove.java @@ -0,0 +1,168 @@ +package org.opentcs.communication.mqtt.service; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.HashMap; +import org.opentcs.communication.mqtt.entity.BaseRequestTo; +import org.opentcs.communication.mqtt.entity.kc.move.Path; +import org.opentcs.communication.mqtt.entity.kc.move.Point; +import org.opentcs.communication.mqtt.entity.kc.move.RequestAe; +import org.opentcs.drivers.vehicle.MovementCommand; + +public class ExecuteMove { + + /** + * 订单名映射int类型数据. + */ + private static final HashMap orderNameMap = new HashMap<>(); + /** + * 记录上次下发订单名称 + */ + private static String oldOrderName; + /** + * 当前订单名称对应唯一整数ID + */ + private static Integer currentOrderId = 0; + /** + * 任务ID + * 任务的唯一标识。与订单ID绑定,从1开始,当同一订单下发新的资源时加1;订单ID发生改变,任务KEY需要重新计数。 + */ + private static Integer taskKey; + /** + * 序列号 + * 用于定位点在整个任务中的位置。目的是区分同一个点ID是否在一个任务中出现多次。从0开始偶数递增,例如:0->2->4->6…… + */ + private static Integer pointSerialNum = 0; + /** + * 用于定位段在整个任务中的位置。目的是区分同一个段ID是否在一个任务中出现多次。从1开始奇数递增,例如:1->3->5->7…… + */ + private static Integer pathSerialNum = 1; + + public static void sendCmd(MovementCommand cmd, Integer serialNum) + { + // 获取当前时间 + LocalDateTime now = LocalDateTime.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + String time = now.format(formatter); + + RequestAe ae = buildCommand(cmd); + BaseRequestTo baseRequestTo = new BaseRequestTo(2, "OPENTCS", "KC-CTRL", serialNum, time, ae); + +// HttpClient httpClient = new HttpClient(); +// httpClient.sendCommand(baseRequestTo); + } + + private static RequestAe buildCommand(MovementCommand cmd) + { + //订单ID + int orderID = getUniqueOrderID(cmd.getTransportOrder().getName()); + //下发起点 + String sourcePoint = cmd.getStep().getSourcePoint().getName(); + int newSourcePoint = Integer.parseInt(sourcePoint); + //下发终点 + String destinationPoint = cmd.getStep().getDestinationPoint().getName(); + int newDestination = Integer.parseInt(destinationPoint); + + //获取路径ID + int pathID; + if (newSourcePoint < newDestination) { + pathID = Integer.parseInt(sourcePoint + destinationPoint); + } else { + pathID = Integer.parseInt(destinationPoint + sourcePoint); + } + + org.opentcs.data.model.Path pathInfo = cmd.getStep().getPath(); + int maxVelocity = 1; //获取最大速度 + int maxReverseVelocity = 1; //获取最大反向速度 + if (pathInfo != null) { + maxVelocity = pathInfo.getMaxVelocity(); //获取设置速度 + maxReverseVelocity = pathInfo.getMaxReverseVelocity(); + } + + RequestAe ae = new RequestAe(); + ae.setOrderId(orderID); + ae.setTaskKey(taskKey); + ae.setPointSize(2); + ae.setPathSize(1); + + ArrayList pointList = new ArrayList<>(); + for (int i = 0; i < ae.getPointSize(); i++) { + Point point = new Point(); + + if (i == 0) { + point.setPoint_id(newSourcePoint); + } else { + point.setPoint_id(newDestination); + } + point.setPoint_serial(pointSerialNum); + point.setPoint_angle(0); + + pointSerialNum = pointSerialNum + 2; + + pointList.add(point); + } + ae.setPoint(pointList); + + ArrayList pathList = new ArrayList<>(); + for (int i = 0; i < ae.getPathSize(); i++) { + Path path = new Path(); + + path.setPath_id(pathID); + path.setPath_serial(pathSerialNum); + path.setPath_angle(0); + path.setDrive_pose(1); + path.setMax_speed(maxVelocity); + path.setMax_angle_speed(maxVelocity); + + pathSerialNum = pathSerialNum + 2; + + pathList.add(path); + } + ae.setPath(pathList); + + return ae; + } + + /** + * 维护订单名对应int类型唯一ID--------todo 待优化:如果调度重启,控制器也需要重启。否则0xAE指令会因为重置订单ID和任务key下发失败。应改成wms数据库ID交互 + * @param orderName 订单名 + * @return Integer + */ + private static int getUniqueOrderID(String orderName){ + + Integer orderId; + + if (orderNameMap.containsKey(orderName)) { + //订单名已存在 + orderId = orderNameMap.get(orderName); + } else { //订单名不存在 + //初始化参数 + initParams(); + + if (oldOrderName != null) { + //删除上次订单映射唯一ID + orderNameMap.remove(oldOrderName); + } + + //更新记录订单名称 + oldOrderName = orderName; + + // 创建对应映射 + orderId = currentOrderId; + currentOrderId++; + orderNameMap.put(orderName, orderId); + } + + return orderId; + } + + /** + * 初始化参数 + */ + private static void initParams() { + taskKey = 1; + pointSerialNum = 0; + pathSerialNum = 1; + } +} diff --git a/opentcs-common/src/main/java/org/opentcs/communication/mqtt/service/ExecuteOperation.java b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/service/ExecuteOperation.java new file mode 100644 index 0000000..32645a4 --- /dev/null +++ b/opentcs-common/src/main/java/org/opentcs/communication/mqtt/service/ExecuteOperation.java @@ -0,0 +1,7 @@ +package org.opentcs.communication.mqtt.service; + +public class ExecuteOperation { + + + +} diff --git a/opentcs-common/src/main/java/org/opentcs/manage/AdapterManage.java b/opentcs-common/src/main/java/org/opentcs/manage/AdapterManage.java index bf3b597..bc3783f 100644 --- a/opentcs-common/src/main/java/org/opentcs/manage/AdapterManage.java +++ b/opentcs-common/src/main/java/org/opentcs/manage/AdapterManage.java @@ -8,7 +8,11 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import org.opentcs.access.KernelServicePortal; +import org.opentcs.access.rmi.KernelServicePortalBuilder; import org.opentcs.common.GuestUserCredentials; +import org.opentcs.components.kernel.services.VehicleService; +import org.opentcs.data.model.Vehicle; import org.opentcs.manage.entity.AgvInfo; import org.opentcs.manage.entity.AgvInfoParams; import org.opentcs.manage.entity.AgvStatus; @@ -41,7 +45,7 @@ public class AdapterManage { /** * 设置自动关闭适配器时间阈值,单位:毫秒 */ - private final Long AUTO_CLOSE_TIME = 2000L; + private static final Long AUTO_CLOSE_TIME = 2000L; /** * 记录通讯适配器数据,实现异步不阻塞更新车辆模型 * 基于线程安全HASHMAP @@ -54,10 +58,12 @@ public class AdapterManage { private static final String IP = GuestUserCredentials.IP; private static final Integer PORT = GuestUserCredentials.PORT; + private int i = 1; + //开启定时任务 public void START() { - scheduler.scheduleWithFixedDelay(task, 0, 500, TimeUnit.MILLISECONDS); + scheduler.scheduleWithFixedDelay(task, 5000, 500, TimeUnit.MILLISECONDS); } //关闭定时任务 @@ -66,51 +72,88 @@ public class AdapterManage { } Runnable task = () -> { - kernel = new KernelCommunication(USER, PASSWORD, IP, PORT); +// kernel = new KernelCommunication(USER, PASSWORD, IP, PORT); - autoManageAdapterStatus(); - updateAdapterVehicleModel(); +// autoManageAdapterStatus(); +// updateAdapterVehicleModel(); kernel.logout(); + +// System.out.println("end task"); }; /** * 自动管理通讯适配器 */ - private void autoManageAdapterStatus() { + private static boolean autoManageAdapterStatus(String name) { Date date = new Date(); long currentTime = date.getTime(); - adapterStatusMap.forEach((key, value) -> { - Long time = value.getTime(); - AgvStatus agvStatus = new AgvStatus(); - agvStatus.setTime(value.getTime()); - agvStatus.setActionStatus(value.getActionStatus()); + System.out.println("autoManageAdapterStatus: " + adapterStatusMap); - if (currentTime - time > AUTO_CLOSE_TIME) { - agvStatus.setStatus(AdapterStatus.DISABLE); - } else { - agvStatus.setStatus(AdapterStatus.ENABLE); - } + AgvStatus oldData = adapterStatusMap.get(name); - LOG.info("update the adapter: {} status:{}", key, value); - kernel.sendToAdapter(key, agvStatus); + if (oldData == null) { + return false; + } - if (agvStatus.getActionStatus()) { - //动作执行结束,修改数据 - agvStatus.setActionStatus(false); - } + AgvStatus agvStatus = new AgvStatus(); + agvStatus.setTime(oldData.getTime()); - //更新记录数据 - adapterStatusMap.put(key, agvStatus); - }); + if (currentTime - oldData.getTime() > AUTO_CLOSE_TIME) { + System.out.println("adapterStatusMap first DISABLE"); + agvStatus.setStatus(AdapterStatus.DISABLE); + } else { + System.out.println("adapterStatusMap first ENABLE"); + agvStatus.setStatus(AdapterStatus.ENABLE); + } + + //更新记录数据 + adapterStatusMap.put(name, agvStatus); + + return agvStatus.getStatus(); } +// private void autoManageAdapterStatus() { +// Date date = new Date(); +// long currentTime = date.getTime(); +// +// System.out.println("autoManageAdapterStatus: " + adapterStatusMap); +// adapterStatusMap.forEach((key, value) -> { +// System.out.println("adapterStatusMap first"); +// Long time = value.getTime(); +// AgvStatus agvStatus = new AgvStatus(); +// agvStatus.setTime(value.getTime()); +// agvStatus.setActionStatus(value.getActionStatus()); +// +// if (currentTime - time > AUTO_CLOSE_TIME) { +// System.out.println("adapterStatusMap first DISABLE"); +// agvStatus.setStatus(AdapterStatus.DISABLE); +// kernel.disableAdapter(key); +// } else { +// System.out.println("adapterStatusMap first ENABLE"); +// agvStatus.setStatus(AdapterStatus.ENABLE); +// kernel.enableAdapter(key); +// } +// +// LOG.info("update the adapter: {} status:{}", key, value); +// +// if (agvStatus.getActionStatus()) { +// kernel.sendToAdapter(key, agvStatus); +// //动作执行结束,修改数据 +// agvStatus.setActionStatus(false); +// } +// +// //更新记录数据 +// adapterStatusMap.put(key, agvStatus); +// }); +// } /** * 更新适配器车辆模型 */ private void updateAdapterVehicleModel() { adapterDataMap.forEach((key, value) -> { + System.out.println("updateAdapterVehicleModel first"); kernel.sendToAdapter(key, value); adapterDataMap.remove(key); }); @@ -125,45 +168,48 @@ public class AdapterManage { long time = date.getTime(); AgvStatus newAgvStatus = new AgvStatus(); + newAgvStatus.setTime(time); - if (adapterStatusMap.containsKey(name)) { + if (!adapterStatusMap.isEmpty() && adapterStatusMap.containsKey(name)) { //已记录,只更新时间 - AgvStatus agvStatus = adapterStatusMap.get(name); - newAgvStatus.setTime(time); - newAgvStatus.setStatus(agvStatus.getStatus()); + newAgvStatus.setStatus(AdapterStatus.ENABLE); } else { //未记录,初始化 - newAgvStatus.setTime(time); newAgvStatus.setStatus(AdapterStatus.DISABLE); } adapterStatusMap.put(name, newAgvStatus); } + public static boolean getAdapterStatus(String name) { + + AgvStatus agvStatus = adapterStatusMap.get(name); + if (agvStatus != null) { + return agvStatus.getStatus(); + } + + return false; + } + /** * 设置动作完成状态 * @param name 车辆名称 */ - public static void setActionStatus(String name) { - AgvStatus agvStatus = adapterStatusMap.get(name); - agvStatus.setActionStatus(true); - adapterStatusMap.put(name, agvStatus); - } +// public static void setActionStatus(String name) { +// AgvStatus agvStatus = adapterStatusMap.get(name); +// agvStatus.setActionStatus(true); +// adapterStatusMap.put(name, agvStatus); +// } /** * 记录对应通讯适配器最后一次上报数据 * @param name 车辆名称 * @param data 数据 */ - public static void setAdapterVehicleModel(String name, String data) { + public static AgvInfo setAdapterVehicleModel(String name, String data) { JSONObject jsonObject = JSON.parseObject(data); - //因为是异步上报。所以需要校验序列号。防止旧数据覆盖新数据 - if (adapterDataMap.get(name).getSerial_num() > jsonObject.getInteger("serial_num")) { - return; - } - AgvInfo agvInfo = new AgvInfo(); agvInfo.setSender(jsonObject.getString("sender")); agvInfo.setReceiver(jsonObject.getString("receiver")); @@ -172,7 +218,7 @@ public class AdapterManage { agvInfo.setTime(jsonObject.getString("time")); agvInfo.setParams(getAgvInfoParams(jsonObject.getString("params"))); - adapterDataMap.put(name, agvInfo); + return agvInfo; } private static AgvInfoParams getAgvInfoParams(String paramsStr) { @@ -202,4 +248,13 @@ public class AdapterManage { Boolean DISABLE = false; } + private static void test() { + KernelServicePortal build = new KernelServicePortalBuilder(USER, PASSWORD).build(); + build.login(IP, PORT); + VehicleService vehicleService = build.getVehicleService(); + Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, "v1"); + vehicleService.enableCommAdapter(vehicle.getReference()); + vehicleService.sendCommAdapterMessage(vehicle.getReference(), "test"); + } + } diff --git a/opentcs-common/src/main/java/org/opentcs/manage/entity/ActionStatus.java b/opentcs-common/src/main/java/org/opentcs/manage/entity/ActionStatus.java new file mode 100644 index 0000000..4b32d97 --- /dev/null +++ b/opentcs-common/src/main/java/org/opentcs/manage/entity/ActionStatus.java @@ -0,0 +1,13 @@ +package org.opentcs.manage.entity; + +import lombok.Data; + +@Data +public class ActionStatus { + + /** + * 动作执行状态,true表示执行完成,false表示执行中 + */ + private Boolean status; + +} diff --git a/opentcs-common/src/main/java/org/opentcs/manage/entity/AgvStatus.java b/opentcs-common/src/main/java/org/opentcs/manage/entity/AgvStatus.java index 0bae3b7..304e27f 100644 --- a/opentcs-common/src/main/java/org/opentcs/manage/entity/AgvStatus.java +++ b/opentcs-common/src/main/java/org/opentcs/manage/entity/AgvStatus.java @@ -13,9 +13,5 @@ public class AgvStatus { * 通讯适配器状态:trye=开启,false=关闭 */ private Boolean status; - /** - * 动作状态:true=上报状态,false=无任务 - */ - private Boolean actionStatus; } diff --git a/opentcs-common/src/main/java/org/opentcs/park/ParkStrategy.java b/opentcs-common/src/main/java/org/opentcs/park/ParkStrategy.java index 30fabd4..5d2b8cb 100644 --- a/opentcs-common/src/main/java/org/opentcs/park/ParkStrategy.java +++ b/opentcs-common/src/main/java/org/opentcs/park/ParkStrategy.java @@ -1,21 +1,20 @@ package org.opentcs.park; import java.util.ArrayList; -import java.util.HashMap; import org.opentcs.park.entity.Park; public class ParkStrategy { - private static ArrayList AllPark = new ArrayList<>(); + private static ArrayList allPark = new ArrayList<>(); public static void setPark(Park park) { - AllPark.add(park); + allPark.add(park); printPark(); } private static void printPark() { - for (Park park : AllPark) { + for (Park park : allPark) { System.out.println("print park:" + park.getName() + ", status:" + park.getStatus()); } } diff --git a/opentcs-common/src/main/java/org/opentcs/traffic/TrafficControl.java b/opentcs-common/src/main/java/org/opentcs/traffic/TrafficControl.java index 2478aa7..fb32544 100644 --- a/opentcs-common/src/main/java/org/opentcs/traffic/TrafficControl.java +++ b/opentcs-common/src/main/java/org/opentcs/traffic/TrafficControl.java @@ -1,8 +1,12 @@ package org.opentcs.traffic; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import org.opentcs.traffic.common.AvoidanceAlgorithm; import org.opentcs.traffic.common.ContourAlgorithm; import org.opentcs.traffic.common.ResourceLock; +import org.opentcs.util.KernelCommunication; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,6 +33,26 @@ public class TrafficControl { * 交管避让权重算法 */ private static final AvoidanceAlgorithm avoidanceAlgorithm = new AvoidanceAlgorithm(); + /** + * 创建调度线程池(单线程) + */ + private static final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); + + //开启定时任务 + public void START() { + scheduler.scheduleWithFixedDelay(task, 0, 500, TimeUnit.MILLISECONDS); + } + + //关闭定时任务 + public void STOP() { + scheduler.shutdownNow(); + } + + Runnable task = () -> { + //轮廓避障算法 + }; + + /** * 交通管制策略 @@ -41,25 +65,25 @@ public class TrafficControl { public static boolean trafficControlStrategy(String vehiclePosition, String finalOccupiedPosition, String resource, String type) { //进行资源占用 - boolean lockStatus = resourceLock.trafficControlOccupy(vehiclePosition, finalOccupiedPosition, resource, type); - if (!lockStatus) { - LOG.debug("trafficControlStrategy Failed to lock resource: {} type: {}", resource, type); - return false; - } +// boolean lockStatus = resourceLock.trafficControlOccupy(vehiclePosition, finalOccupiedPosition, resource, type); +// if (!lockStatus) { +// LOG.debug("trafficControlStrategy Failed to lock resource: {} type: {}", resource, type); +// return false; +// } //todo: 轮廓算法,判断下一个点位是否会被其他车辆干涉 - boolean contourStatus = contourAlgorithm.interferenceDetection(); - if (!contourStatus) { - LOG.debug("trafficControlStrategy Contour algorithm failed: resource: {} type: {}", resource, type); - return false; - } +// boolean contourStatus = contourAlgorithm.interferenceDetection(); +// if (!contourStatus) { +// LOG.debug("trafficControlStrategy Contour algorithm failed: resource: {} type: {}", resource, type); +// return false; +// } //todo: 订单避让算法,根据权重计算车辆优先级执行避让算法 - boolean avoidanceStatus = avoidanceAlgorithm.weightCalculation(); - if (!avoidanceStatus) { - LOG.debug("trafficControlStrategy Avoidance algorithm failed: resource: {} type: {}", resource, type); - return false; - } +// boolean avoidanceStatus = avoidanceAlgorithm.weightCalculation(); +// if (!avoidanceStatus) { +// LOG.debug("trafficControlStrategy Avoidance algorithm failed: resource: {} type: {}", resource, type); +// return false; +// } //有所有校验通过,返回true return true; diff --git a/opentcs-common/src/main/java/org/opentcs/traffic/common/ContourAlgorithm.java b/opentcs-common/src/main/java/org/opentcs/traffic/common/ContourAlgorithm.java index 354bfeb..4940a13 100644 --- a/opentcs-common/src/main/java/org/opentcs/traffic/common/ContourAlgorithm.java +++ b/opentcs-common/src/main/java/org/opentcs/traffic/common/ContourAlgorithm.java @@ -1,5 +1,7 @@ package org.opentcs.traffic.common; +import java.util.HashMap; +import org.opentcs.traffic.entity.VehicleAttr; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/opentcs-common/src/main/java/org/opentcs/traffic/common/VehicleCollisionAvoidance.java b/opentcs-common/src/main/java/org/opentcs/traffic/common/VehicleCollisionAvoidance.java new file mode 100644 index 0000000..5b900b5 --- /dev/null +++ b/opentcs-common/src/main/java/org/opentcs/traffic/common/VehicleCollisionAvoidance.java @@ -0,0 +1,100 @@ +package org.opentcs.traffic.common; + +import java.util.ArrayList; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Polygon; +import org.opentcs.traffic.entity.VehicleAttr; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author xuzhiheng + * @desc 车辆轮廓检测 + */ +public class VehicleCollisionAvoidance { + + /** + * LOG + */ + private static final Logger LOG = LoggerFactory.getLogger(VehicleCollisionAvoidance.class); + /** + * 车辆对象轮廓避障信息集合: key:车辆名称 value:车辆对象轮廓信息 + */ + private static ArrayList vehicleAttrs = new ArrayList<>(); + /** + * 两台车之间最小距离 + */ + private final Integer minDistance = 100; + + /** + * 轮廓避障检测 + * @param vehicleName 当前车辆名称 + */ + public void interferenceDetection(String vehicleName) { + + GeometryFactory geometryFactory = new GeometryFactory(); + + //计算当前车辆的 + + } + + public static class Vehicle { + private final Long x; // 中心X坐标(米) + private final Long y; // 中心Y坐标(米) + private final double angle; // 方向角(弧度,0弧度指向东) + private final Integer length; // 长度(米) + private final Integer width; // 宽度(米) + private final Coordinate[] vertices; // 四个顶点坐标 + + public Vehicle(Long x, Long y, double angle, Integer length, Integer width) { + this.x = x; + this.y = y; + this.angle = angle; + this.length = length; + this.width = width; + this.vertices = calculateVertices(); + } + + /** + * 计算旋转矩形的四个顶点 + */ + private Coordinate[] calculateVertices() { + double halfLength = length / 2; + double halfWidth = width / 2; + double cosTheta = Math.cos(angle); + double sinTheta = Math.sin(angle); + + return new Coordinate[]{ + // 顶点1 + new Coordinate( + x + halfLength * cosTheta - halfWidth * sinTheta, + y + halfLength * sinTheta + halfWidth * cosTheta + ), + // 顶点2 + new Coordinate( + x - halfLength * cosTheta - halfWidth * sinTheta, + y - halfLength * sinTheta + halfWidth * cosTheta + ), + // 顶点3 + new Coordinate( + x - halfLength * cosTheta + halfWidth * sinTheta, + y - halfLength * sinTheta - halfWidth * cosTheta + ), + // 顶点4 + new Coordinate( + x + halfLength * cosTheta + halfWidth * sinTheta, + y + halfLength * sinTheta - halfWidth * cosTheta + ) + }; + } + + /** + * 转换为JTS多边形 + */ + public Polygon toPolygon(GeometryFactory geomFactory) { + return geomFactory.createPolygon(vertices); + } + } + +} diff --git a/opentcs-common/src/main/java/org/opentcs/traffic/entity/VehicleAttr.java b/opentcs-common/src/main/java/org/opentcs/traffic/entity/VehicleAttr.java new file mode 100644 index 0000000..6cfeb2a --- /dev/null +++ b/opentcs-common/src/main/java/org/opentcs/traffic/entity/VehicleAttr.java @@ -0,0 +1,30 @@ +package org.opentcs.traffic.entity; + +import lombok.Data; +import org.opentcs.data.model.Pose; + +/** + * @author xuzhiheng + * @desc 车辆属性,用于计算轮廓避障 + */ +@Data +public class VehicleAttr { + + /** + * 车辆名称 + */ + private String name; + /** + * 长度/cm(单位厘米) + */ + private Integer length; + /** + * 宽度/cm(单位厘米) + */ + private Integer width; + /** + * 车辆姿态 + */ + private Pose pose; + +} diff --git a/opentcs-common/src/main/java/org/opentcs/util/KernelCommunication.java b/opentcs-common/src/main/java/org/opentcs/util/KernelCommunication.java index f85d48e..fea982b 100644 --- a/opentcs-common/src/main/java/org/opentcs/util/KernelCommunication.java +++ b/opentcs-common/src/main/java/org/opentcs/util/KernelCommunication.java @@ -34,7 +34,34 @@ public class KernelCommunication { throw new ObjectUnknownException("sendToAdapter Unknown vehicle: " + name); } + System.out.println("sendToAdapter: " + message); vehicleService.sendCommAdapterMessage(vehicle.getReference(), message); } + /** + * 启用车辆通讯适配器 + * @param name 车辆名称 + */ + public void enableAdapter(String name) { + VehicleService vehicleService = servicePortal.getVehicleService(); + Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, name); + if (vehicle == null) { + throw new ObjectUnknownException("enableAdapter Unknown vehicle: " + name); + } + vehicleService.enableCommAdapter(vehicle.getReference()); + } + + /** + * 禁用车辆通讯适配器 + * @param name 车辆名称 + */ + public void disableAdapter(String name) { + VehicleService vehicleService = servicePortal.getVehicleService(); + Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, name); + if (vehicle == null) { + throw new ObjectUnknownException("disableAdapter Unknown vehicle: " + name); + } + vehicleService.disableCommAdapter(vehicle.getReference()); + } + } diff --git a/opentcs-kernel-extension-http-services/src/main/java/org/opentcs/kernel/extensions/servicewebapi/v1/VehicleHandler.java b/opentcs-kernel-extension-http-services/src/main/java/org/opentcs/kernel/extensions/servicewebapi/v1/VehicleHandler.java index 7ec8662..581825d 100644 --- a/opentcs-kernel-extension-http-services/src/main/java/org/opentcs/kernel/extensions/servicewebapi/v1/VehicleHandler.java +++ b/opentcs-kernel-extension-http-services/src/main/java/org/opentcs/kernel/extensions/servicewebapi/v1/VehicleHandler.java @@ -37,6 +37,8 @@ import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostVehicleRoutesR import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PutVehicleAllowedOrderTypesTO; import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PutVehicleEnergyLevelThresholdSetTO; import org.opentcs.manage.AdapterManage; +import org.opentcs.manage.entity.ActionStatus; +import org.opentcs.manage.entity.AgvInfo; /** * Handles requests related to vehicles. @@ -69,22 +71,38 @@ public class VehicleHandler { * 接收平台异步回调处理 */ public void postReceiveCallback(Object data) { - System.out.println("jsonObject-----ssss: " + data.toString()); +// System.out.println("jsonObject-----ssss: " + data.toString()); //截取平台响应的字符串 -// String jsonStr = data.toString().split("=", 2)[1]; - String jsonStr = data.toString(); + String jsonStr = data.toString().split("=", 2)[1]; +// String jsonStr = data.toString(); JSONObject jsonObject = JSON.parseObject(jsonStr); - String name = jsonObject.getString("name"); + String name = jsonObject.getString("vehicle_name"); Integer type = jsonObject.getInteger("type"); + Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, name); + if (vehicle == null) { + throw new ObjectUnknownException("postReceiveCallback Unknown vehicle: " + name); + } + //将数据更新到线程安全的集合中,防止线程阻塞 AdapterManage.setAdapterStatus(name); - if (type == 1) { - AdapterManage.setAdapterVehicleModel(name, jsonStr); - } else if (type == 5) { + + if (AdapterManage.getAdapterStatus(name)) { + vehicleService.enableCommAdapter(vehicle.getReference()); + } else { + //todo 关闭暂时有问题 + vehicleService.disableCommAdapter(vehicle.getReference()); + } + + if (type == 1) { //上报agv详细信息 + AgvInfo agvInfo = AdapterManage.setAdapterVehicleModel(name, jsonStr); + vehicleService.sendCommAdapterMessage(vehicle.getReference(), agvInfo); + } else if (type == 5) { //上报动作完成 //动作完成上报 - AdapterManage.setActionStatus(name); + ActionStatus actionStatus = new ActionStatus(); + actionStatus.setStatus(true); + vehicleService.sendCommAdapterMessage(vehicle.getReference(), actionStatus); } } diff --git a/opentcs-kernel/src/main/java/org/opentcs/kernel/StandardKernel.java b/opentcs-kernel/src/main/java/org/opentcs/kernel/StandardKernel.java index 540f972..2bfa14e 100644 --- a/opentcs-kernel/src/main/java/org/opentcs/kernel/StandardKernel.java +++ b/opentcs-kernel/src/main/java/org/opentcs/kernel/StandardKernel.java @@ -7,7 +7,6 @@ import static java.util.Objects.requireNonNull; import com.google.common.util.concurrent.Uninterruptibles; import com.google.inject.Provider; import jakarta.inject.Inject; -import java.io.IOException; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -15,10 +14,8 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import org.opentcs.access.Kernel; -import org.opentcs.access.Kernel.State; import org.opentcs.access.KernelStateTransitionEvent; import org.opentcs.access.LocalKernel; -import org.opentcs.communication.mqtt.MqttBrokerServer; import org.opentcs.components.kernel.KernelExtension; import org.opentcs.components.kernel.services.NotificationService; import org.opentcs.customizations.ApplicationEventBus; @@ -73,14 +70,10 @@ public class StandardKernel * The kernel implementing the actual functionality for the current mode. */ private KernelState kernelState; - /** - * MQTT 服务 - */ - private MqttBrokerServer mqttBrokerServer = new MqttBrokerServer(); /** * 通讯适配器管理类 */ - private AdapterManage adapterManage = new AdapterManage(); + private final AdapterManage adapterManage = new AdapterManage(); /** * Creates a new kernel. @@ -122,14 +115,14 @@ public class StandardKernel //开启MQTT服务 // mqttBrokerServer.startMqtt(); - //开启通讯适配器自动管理 - adapterManage.START(); - LOG.info("Communication adapter auto management started successfully"); - initialized = true; LOG.debug("Starting kernel thread"); Thread kernelThread = new Thread(this, "kernelThread"); kernelThread.start(); + + //开启通讯适配器自动管理 +// adapterManage.START(); +// LOG.info("Communication adapter auto management started successfully"); } @Override @@ -149,8 +142,8 @@ public class StandardKernel //关闭MQTT服务 // mqttBrokerServer.stopMqtt(); - //关闭通讯适配器管理类 - adapterManage.STOP(); + //关闭通讯适配器自动管理 +// adapterManage.STOP(); initialized = false; terminationSemaphore.release(); diff --git a/opentcs-kernel/src/main/java/org/opentcs/kernel/workingset/PlantModelManager.java b/opentcs-kernel/src/main/java/org/opentcs/kernel/workingset/PlantModelManager.java index 4017654..84ec332 100644 --- a/opentcs-kernel/src/main/java/org/opentcs/kernel/workingset/PlantModelManager.java +++ b/opentcs-kernel/src/main/java/org/opentcs/kernel/workingset/PlantModelManager.java @@ -1385,14 +1385,6 @@ public class PlantModelManager private Point createPoint(PointCreationTO to) throws ObjectExistsException { // Get a unique ID for the new point and create an instance. - if (to.getType() == Point.Type.PARK_POSITION) { - //是休息点,记录 - Park park = new Park(); - park.setName(to.getName()); - park.setStatus(false); - ParkStrategy.setPark(park); - } - Point newPoint = new Point(to.getName()) .withPose(new Pose(to.getPose().getPosition(), to.getPose().getOrientationAngle())) .withType(to.getType())