Compare commits
32 Commits
master
...
new_opentc
Author | SHA1 | Date | |
---|---|---|---|
|
f2241efc68 | ||
|
e6818275ab | ||
|
ec7e0fc699 | ||
|
5d9505ec01 | ||
|
cccb4e5016 | ||
|
4a9c261d05 | ||
|
d60a70483d | ||
|
ec8e2cb292 | ||
|
dddb5e6b39 | ||
|
261cb7a7e7 | ||
|
d00b274b49 | ||
|
dc19bb01b2 | ||
|
c8c91b12f4 | ||
|
d60d6081ac | ||
|
64adb38fed | ||
|
bb878f6dc8 | ||
|
3e546985f2 | ||
|
84066c7dfd | ||
|
6d96ea6891 | ||
|
2be7b938c2 | ||
|
51bbac6405 | ||
|
9121b1985a | ||
|
5bcba189ef | ||
|
b870caa0e5 | ||
|
7ac42a2b80 | ||
|
952d0db0d1 | ||
|
305735c92c | ||
|
c53ea3f750 | ||
|
22c0518721 | ||
|
b14d86c96b | ||
|
2cc6f2906b | ||
|
503771fe7c |
@ -19,7 +19,7 @@ ext.guiceSrcDir = sourceSets.guiceConfig.java.srcDirs[0]
|
||||
|
||||
compileGuiceConfigJava {
|
||||
options.release = 21
|
||||
options.compilerArgs << "-Werror"
|
||||
// options.compilerArgs << "-Werror"
|
||||
options.compilerArgs << "-Xlint:all"
|
||||
options.compilerArgs << "-Xlint:-serial"
|
||||
}
|
||||
|
@ -35,14 +35,14 @@ dependencies {
|
||||
|
||||
compileJava {
|
||||
options.release = 21
|
||||
options.compilerArgs << "-Werror"
|
||||
// options.compilerArgs << "-Werror"
|
||||
options.compilerArgs << "-Xlint:all"
|
||||
options.compilerArgs << "-Xlint:-serial"
|
||||
}
|
||||
|
||||
compileTestJava {
|
||||
options.release = 21
|
||||
options.compilerArgs << "-Werror"
|
||||
// options.compilerArgs << "-Werror"
|
||||
options.compilerArgs << "-Xlint:all"
|
||||
options.compilerArgs << "-Xlint:-serial"
|
||||
}
|
||||
|
@ -8,3 +8,11 @@ apply from: "${rootDir}/gradle/publishing-java.gradle"
|
||||
task release {
|
||||
dependsOn build
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile){
|
||||
options.encoding="utf-8"
|
||||
}
|
||||
|
||||
tasks.withType(Javadoc){
|
||||
options.encoding="utf-8"
|
||||
}
|
||||
|
@ -109,4 +109,11 @@ public interface RemoteVehicleService
|
||||
)
|
||||
throws RemoteException;
|
||||
// CHECKSTYLE:ON
|
||||
|
||||
void receiveCallback(
|
||||
ClientID clientId,
|
||||
TCSObjectReference<Vehicle> ref,
|
||||
Object data
|
||||
)
|
||||
throws RemoteException;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ public interface InternalVehicleService
|
||||
|
||||
/**
|
||||
* Updates a vehicle's energy level.
|
||||
* 更新车辆的能级。
|
||||
* 更新车辆的能级.
|
||||
*
|
||||
* @param ref A reference to the vehicle to be modified.
|
||||
* @param energyLevel The vehicle's new energy level.
|
||||
@ -40,7 +40,7 @@ public interface InternalVehicleService
|
||||
|
||||
/**
|
||||
* Updates a vehicle's load handling devices.
|
||||
* 更新车辆的负载处理设备。
|
||||
* 更新车辆的负载处理设备.
|
||||
*
|
||||
* @param ref A reference to the vehicle to be modified.
|
||||
* @param devices The vehicle's new load handling devices.
|
||||
@ -68,7 +68,7 @@ public interface InternalVehicleService
|
||||
|
||||
/**
|
||||
* Updates a vehicle's order sequence.
|
||||
* 更新车辆的订单序列。
|
||||
* 更新车辆的订单序列.
|
||||
*
|
||||
* @param vehicleRef A reference to the vehicle to be modified.
|
||||
* @param sequenceRef A reference to the order sequence the vehicle processes.
|
||||
@ -83,7 +83,7 @@ public interface InternalVehicleService
|
||||
/**
|
||||
* Updates the vehicle's current orientation angle (-360..360 degrees, or {@link Double#NaN}, if
|
||||
* the vehicle doesn't provide an angle).
|
||||
* 如果车辆不提供角度,则更新车辆的当前方向角(-360..360度,或{@link double},如果车辆不提供角度)。
|
||||
* 如果车辆不提供角度,则更新车辆的当前方向角(-360..360度,或{@link double},如果车辆不提供角度).
|
||||
*
|
||||
* @param ref A reference to the vehicle to be modified.
|
||||
* @param angle The vehicle's orientation angle.
|
||||
@ -97,7 +97,7 @@ public interface InternalVehicleService
|
||||
|
||||
/**
|
||||
* Places a vehicle on a point.
|
||||
* 将车辆放在一个点上。
|
||||
* 将车辆放在一个点上.
|
||||
*
|
||||
* @param vehicleRef A reference to the vehicle to be modified.
|
||||
* @param pointRef A reference to the point on which the vehicle is to be placed.
|
||||
@ -125,7 +125,7 @@ public interface InternalVehicleService
|
||||
|
||||
/**
|
||||
* Updates the vehicle's pose.
|
||||
* 更新车辆的姿势。
|
||||
* 更新车辆的姿势.
|
||||
*
|
||||
* @param ref A reference to the vehicle to be modified.
|
||||
* @param pose The vehicle's new pose.
|
||||
@ -143,7 +143,7 @@ public interface InternalVehicleService
|
||||
|
||||
/**
|
||||
* Updates a vehicle's processing state.
|
||||
* 更新车辆的处理状态。
|
||||
* 更新车辆的处理状态.
|
||||
*
|
||||
* @param ref A reference to the vehicle to be modified.
|
||||
* @param state The vehicle's new processing state.
|
||||
@ -154,7 +154,7 @@ public interface InternalVehicleService
|
||||
|
||||
/**
|
||||
* Updates a vehicle's recharge operation.
|
||||
* 更新车辆的充电操作。
|
||||
* 更新车辆的充电操作.
|
||||
*
|
||||
* @param ref A reference to the vehicle to be modified.
|
||||
* @param rechargeOperation The vehicle's new recharge action.
|
||||
@ -165,7 +165,7 @@ public interface InternalVehicleService
|
||||
|
||||
/**
|
||||
* Updates a vehicle's claimed resources.
|
||||
* 更新车辆声称的资源。
|
||||
* 更新车辆生成的资源。
|
||||
*
|
||||
* @param ref A reference to the vehicle to be modified.
|
||||
* @param resources The new resources.
|
||||
@ -179,7 +179,7 @@ public interface InternalVehicleService
|
||||
|
||||
/**
|
||||
* Updates a vehicle's allocated resources.
|
||||
* 更新车辆分配的资源。
|
||||
* 更新车辆已分配的资源.
|
||||
*
|
||||
* @param ref A reference to the vehicle to be modified.
|
||||
* @param resources The new resources.
|
||||
@ -193,7 +193,7 @@ public interface InternalVehicleService
|
||||
|
||||
/**
|
||||
* Updates a vehicle's state.
|
||||
* 更新车辆的状态。
|
||||
* Update Vehicle Status
|
||||
*
|
||||
* @param ref A reference to the vehicle to be modified.
|
||||
* @param state The vehicle's new state.
|
||||
@ -204,7 +204,7 @@ public interface InternalVehicleService
|
||||
|
||||
/**
|
||||
* Updates a vehicle's length.
|
||||
* 更新车辆的长度。
|
||||
* 更新车辆长度.
|
||||
*
|
||||
* @param ref A reference to the vehicle to be modified.
|
||||
* @param length The vehicle's new length.
|
||||
@ -218,7 +218,7 @@ public interface InternalVehicleService
|
||||
|
||||
/**
|
||||
* Updates the vehicle's bounding box.
|
||||
* 更新车辆的边界框。
|
||||
* 更新车辆的边界框.
|
||||
*
|
||||
* @param ref A reference to the vehicle.
|
||||
* @param boundingBox The vehicle's new bounding box (in mm).
|
||||
@ -234,7 +234,7 @@ public interface InternalVehicleService
|
||||
|
||||
/**
|
||||
* Updates a vehicle's transport order.
|
||||
* 更新车辆的运输订单。
|
||||
* 更新车辆运输订单.
|
||||
*
|
||||
* @param vehicleRef A reference to the vehicle to be modified.
|
||||
* @param orderRef A reference to the transport order the vehicle processes.
|
||||
|
@ -1633,16 +1633,39 @@ public class Vehicle
|
||||
ERROR,
|
||||
/**
|
||||
* The vehicle is currently idle/available for processing movement orders.
|
||||
* 空闲,对应科聪控制器状态0x00
|
||||
*/
|
||||
IDLE,
|
||||
/**
|
||||
* The vehicle is processing a movement order.
|
||||
* 运行,对应科聪控制器状态0x01
|
||||
*/
|
||||
EXECUTING,
|
||||
/**
|
||||
* The vehicle is currently recharging its battery/refilling fuel.
|
||||
*/
|
||||
CHARGING
|
||||
CHARGING,
|
||||
/**
|
||||
* 暂停,对应科聪控制器状态0x02
|
||||
*/
|
||||
PAUSED,
|
||||
/**
|
||||
* 车辆初始化中,对应科聪控制器状态0x03
|
||||
*/
|
||||
INITIALIZING,
|
||||
/**
|
||||
* 人工确认,对应科聪控制器状态0x04
|
||||
*/
|
||||
CONFIRMATION,
|
||||
/**
|
||||
* 车辆未初始化,对应科聪控制器状态0x05
|
||||
*/
|
||||
UNINITIALIZED,
|
||||
/**
|
||||
* 低电量模式
|
||||
*/
|
||||
FEED
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1652,19 +1675,23 @@ public class Vehicle
|
||||
|
||||
/**
|
||||
* The vehicle's reported position is ignored.
|
||||
* 车辆报告的位置将被忽略。
|
||||
*/
|
||||
TO_BE_IGNORED,
|
||||
/**
|
||||
* The vehicle's reported position is noticed, meaning that resources will not be reserved for
|
||||
* it.
|
||||
* 车辆报告的位置会被注意到,这意味着不会为其保留资源。
|
||||
*/
|
||||
TO_BE_NOTICED,
|
||||
/**
|
||||
* The vehicle's reported position is respected, meaning that resources will be reserved for it.
|
||||
* 车辆报告的位置将被遵守,这意味着将为其保留资源。
|
||||
*/
|
||||
TO_BE_RESPECTED,
|
||||
/**
|
||||
* The vehicle is fully integrated and may be assigned to transport orders.
|
||||
* 车辆已完全集成,可以分配给运输订单。
|
||||
*/
|
||||
TO_BE_UTILIZED
|
||||
}
|
||||
|
@ -91,6 +91,14 @@ public class VehicleProcessModel {
|
||||
* The vehicle's current bounding box.
|
||||
*/
|
||||
private BoundingBox boundingBox;
|
||||
/**
|
||||
* 当前车辆级别
|
||||
*/
|
||||
private Vehicle.IntegrationLevel integrationLevel;
|
||||
/**
|
||||
* 载货状态
|
||||
*/
|
||||
private Boolean loadState;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
@ -661,6 +669,7 @@ public class VehicleProcessModel {
|
||||
@Nonnull
|
||||
Vehicle.IntegrationLevel level
|
||||
) {
|
||||
integrationLevel = level;
|
||||
getPropertyChangeSupport().firePropertyChange(
|
||||
Attribute.INTEGRATION_LEVEL_CHANGE_REQUESTED.name(),
|
||||
null,
|
||||
@ -668,6 +677,14 @@ public class VehicleProcessModel {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取车辆级别
|
||||
* @return 车辆级别
|
||||
*/
|
||||
public Vehicle.IntegrationLevel getIntegrationLevel() {
|
||||
return integrationLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies observers that the vehicle would like to have its current transport order withdrawn.
|
||||
*
|
||||
|
@ -14,3 +14,11 @@ dependencies {
|
||||
task release {
|
||||
dependsOn build
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile){
|
||||
options.encoding="utf-8"
|
||||
}
|
||||
|
||||
tasks.withType(Javadoc){
|
||||
options.encoding="utf-8"
|
||||
}
|
||||
|
@ -9,8 +9,18 @@ apply from: "${rootDir}/gradle/publishing-java.gradle"
|
||||
dependencies {
|
||||
api project(':opentcs-api-injection')
|
||||
api project(':opentcs-common')
|
||||
//fastjson
|
||||
implementation 'com.alibaba:fastjson:1.2.83'
|
||||
}
|
||||
|
||||
task release {
|
||||
dependsOn build
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile){
|
||||
options.encoding="utf-8"
|
||||
}
|
||||
|
||||
tasks.withType(Javadoc){
|
||||
options.encoding="utf-8"
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ package org.opentcs.virtualvehicle;
|
||||
|
||||
import com.google.inject.assistedinject.FactoryModuleBuilder;
|
||||
import org.opentcs.customizations.kernel.KernelInjectionModule;
|
||||
import org.opentcs.virtualvehicle.testadapter.TestLoopbackAdapterComponentsFactory;
|
||||
import org.opentcs.virtualvehicle.testadapter.TestLoopbackCommunicationAdapterFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -46,6 +48,8 @@ public class LoopbackCommAdapterModule
|
||||
// tag::documentation_createCommAdapterModule[]
|
||||
vehicleCommAdaptersBinder().addBinding().to(LoopbackCommunicationAdapterFactory.class);
|
||||
// end::documentation_createCommAdapterModule[]
|
||||
install(new FactoryModuleBuilder().build(TestLoopbackAdapterComponentsFactory.class));
|
||||
vehicleCommAdaptersBinder().addBinding().to(TestLoopbackCommunicationAdapterFactory.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -296,30 +296,37 @@ public class LoopbackVehicleModel
|
||||
public enum Attribute {
|
||||
/**
|
||||
* Indicates a change of the virtual vehicle's single step mode setting.
|
||||
* 表示虚拟车辆的单步模式设置的更改。
|
||||
*/
|
||||
SINGLE_STEP_MODE,
|
||||
/**
|
||||
* Indicates a change of the virtual vehicle's default operating time.
|
||||
* 表示虚拟车辆的默认运行时间的更改。
|
||||
*/
|
||||
OPERATING_TIME,
|
||||
/**
|
||||
* Indicates a change of the virtual vehicle's maximum acceleration.
|
||||
* 表示虚拟车辆的最大加速度的变化。
|
||||
*/
|
||||
ACCELERATION,
|
||||
/**
|
||||
* Indicates a change of the virtual vehicle's maximum deceleration.
|
||||
* 表示虚拟车辆的最大减速度发生变化。
|
||||
*/
|
||||
DECELERATION,
|
||||
/**
|
||||
* Indicates a change of the virtual vehicle's maximum forward velocity.
|
||||
* 表示虚拟车辆的最大前进速度的变化。
|
||||
*/
|
||||
MAX_FORWARD_VELOCITY,
|
||||
/**
|
||||
* Indicates a change of the virtual vehicle's maximum reverse velocity.
|
||||
* 表示虚拟车辆的最大倒车速度的变化。
|
||||
*/
|
||||
MAX_REVERSE_VELOCITY,
|
||||
/**
|
||||
* Indicates a change of the virtual vehicle's paused setting.
|
||||
* 表示更改虚拟车辆的暂停设置。
|
||||
*/
|
||||
VEHICLE_PAUSED,
|
||||
}
|
||||
|
@ -74,4 +74,20 @@ public interface VirtualVehicleConfiguration {
|
||||
orderKey = "2_behaviour_3"
|
||||
)
|
||||
int vehicleLengthUnloaded();
|
||||
|
||||
@ConfigurationEntry(
|
||||
type = "String",
|
||||
description = "充电策略选择",
|
||||
changesApplied = ConfigurationEntry.ChangesApplied.INSTANTLY,
|
||||
orderKey = "3_model_1"
|
||||
)
|
||||
String chargingStrategyName();
|
||||
|
||||
@ConfigurationEntry(
|
||||
type = "Float",
|
||||
description = "每秒钟耗电",
|
||||
changesApplied = ConfigurationEntry.ChangesApplied.INSTANTLY,
|
||||
orderKey = "3_model_2"
|
||||
) // 每秒消耗5%电量
|
||||
float energyDrainRatePerSecond();
|
||||
}
|
||||
|
@ -0,0 +1,18 @@
|
||||
package org.opentcs.virtualvehicle.chargingstrategy;
|
||||
|
||||
import org.opentcs.virtualvehicle.LoopbackVehicleModel;
|
||||
|
||||
public interface ChargingStrategy {
|
||||
/**
|
||||
* 检查是否需要充电
|
||||
* @param processModel 车辆进程模型
|
||||
* @return 是否需要充电
|
||||
*/
|
||||
boolean needToCharge(LoopbackVehicleModel processModel);
|
||||
|
||||
/**
|
||||
* 获取充电策略名称
|
||||
* @return 策略名称
|
||||
*/
|
||||
String getStrategyName();
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package org.opentcs.virtualvehicle.chargingstrategy;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.opentcs.virtualvehicle.LoopbackVehicleModel;
|
||||
|
||||
/**
|
||||
* 充电策略管理器:管理充电策略的选择和应用
|
||||
*/
|
||||
public class ChargingStrategyManager {
|
||||
private final List<ChargingStrategy> strategies;
|
||||
private ChargingStrategy currentStrategy;
|
||||
|
||||
public ChargingStrategyManager() {
|
||||
// 初始化支持的充电策略
|
||||
strategies = Arrays.asList(
|
||||
new ThresholdChargingStrategy(60, 95)
|
||||
);
|
||||
// 默认使用阈值充电策略
|
||||
currentStrategy = strategies.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据当前情况选择最合适的充电策略
|
||||
*/
|
||||
public void selectOptimalStrategy(LoopbackVehicleModel processModel) {
|
||||
for (ChargingStrategy strategy : strategies) {
|
||||
if (strategy.needToCharge(processModel)) {
|
||||
currentStrategy = strategy;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 如果没有策略触发,使用默认策略
|
||||
currentStrategy = strategies.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否需要充电
|
||||
*/
|
||||
public boolean needToCharge(LoopbackVehicleModel processModel) {
|
||||
selectOptimalStrategy(processModel);
|
||||
return currentStrategy.needToCharge(processModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前使用的充电策略
|
||||
*/
|
||||
public ChargingStrategy getCurrentStrategy() {
|
||||
return currentStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有可用的充电策略
|
||||
*/
|
||||
public List<ChargingStrategy> getAvailableStrategies() {
|
||||
return Collections.unmodifiableList(strategies);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置指定名称的充电策略
|
||||
*/
|
||||
public void setStrategyByName(String strategyName) {
|
||||
for (ChargingStrategy strategy : strategies) {
|
||||
if (strategy.getStrategyName().equals(strategyName)) {
|
||||
currentStrategy = strategy;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 如果未找到指定策略,使用默认策略
|
||||
currentStrategy = strategies.get(0);
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package org.opentcs.virtualvehicle.chargingstrategy;
|
||||
|
||||
import org.opentcs.data.model.Vehicle;
|
||||
import org.opentcs.virtualvehicle.LoopbackVehicleModel;
|
||||
|
||||
/**
|
||||
* 电量阈值充电策略:当电量低于指定阈值时开始充电
|
||||
*/
|
||||
public class ThresholdChargingStrategy implements ChargingStrategy {
|
||||
private final int chargeThreshold; // 充电阈值(%)
|
||||
private final int fullChargeThreshold; // 充满阈值(%)
|
||||
|
||||
public ThresholdChargingStrategy(int chargeThreshold, int fullChargeThreshold) {
|
||||
this.chargeThreshold = chargeThreshold;
|
||||
this.fullChargeThreshold = fullChargeThreshold;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needToCharge(LoopbackVehicleModel processModel) {
|
||||
int energyLevel = processModel.getEnergyLevel();
|
||||
return energyLevel <= chargeThreshold && !isChargingOrFull(processModel);
|
||||
}
|
||||
|
||||
private boolean isChargingOrFull(LoopbackVehicleModel processModel) {
|
||||
return processModel.getState() == Vehicle.State.CHARGING ||
|
||||
processModel.getEnergyLevel() >= fullChargeThreshold;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStrategyName() {
|
||||
return "threshold_charging_strategy";
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package org.opentcs.virtualvehicle.testadapter;
|
||||
|
||||
import org.opentcs.data.model.Vehicle;
|
||||
|
||||
public interface TestLoopbackAdapterComponentsFactory {
|
||||
|
||||
/**
|
||||
* Creates a new LoopbackCommunicationAdapter for the given vehicle.
|
||||
*
|
||||
* @param vehicle The vehicle.
|
||||
* @return A new LoopbackCommunicationAdapter for the given vehicle.
|
||||
*/
|
||||
TestLoopbackCommunicationAdapter createLoopbackCommAdapter(Vehicle vehicle);
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
package org.opentcs.virtualvehicle.testadapter;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import jakarta.annotation.Nonnull;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.opentcs.access.KernelServicePortal;
|
||||
import org.opentcs.data.TCSObjectReference;
|
||||
import org.opentcs.data.model.Vehicle;
|
||||
import org.opentcs.drivers.vehicle.VehicleCommAdapterDescription;
|
||||
import org.opentcs.drivers.vehicle.management.VehicleCommAdapterPanel;
|
||||
import org.opentcs.drivers.vehicle.management.VehicleCommAdapterPanelFactory;
|
||||
import org.opentcs.drivers.vehicle.management.VehicleProcessModelTO;
|
||||
import org.opentcs.virtualvehicle.AdapterPanelComponentsFactory;
|
||||
import org.opentcs.virtualvehicle.LoopbackVehicleModelTO;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class TestLoopbackCommAdapterPanelFactory
|
||||
implements
|
||||
VehicleCommAdapterPanelFactory {
|
||||
|
||||
/**
|
||||
* This class's logger.
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TestLoopbackCommAdapterPanelFactory.class);
|
||||
/**
|
||||
* The service portal.
|
||||
*/
|
||||
private final KernelServicePortal servicePortal;
|
||||
/**
|
||||
* The components factory.
|
||||
*/
|
||||
private final AdapterPanelComponentsFactory componentsFactory;
|
||||
/**
|
||||
* Whether this factory is initialized or not.
|
||||
*/
|
||||
private boolean initialized;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param servicePortal The service portal.
|
||||
* @param componentsFactory The components factory.
|
||||
*/
|
||||
@Inject
|
||||
public TestLoopbackCommAdapterPanelFactory(
|
||||
KernelServicePortal servicePortal,
|
||||
AdapterPanelComponentsFactory componentsFactory
|
||||
) {
|
||||
this.servicePortal = requireNonNull(servicePortal, "servicePortal");
|
||||
this.componentsFactory = requireNonNull(componentsFactory, "componentsFactory");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
if (isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void terminate() {
|
||||
if (!isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<VehicleCommAdapterPanel> getPanelsFor(
|
||||
@Nonnull
|
||||
VehicleCommAdapterDescription description,
|
||||
@Nonnull
|
||||
TCSObjectReference<Vehicle> vehicle,
|
||||
@Nonnull
|
||||
VehicleProcessModelTO processModel
|
||||
) {
|
||||
requireNonNull(description, "description");
|
||||
requireNonNull(vehicle, "vehicle");
|
||||
requireNonNull(processModel, "processModel");
|
||||
|
||||
if (!providesPanelsFor(description, processModel)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<VehicleCommAdapterPanel> panels = new ArrayList<>();
|
||||
panels.add(
|
||||
componentsFactory.createPanel(
|
||||
((LoopbackVehicleModelTO) processModel),
|
||||
servicePortal.getVehicleService()
|
||||
)
|
||||
);
|
||||
return panels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether this factory can provide comm adapter panels for the given description and the
|
||||
* given type of process model.
|
||||
*
|
||||
* @param description The description to check for.
|
||||
* @param processModel The process model.
|
||||
* @return {@code true} if, and only if, this factory can provide comm adapter panels for the
|
||||
* given description and the given type of process model.
|
||||
*/
|
||||
private boolean providesPanelsFor(
|
||||
VehicleCommAdapterDescription description,
|
||||
VehicleProcessModelTO processModel
|
||||
) {
|
||||
return (description instanceof TestLoopbackCommunicationAdapterDescription)
|
||||
&& (processModel instanceof LoopbackVehicleModelTO);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,28 @@
|
||||
package org.opentcs.virtualvehicle.testadapter;
|
||||
|
||||
import static org.opentcs.virtualvehicle.I18nLoopbackCommAdapter.BUNDLE_PATH;
|
||||
|
||||
import java.util.ResourceBundle;
|
||||
import org.opentcs.drivers.vehicle.VehicleCommAdapterDescription;
|
||||
|
||||
public class TestLoopbackCommunicationAdapterDescription
|
||||
extends
|
||||
VehicleCommAdapterDescription {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public TestLoopbackCommunicationAdapterDescription() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return ResourceBundle.getBundle(BUNDLE_PATH)
|
||||
.getString("myLoopbackCommunicationAdapterDescription.description");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSimVehicleCommAdapter() {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.virtualvehicle.testadapter;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
import org.opentcs.data.model.Vehicle;
|
||||
import org.opentcs.drivers.vehicle.VehicleCommAdapterDescription;
|
||||
import org.opentcs.drivers.vehicle.VehicleCommAdapterFactory;
|
||||
import org.opentcs.virtualvehicle.LoopbackAdapterComponentsFactory;
|
||||
import org.opentcs.virtualvehicle.LoopbackCommunicationAdapter;
|
||||
|
||||
/**
|
||||
* A factory for loopback communication adapters (virtual vehicles).
|
||||
*/
|
||||
public class TestLoopbackCommunicationAdapterFactory
|
||||
implements
|
||||
VehicleCommAdapterFactory {
|
||||
|
||||
/**
|
||||
* The adapter components factory.
|
||||
*/
|
||||
private final TestLoopbackAdapterComponentsFactory adapterFactory;
|
||||
/**
|
||||
* Indicates whether this component is initialized or not.
|
||||
*/
|
||||
private boolean initialized;
|
||||
|
||||
/**
|
||||
* Creates a new factory.
|
||||
*
|
||||
* @param componentsFactory The adapter components factory.
|
||||
*/
|
||||
@Inject
|
||||
public TestLoopbackCommunicationAdapterFactory(TestLoopbackAdapterComponentsFactory componentsFactory) {
|
||||
this.adapterFactory = requireNonNull(componentsFactory, "componentsFactory");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
if (isInitialized()) {
|
||||
return;
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void terminate() {
|
||||
if (!isInitialized()) {
|
||||
return;
|
||||
}
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VehicleCommAdapterDescription getDescription() {
|
||||
return new TestLoopbackCommunicationAdapterDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean providesAdapterFor(Vehicle vehicle) {
|
||||
requireNonNull(vehicle, "vehicle");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TestLoopbackCommunicationAdapter getAdapterFor(Vehicle vehicle) {
|
||||
requireNonNull(vehicle, "vehicle");
|
||||
return adapterFactory.createLoopbackCommAdapter(vehicle);
|
||||
}
|
||||
}
|
@ -42,3 +42,4 @@ loopbackCommAdapterPanel.radioButton_setProperty.text=Set this value:
|
||||
loopbackCommAdapterPanel.textArea_precisePosition.positionNotSetPlaceholder=-
|
||||
loopbackCommAdapterPanel.textField_orientationAngle.angleNotSetPlaceholder=-
|
||||
loopbackCommunicationAdapterDescription.description=Loopback adapter (virtual vehicle)
|
||||
myLoopbackCommunicationAdapterDescription.description=Test Loopback adapter (virtual vehicle)
|
||||
|
@ -22,8 +22,21 @@ dependencies {
|
||||
implementation group: 'org.springframework.retry', name: 'spring-retry', version: '2.0.5'
|
||||
// https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
|
||||
implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.5.16'
|
||||
|
||||
|
||||
// Lombok依赖
|
||||
compileOnly 'org.projectlombok:lombok:1.18.30'
|
||||
annotationProcessor 'org.projectlombok:lombok:1.18.30'
|
||||
//fastjson
|
||||
implementation 'com.alibaba:fastjson:1.2.83'
|
||||
//okhttp
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
|
||||
// 嵌入式 MQTT 服务端代理
|
||||
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'
|
||||
// https://mvnrepository.com/artifact/org.locationtech.jts.io/jts-io-common
|
||||
// implementation("org.locationtech.jts.io:jts-io-common:1.20.0")
|
||||
}
|
||||
|
||||
processResources.doLast {
|
||||
@ -40,3 +53,11 @@ processResources.doLast {
|
||||
task release {
|
||||
dependsOn build
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile){
|
||||
options.encoding="utf-8"
|
||||
}
|
||||
|
||||
tasks.withType(Javadoc){
|
||||
options.encoding="utf-8"
|
||||
}
|
||||
|
@ -0,0 +1,56 @@
|
||||
package org.opentcs.charge;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import org.opentcs.charge.entity.ChargePiles;
|
||||
import org.opentcs.data.model.Vehicle;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author xuzhiheng
|
||||
* @date 2025-5-19
|
||||
* @desc 充电策略
|
||||
*/
|
||||
public class ChargeStrategy {
|
||||
|
||||
/**
|
||||
* LOG
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ChargeStrategy.class);
|
||||
/**
|
||||
* 所有车辆电量:key为车辆名称,value为电量百分比
|
||||
*/
|
||||
public static HashMap<String, Integer> vehiclePower = new HashMap<>();
|
||||
/**
|
||||
* 充电桩维护对象
|
||||
*/
|
||||
public static ArrayList<ChargePiles> AllChargePiles;
|
||||
|
||||
/**
|
||||
* 闲时充电
|
||||
* @param vehicle 车辆
|
||||
* @param power 电量百分比
|
||||
* @param batteryStatus 电池状态
|
||||
* @param energyLevelThresholdSet 设定阈值
|
||||
*/
|
||||
public synchronized static void chargeStrategy(String vehicle, Integer power, Integer batteryStatus, Vehicle.EnergyLevelThresholdSet energyLevelThresholdSet) {
|
||||
|
||||
LOG.debug("vehicle: {}, start exec chargeStrategy" , vehicle);
|
||||
|
||||
//充电桩数组还是为空,直接返回
|
||||
if (AllChargePiles.isEmpty()) {
|
||||
//无充电桩,无法执行自动充电,返回未知状态
|
||||
return;
|
||||
}
|
||||
|
||||
if (batteryStatus == 0) { //放电状态
|
||||
//判断是否执行充电
|
||||
|
||||
} else { //充电状态
|
||||
//判断是否释放车辆
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package org.opentcs.charge.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ChargePiles {
|
||||
|
||||
/**
|
||||
* 充电桩名称
|
||||
*/
|
||||
private String pilesName;
|
||||
/**
|
||||
* 占用车辆
|
||||
*/
|
||||
private String vehicleName;
|
||||
/**
|
||||
* 充电桩状态:true充电中,false空闲
|
||||
*/
|
||||
private Boolean status;
|
||||
/**
|
||||
* 电量
|
||||
*/
|
||||
private Integer power;
|
||||
}
|
@ -18,9 +18,13 @@ public interface GuestUserCredentials {
|
||||
/**
|
||||
* 主机IP
|
||||
*/
|
||||
String IP = "192.168.124.109";
|
||||
String IP = "0.0.0.0";
|
||||
/**
|
||||
* 内核开放端口
|
||||
*/
|
||||
Integer PORT = 1099;
|
||||
/**
|
||||
* WMS系统地址
|
||||
*/
|
||||
String WMS_URL = "http://192.168.124.114:2004/api/project.yueda.agv.agv/updateAgvTaskDevice";
|
||||
}
|
||||
|
@ -54,8 +54,7 @@ public interface LoopbackAdapterConstants {
|
||||
*/
|
||||
String AGV_PORT = "AGV:PORT";
|
||||
/**
|
||||
* 定制点位类型,1=WMS请求点位
|
||||
* 对应路线车辆行驶姿态:0=正走,1=到走
|
||||
*/
|
||||
String POINT_TYPE = "tcs:point";
|
||||
|
||||
String AGV_DRIVE_POSE = "PATH:DRIVE_POSE";
|
||||
}
|
||||
|
@ -0,0 +1,236 @@
|
||||
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.opentcs.communication.http.service.ExecuteOperation;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author xuzhiheng
|
||||
* @date 2025/5/12
|
||||
* @desc http客户端
|
||||
*/
|
||||
public class HttpClient {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HttpClient.class);
|
||||
|
||||
private final OkHttpClient client;
|
||||
|
||||
// private final String url = "http://192.168.124.121:2005";
|
||||
|
||||
public HttpClient() {
|
||||
this.client = new OkHttpClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送 POST 请求
|
||||
* @return ApiResponse 对象
|
||||
*/
|
||||
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);
|
||||
RequestBody body = RequestBody.create(jsonBody, MediaType.get("application/json"));
|
||||
Request request = new Request.Builder().url(url).post(body).build();
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
return response.body().string();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("HTTP POST Request Failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 测试代码
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
|
||||
|
||||
ExecuteOperation executeOperation = new ExecuteOperation();
|
||||
|
||||
{ //暂停订单
|
||||
ExecuteOperation.pauseOrder("v1", 1);
|
||||
}
|
||||
|
||||
{ //继续执行订单
|
||||
ExecuteOperation.continueOrder("v1", 1);
|
||||
}
|
||||
|
||||
{ //撤销订单
|
||||
ExecuteOperation.cancelOrder("v1");
|
||||
}
|
||||
|
||||
|
||||
// HttpClient httpClient = new HttpClient();
|
||||
|
||||
// { //0x02
|
||||
// //变量成员
|
||||
// ArrayList<org.opentcs.communication.http.dto.kc.cmd02.ValueMember> valueMemberList = new ArrayList<>();
|
||||
// org.opentcs.communication.http.dto.kc.cmd02.ValueMember valueMember = new org.opentcs.communication.http.dto.kc.cmd02.ValueMember();
|
||||
// valueMember.setLength(4);
|
||||
// valueMember.setOffset(0);
|
||||
// valueMemberList.add(valueMember);
|
||||
//
|
||||
// //变量结构体
|
||||
// ArrayList<org.opentcs.communication.http.dto.kc.cmd02.StrValue> strValueList = new ArrayList<>();
|
||||
// org.opentcs.communication.http.dto.kc.cmd02.StrValue strValue = new org.opentcs.communication.http.dto.kc.cmd02.StrValue();
|
||||
// strValue.setName("NaviControl");
|
||||
// strValue.setCount(1);
|
||||
// strValue.setMember(valueMemberList);
|
||||
// strValueList.add(strValue);
|
||||
//
|
||||
// //请求对象
|
||||
// Request02 request02 = new Request02();
|
||||
// request02.setVar(strValueList);
|
||||
// request02.setVarCount(1);
|
||||
//
|
||||
// BaseRequestTo baseRequestTo = new BaseRequestTo(KcCmdEnum.getValueByKey(0x02), "OPENTCS", "KC-CTRL", request02);
|
||||
//
|
||||
// httpClient.sendCommand(baseRequestTo);
|
||||
// }
|
||||
|
||||
// { //0xB0
|
||||
// BaseRequestTo baseRequestTo = new BaseRequestTo(KcCmdEnum.getValueByKey(0xB0), "OPENTCS", "KC-CTRL", new Object());
|
||||
// httpClient.sendCommand(baseRequestTo);
|
||||
// }
|
||||
|
||||
// { //0xB2
|
||||
// //action: 0x01
|
||||
// Action01 action01 = new Action01();
|
||||
// action01.setIsStopImmediately(1);
|
||||
// ArrayList<Action01> actionList = new ArrayList<>();
|
||||
// actionList.add(action01);
|
||||
//
|
||||
//// //action: 0x02
|
||||
//// Action02 action02 = new Action02();
|
||||
//// action02.setOrderId(1);
|
||||
//// action02.setTaskKey(1);
|
||||
//// ArrayList<Action02> actionList = new ArrayList<>();
|
||||
//// actionList.add(action02);
|
||||
////
|
||||
//// //action: 0x03
|
||||
//// Action03 action03 = new Action03();
|
||||
//// action03.setOrderId(1);
|
||||
//// action03.setIsStopImmediately(1);
|
||||
//// ArrayList<Action03> actionList = new ArrayList<>();
|
||||
//// actionList.add(action03);
|
||||
//
|
||||
// RequestB2 requestB2 = new RequestB2();
|
||||
// requestB2.setActionType(KcActionCmdEnum.getValueByKey(0x03));
|
||||
// requestB2.setActionParallelManner(0x02);
|
||||
// requestB2.setParamSize(8);
|
||||
// requestB2.setParamData(actionList);
|
||||
//
|
||||
// BaseRequestTo baseRequestTo = new BaseRequestTo(KcCmdEnum.getValueByKey(0xB2), "OPENTCS", "KC-CTRL", requestB2);
|
||||
// httpClient.sendCommand(baseRequestTo);
|
||||
// }
|
||||
|
||||
// { //0x17
|
||||
// BaseRequestTo baseRequestTo = new BaseRequestTo(KcCmdEnum.getValueByKey(0x17), "OPENTCS", "KC-CTRL", new Object());
|
||||
// httpClient.sendCommand(baseRequestTo);
|
||||
// }
|
||||
|
||||
// { //0x1F
|
||||
// BaseRequestTo baseRequestTo = new BaseRequestTo(KcCmdEnum.getValueByKey(0x1F), "OPENTCS", "KC-CTRL", new Object());
|
||||
// httpClient.sendCommand(baseRequestTo);
|
||||
// }
|
||||
|
||||
// { //0x14
|
||||
// Request14 request14 = new Request14();
|
||||
// request14.setX(3.0);
|
||||
// request14.setY(2.0);
|
||||
// request14.setAngle(1.0);
|
||||
//
|
||||
// BaseRequestTo baseRequestTo = new BaseRequestTo(KcCmdEnum.getValueByKey(0x14), "OPENTCS", "KC-CTRL", request14);
|
||||
// httpClient.sendCommand(baseRequestTo);
|
||||
// }
|
||||
|
||||
// { //AF
|
||||
// BaseRequestTo baseRequestTo = new BaseRequestTo(KcCmdEnum.getValueByKey(0xAF), "OPENTCS", "KC-CTRL", new Object());
|
||||
// httpClient.sendCommand(baseRequestTo);
|
||||
// }
|
||||
|
||||
// { //0x03
|
||||
// //变量成员
|
||||
// ArrayList<ValueMember> valueMemberList = new ArrayList<>();
|
||||
// org.opentcs.communication.http.dto.kc.cmd03.ValueMember valueMember = new org.opentcs.communication.http.dto.kc.cmd03.ValueMember();
|
||||
// valueMember.setLength(4);
|
||||
// valueMember.setOffset(0);
|
||||
// valueMember.setVelue(1);
|
||||
// valueMemberList.add(valueMember);
|
||||
//
|
||||
// //变量结构体
|
||||
// ArrayList<StrValue> strValueList = new ArrayList<>();
|
||||
// org.opentcs.communication.http.dto.kc.cmd03.StrValue strValue = new org.opentcs.communication.http.dto.kc.cmd03.StrValue();
|
||||
// strValue.setName("NaviControl");
|
||||
// strValue.setCount(1);
|
||||
// strValue.setMember(valueMemberList);
|
||||
// strValueList.add(strValue);
|
||||
//
|
||||
// //请求对象
|
||||
// Request03 request03 = new Request03();
|
||||
// request03.setVar(strValueList);
|
||||
// request03.setVarCount(1);
|
||||
//
|
||||
// BaseRequestTo baseRequestTo = new BaseRequestTo(KcCmdEnum.getValueByKey(0x03), "OPENTCS", "KC-CTRL", request03);
|
||||
//
|
||||
// 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<Point> 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<Path> 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);
|
||||
// }
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package org.opentcs.communication.http.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author xuzhiheng
|
||||
* @date 2025/5/12
|
||||
* @desc 实体基类
|
||||
*/
|
||||
@Data
|
||||
public class BaseRequestTo {
|
||||
|
||||
public BaseRequestTo(
|
||||
Integer type,
|
||||
String sender,
|
||||
String receiver,
|
||||
Integer serial_num,
|
||||
String time,
|
||||
Object params
|
||||
) {
|
||||
this.type = type;
|
||||
this.sender = sender;
|
||||
this.receiver = receiver;
|
||||
this.serial_num = serial_num;
|
||||
this.time = time;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
private Integer type;
|
||||
|
||||
private String sender;
|
||||
|
||||
private String receiver;
|
||||
|
||||
private Integer serial_num;
|
||||
|
||||
private String time;
|
||||
|
||||
private Object params;
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package org.opentcs.communication.http.dto.kc.action;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author xuzhiheng
|
||||
*/
|
||||
@Data
|
||||
public class RequestAction {
|
||||
|
||||
/**
|
||||
* 订单名称
|
||||
*/
|
||||
private String order_name;
|
||||
/**
|
||||
* 动作
|
||||
*/
|
||||
private String action;
|
||||
/**
|
||||
* 当前位置
|
||||
*/
|
||||
private String point;
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package org.opentcs.communication.http.dto.kc.move;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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 driver_pose;
|
||||
/**
|
||||
* 指定的目标最大速度
|
||||
*/
|
||||
private float max_speed;
|
||||
/**
|
||||
* 指定的目标最大角速度
|
||||
*/
|
||||
private float max_angle_speed;
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package org.opentcs.communication.http.dto.kc.move;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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;
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package org.opentcs.communication.http.dto.kc.move;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author xuzhiheng
|
||||
* @date 2025/5/12
|
||||
* @desc 实体类
|
||||
*/
|
||||
@Data
|
||||
public class RequestAe {
|
||||
|
||||
public RequestAe() {}
|
||||
|
||||
/**
|
||||
* 订单ID wms_order_id
|
||||
*/
|
||||
private Integer wms_order_id;
|
||||
/**
|
||||
* 订单ID
|
||||
*/
|
||||
private Integer order_id;
|
||||
/**
|
||||
* 任务ID task_key: 任务的唯一标识。与订单 ID 绑定,从 1 开始,当同一订单下发新的资源时加 1;
|
||||
*/
|
||||
private Integer task_key;
|
||||
/**
|
||||
* 点个数 point_size
|
||||
*/
|
||||
private Integer point_size;
|
||||
/**
|
||||
* 路径个数 path_size
|
||||
*/
|
||||
private Integer path_size;
|
||||
/**
|
||||
* 点信息结构
|
||||
*/
|
||||
private ArrayList<Point> point_info;
|
||||
/**
|
||||
* 路线信息结构
|
||||
*/
|
||||
private ArrayList<Path> path_info;
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package org.opentcs.communication.http.dto.kc.operation;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author xuzhiheng
|
||||
* @date 2025/5/14
|
||||
* @desc 实体类
|
||||
*/
|
||||
@Data
|
||||
public class Action01 {
|
||||
|
||||
/**
|
||||
* 是否立即停止移动: 0:机器人正常移动到点上停止(停不下来就移动到下一个点), 1:立刻停止(缓停);
|
||||
*/
|
||||
private Integer isStopImmediately;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package org.opentcs.communication.http.dto.kc.operation;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author xuzhiheng
|
||||
* @date 2025/5/13
|
||||
* @desc 实体类
|
||||
*/
|
||||
@Data
|
||||
public class Action02 {
|
||||
|
||||
/**
|
||||
* 订单ID
|
||||
*/
|
||||
private Integer orderId;
|
||||
/**
|
||||
* 任务key
|
||||
*/
|
||||
private Integer taskKey;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package org.opentcs.communication.http.dto.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;
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package org.opentcs.communication.http.dto.kc.operation;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author xuzhiheng
|
||||
* @date 2025/5/14
|
||||
* @desc 实体类
|
||||
*/
|
||||
@Data
|
||||
public class RequestB2 {
|
||||
|
||||
/**
|
||||
* 动作类型-》填写动作命令码
|
||||
*/
|
||||
private Integer action_type;
|
||||
/**
|
||||
* 执行动作并行方式: 0x00:为移动和动作间都可并行, 0x01:为动作间可以并行,不能移动, 0x02 只能执行当前动作
|
||||
*/
|
||||
private Integer action_parallel_manner;
|
||||
/**
|
||||
* 参数内容长度
|
||||
*/
|
||||
private Integer param_size;
|
||||
/**
|
||||
* 参数内容
|
||||
*/
|
||||
private Object param_data;
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package org.opentcs.communication.http.enums;
|
||||
|
||||
public enum Actions {
|
||||
|
||||
//取请求
|
||||
PICK_UP_REQUEST,
|
||||
//放请求
|
||||
RELEASE_REQUEST,
|
||||
//充电
|
||||
CHARGE,
|
||||
//取消充电
|
||||
CANCEL_CHARGE,
|
||||
//取货
|
||||
PICK_UP,
|
||||
//放货
|
||||
RELEASE,
|
||||
;
|
||||
|
||||
/**
|
||||
* 判断是否存在当前动作
|
||||
* @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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package org.opentcs.communication.http.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<Integer, KcActionCmdEnum> 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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package org.opentcs.communication.http.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<Integer, KcCmdEnum> 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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package org.opentcs.communication.http.service;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.opentcs.communication.http.enums.VehicleEnums;
|
||||
|
||||
public class BaseService {
|
||||
|
||||
// 随机初始化起点,减少重复概率
|
||||
private static final AtomicInteger counter = new AtomicInteger(
|
||||
ThreadLocalRandom.current().nextInt(1, Integer.MAX_VALUE / 2)
|
||||
);
|
||||
|
||||
public static String getUrl(String vehicleName) {
|
||||
VehicleEnums vehicleEnums = VehicleEnums.valueOf(vehicleName);
|
||||
return "http://" + vehicleEnums.getIp() + ":" + vehicleEnums.getPort();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成进程内唯一的int型ID
|
||||
* 优点:简单高效
|
||||
* 限制:
|
||||
* - 重启后可能重复,但数据是内存中存在,重启即销毁
|
||||
* - 超过21亿后会回绕(正常应用很难达到)
|
||||
*/
|
||||
public static int generate() {
|
||||
int id = counter.getAndIncrement();
|
||||
// 处理溢出(当接近int上限时重置为随机值)
|
||||
return (id < 0) ? counter.getAndSet(
|
||||
ThreadLocalRandom.current().nextInt(1, Integer.MAX_VALUE / 2)
|
||||
) : id;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package org.opentcs.communication.http.service;
|
||||
|
||||
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.action.RequestAction;
|
||||
import org.opentcs.communication.http.enums.VehicleEnums;
|
||||
|
||||
public class ExecuteAction extends BaseService {
|
||||
|
||||
/**
|
||||
* 下发动作到平台
|
||||
* @param name 名称
|
||||
* @param vehicleName 车辆名称
|
||||
* @param point 当前位置
|
||||
* @param action 动作
|
||||
* @param serialNum 序列号
|
||||
*/
|
||||
public static void sendCmd(String name, String vehicleName, String point, String action, 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);
|
||||
|
||||
RequestAction requestAction = new RequestAction();
|
||||
requestAction.setOrder_name(name);
|
||||
requestAction.setAction(action);
|
||||
requestAction.setPoint(point);
|
||||
|
||||
BaseRequestTo baseRequestTo = new BaseRequestTo(
|
||||
4,
|
||||
"OPENTCS",
|
||||
"KC-CTRL",
|
||||
serialNum,
|
||||
time,
|
||||
requestAction
|
||||
);
|
||||
|
||||
HttpClient httpClient = new HttpClient();
|
||||
httpClient.sendCommand(url, baseRequestTo);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,211 @@
|
||||
package org.opentcs.communication.http.service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Objects;
|
||||
import org.opentcs.common.LoopbackAdapterConstants;
|
||||
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;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ExecuteMove extends BaseService {
|
||||
|
||||
/**
|
||||
* LOG
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ExecuteMove.class);
|
||||
/**
|
||||
* 订单名映射int类型数据.
|
||||
*/
|
||||
private static final HashMap<String, OrderInfo> orderInfoMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 发送移动指令
|
||||
* @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(vehicleName, cmd);
|
||||
BaseRequestTo baseRequestTo = new BaseRequestTo(2, "OPENTCS", "KC-CTRL", serialNum, time, ae);
|
||||
|
||||
HttpClient httpClient = new HttpClient();
|
||||
httpClient.sendCommand(url, baseRequestTo);
|
||||
}
|
||||
|
||||
private static RequestAe buildCommand(String vehicleName, MovementCommand cmd)
|
||||
{
|
||||
// 自动维护订单ID和任务key
|
||||
getUniqueOrderID(vehicleName, cmd.getTransportOrder().getName());
|
||||
OrderInfo orderInfo = orderInfoMap.get(vehicleName);
|
||||
|
||||
//订单ID
|
||||
int orderID = orderInfo.getId();
|
||||
//下发起点
|
||||
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 = 5; //获取最大速度
|
||||
int maxReverseVelocity = 5; //获取最大反向速度
|
||||
if (pathInfo != null) {
|
||||
maxVelocity = pathInfo.getMaxVelocity(); //获取设置速度
|
||||
maxReverseVelocity = pathInfo.getMaxReverseVelocity();
|
||||
}
|
||||
maxVelocity = 5;
|
||||
|
||||
Integer drivePose = getDrivePose(pathInfo); //行驶姿态
|
||||
|
||||
RequestAe ae = new RequestAe();
|
||||
ae.setWms_order_id(1); //todo 测试填写
|
||||
ae.setOrder_id(orderID);
|
||||
ae.setTask_key(orderInfo.getTaskKey());
|
||||
orderInfo.setTaskKey(orderInfo.getTaskKey() + 1);
|
||||
ae.setPoint_size(2);
|
||||
ae.setPath_size(1);
|
||||
|
||||
ArrayList<Point> pointList = new ArrayList<>();
|
||||
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_angle(0);
|
||||
|
||||
pointList.add(point);
|
||||
}
|
||||
ae.setPoint_info(pointList);
|
||||
|
||||
ArrayList<Path> pathList = new ArrayList<>();
|
||||
for (int i = 0; i < ae.getPath_size(); i++) {
|
||||
Path path = new Path();
|
||||
|
||||
path.setPath_id(pathID);
|
||||
path.setPath_serial(orderInfo.getPathSerialNum());
|
||||
path.setPath_angle(0);
|
||||
path.setDriver_pose(drivePose);
|
||||
path.setMax_speed(maxVelocity);
|
||||
path.setMax_angle_speed(maxVelocity);
|
||||
|
||||
orderInfo.setPathSerialNum(orderInfo.getPathSerialNum() + 2);
|
||||
|
||||
pathList.add(path);
|
||||
}
|
||||
ae.setPath_info(pathList);
|
||||
|
||||
return ae;
|
||||
}
|
||||
|
||||
/**
|
||||
* 维护订单名对应int类型唯一ID--------todo 待优化:如果调度重启,控制器也需要重启。否则0xAE指令会因为重置订单ID和任务key下发失败。应改成wms数据库ID交互
|
||||
* @param vehicleName 车辆名称
|
||||
* @param orderName 订单名称
|
||||
* @return Integer
|
||||
*/
|
||||
private static void getUniqueOrderID(String vehicleName, String orderName){
|
||||
|
||||
|
||||
if (orderInfoMap.containsKey(vehicleName)) {
|
||||
//当前车辆已存在集合中
|
||||
OrderInfo orderInfo = orderInfoMap.get(vehicleName);
|
||||
|
||||
if (!Objects.equals(orderName, orderInfo.getName()) && orderInfoMap.containsKey(vehicleName)) {
|
||||
orderInfo.setId(generate());
|
||||
orderInfo.setName(orderName);
|
||||
orderInfo.setState(1);
|
||||
orderInfo.setTaskKey(1);
|
||||
orderInfo.setPointSerialNum(0);
|
||||
orderInfo.setPathSerialNum(1);
|
||||
}
|
||||
} else {
|
||||
//车辆不在集合中
|
||||
OrderInfo orderInfo = new OrderInfo();
|
||||
orderInfo.setId(generate());
|
||||
orderInfo.setName(orderName);
|
||||
orderInfo.setState(1);
|
||||
orderInfo.setTaskKey(1);
|
||||
orderInfo.setPointSerialNum(0);
|
||||
orderInfo.setPathSerialNum(1);
|
||||
|
||||
orderInfoMap.put(vehicleName, orderInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前订单ID
|
||||
*/
|
||||
public static Integer getOrderID(String vehicleName) {
|
||||
|
||||
if (orderInfoMap.containsKey(vehicleName)) {
|
||||
return orderInfoMap.get(vehicleName).getId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理对应订单,实现平台接管后继续执行订单
|
||||
*/
|
||||
public static boolean resetOrder(String vehicleName) {
|
||||
|
||||
orderInfoMap.remove(vehicleName);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回行驶姿态
|
||||
*/
|
||||
private static Integer getDrivePose(org.opentcs.data.model.Path path) {
|
||||
String drivePost = path.getProperties().get(LoopbackAdapterConstants.AGV_DRIVE_POSE);
|
||||
// System.out.println("getDrivePose drivePost: " + drivePost);
|
||||
|
||||
int pose = 0;
|
||||
if (drivePost == null) {
|
||||
return pose;
|
||||
}
|
||||
|
||||
switch (drivePost) {
|
||||
case "1" -> pose = 1;
|
||||
default -> {
|
||||
LOG.error("getDrivePose UNKNOWN DRIVE POSE: {}", drivePost);
|
||||
throw new IllegalArgumentException("UNKNOWN DRIVE POSE: " + drivePost);
|
||||
}
|
||||
}
|
||||
|
||||
return pose;
|
||||
}
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
package org.opentcs.communication.http.service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HashMap;
|
||||
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);
|
||||
/**
|
||||
* 通讯适配器当前执行taskkey
|
||||
*/
|
||||
private static HashMap<String, Integer> execTaskKeyMap = new HashMap<>();
|
||||
/**
|
||||
* 序列号
|
||||
*/
|
||||
private static Integer serialNum = 0;
|
||||
|
||||
/**
|
||||
* 暂停执行订单
|
||||
* @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(0);
|
||||
|
||||
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 = 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 车辆名称
|
||||
*/
|
||||
public static void cancelOrder(String vehicleName) {
|
||||
|
||||
//获取订单ID
|
||||
Integer orderId = ExecuteMove.getOrderID(vehicleName);
|
||||
|
||||
if (orderId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
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(0);
|
||||
|
||||
b2.setParam_data(action03);
|
||||
|
||||
sendCmd(vehicleName, b2, getSerialNum());
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前执行任务key
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
private static int getSerialNum() {
|
||||
return serialNum++;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
package org.opentcs.communication.mqtt;
|
||||
|
||||
import io.moquette.interception.InterceptHandler;
|
||||
import io.moquette.interception.messages.InterceptAcknowledgedMessage;
|
||||
import io.moquette.interception.messages.InterceptConnectMessage;
|
||||
import io.moquette.interception.messages.InterceptConnectionLostMessage;
|
||||
import io.moquette.interception.messages.InterceptDisconnectMessage;
|
||||
import io.moquette.interception.messages.InterceptPublishMessage;
|
||||
import io.moquette.interception.messages.InterceptSubscribeMessage;
|
||||
import io.moquette.interception.messages.InterceptUnsubscribeMessage;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.mqtt.MqttQoS;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author xuzhiheng
|
||||
* @date 2025/5/22
|
||||
* @desc 重写MQTT代理拦截器:用于监听MQTT代理的连接、断开、发布、订阅、取消订阅、消息确认等事件
|
||||
*/
|
||||
public class BrokerInterceptor implements InterceptHandler {
|
||||
|
||||
/**
|
||||
* LOG
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BrokerInterceptor.class);
|
||||
|
||||
@Override public String getID() {
|
||||
return "BrokerInterceptor_" + hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?>[] getInterceptedMessageTypes() {
|
||||
return new Class<?>[] {
|
||||
InterceptConnectMessage.class,
|
||||
InterceptDisconnectMessage.class,
|
||||
InterceptConnectionLostMessage.class,
|
||||
InterceptPublishMessage.class,
|
||||
InterceptSubscribeMessage.class,
|
||||
InterceptUnsubscribeMessage.class,
|
||||
InterceptAcknowledgedMessage.class,
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnect(InterceptConnectMessage msg) {
|
||||
LOG.info("[connect] client ID: {} connect to server", msg.getClientID());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnect(InterceptDisconnectMessage msg) {
|
||||
LOG.info("[disconnect] client ID: {} disconnect", msg.getClientID());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionLost(InterceptConnectionLostMessage interceptConnectionLostMessage) {
|
||||
LOG.info("[lost] client ID: {} lost connection", interceptConnectionLostMessage.getClientID());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPublish(InterceptPublishMessage msg) {
|
||||
// 解码消息内容
|
||||
ByteBuf payload = msg.getPayload();
|
||||
String content = payload.toString(StandardCharsets.UTF_8);
|
||||
|
||||
System.out.println("[MessageInterception] topic: " + msg.getTopicName() + ", clientID: " + msg.getClientID() + ", QoS: " + MqttQoS.valueOf(String.valueOf(msg.getQos())) + ", content: " + content);
|
||||
|
||||
LOG.info("[MessageInterception] topic: {}, clientID: {}, QoS: {}, content: {}",
|
||||
msg.getTopicName(),
|
||||
msg.getClientID(),
|
||||
MqttQoS.valueOf(String.valueOf(msg.getQos())),
|
||||
content);
|
||||
|
||||
// 必须保留引用计数,防止内存泄漏
|
||||
payload.retain();
|
||||
|
||||
MqttBrokerServer mqttBrokerServer = new MqttBrokerServer();
|
||||
mqttBrokerServer.sendToClient("v1", MqttQoS.AT_LEAST_ONCE, "hello mqtt client");
|
||||
}
|
||||
|
||||
// 其他需要实现的接口方法(可以留空)
|
||||
@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());
|
||||
}
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
package org.opentcs.communication.mqtt;
|
||||
|
||||
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;
|
||||
import io.netty.handler.codec.mqtt.MqttPublishMessage;
|
||||
import io.netty.handler.codec.mqtt.MqttPublishVariableHeader;
|
||||
import io.netty.handler.codec.mqtt.MqttQoS;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Properties;
|
||||
import org.opentcs.common.GuestUserCredentials;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author xuzhiheng
|
||||
* @date 2025/5/22
|
||||
* @desc MQTT服务
|
||||
*/
|
||||
public class MqttBrokerServer {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MqttBrokerServer.class);
|
||||
private Server mqttServer;
|
||||
|
||||
public MqttBrokerServer() {
|
||||
mqttServer = new Server();
|
||||
}
|
||||
|
||||
/**
|
||||
* start MQTT server
|
||||
* @throws IOException start MQTT server
|
||||
*/
|
||||
public void startMqtt() {
|
||||
try {
|
||||
// 从资源目录加载配置文件
|
||||
Properties configProps = new Properties();
|
||||
try (var input = getClass().getResourceAsStream("/mqtt.conf")) {
|
||||
if (input == null) {
|
||||
throw new IOException("profiles [mqtt.conf] not found");
|
||||
}
|
||||
configProps.load(input);
|
||||
}
|
||||
|
||||
// 使用配置文件初始化
|
||||
IConfig config = new MemoryConfig(configProps);
|
||||
|
||||
mqttServer.startServer(config);
|
||||
mqttServer.addInterceptHandler(new BrokerInterceptor());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭MQTT服务
|
||||
*/
|
||||
public void stopMqtt() {
|
||||
mqttServer.stopServer();
|
||||
LOG.info("MQTT service stopped successfully");
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息给指定客户端 todo 待实现
|
||||
* @param clientId 客户端ID 暂时定义为车辆ID
|
||||
* @param qosLevel 服务质量等级
|
||||
* @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(), // 主题
|
||||
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(); // 确保释放
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package org.opentcs.communication.mqtt;
|
||||
|
||||
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
|
||||
import org.eclipse.paho.client.mqttv3.MqttCallback;
|
||||
import org.eclipse.paho.client.mqttv3.MqttClient;
|
||||
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
|
||||
import org.eclipse.paho.client.mqttv3.MqttException;
|
||||
import org.eclipse.paho.client.mqttv3.MqttMessage;
|
||||
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
|
||||
|
||||
public class MqttClientExample {
|
||||
|
||||
public static void main(String[] args) {
|
||||
String broker = "tcp://192.168.124.114:1883";
|
||||
String clientId = "50";
|
||||
String topic = "vehicles/50/commands";
|
||||
int qos = 1;
|
||||
|
||||
try (MqttClient client = new MqttClient(broker, clientId, new MemoryPersistence())) {
|
||||
// 配置连接选项
|
||||
MqttConnectOptions options = new MqttConnectOptions();
|
||||
options.setCleanSession(true);
|
||||
options.setAutomaticReconnect(true);
|
||||
|
||||
// 设置回调
|
||||
client.setCallback(new SimpleMqttCallback());
|
||||
|
||||
// 连接
|
||||
client.connect(options);
|
||||
System.out.println("Connected to broker");
|
||||
|
||||
// 先订阅后发布
|
||||
client.subscribe(topic, qos);
|
||||
System.out.println("Subscribed to: " + topic);
|
||||
|
||||
// 发布消息
|
||||
String content = "Hello, MQTT";
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
client.publish(topic, content.getBytes(), qos, false);
|
||||
Thread.sleep(300);
|
||||
}
|
||||
|
||||
// 等待接收消息
|
||||
Thread.sleep(5000);
|
||||
|
||||
} catch (MqttException | InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static class SimpleMqttCallback implements MqttCallback {
|
||||
@Override
|
||||
public void connectionLost(Throwable cause) {
|
||||
System.err.println("Connection lost: " + cause.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageArrived(String topic, MqttMessage message) {
|
||||
System.out.printf("[%s] Received: %s%n", topic, new String(message.getPayload()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deliveryComplete(IMqttDeliveryToken token) {
|
||||
System.out.println("Message delivered");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package org.opentcs.communication.mqtt;
|
||||
|
||||
/**
|
||||
* @author xuzhiheng
|
||||
* @date 2025/5/22
|
||||
* @desc 返回mqtt主题
|
||||
*/
|
||||
public class TopicBuilder {
|
||||
|
||||
//预定义模板
|
||||
private static final String COMMAND_TOPIC_TEMPLATE = "vehicles/%s/commands";
|
||||
|
||||
/**
|
||||
* 构建命令主题(带QoS验证)
|
||||
*/
|
||||
public static String buildCommandTopic(String clientId) {
|
||||
return String.format(COMMAND_TOPIC_TEMPLATE, clientId);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package org.opentcs.communication.mqtt.entity.kc.action;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author xuzhiheng
|
||||
*/
|
||||
@Data
|
||||
public class RequestAction {
|
||||
|
||||
/**
|
||||
* 动作
|
||||
*/
|
||||
private String action;
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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> point;
|
||||
/**
|
||||
* 路线信息结构
|
||||
*/
|
||||
private ArrayList<Path> path;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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<Integer, KcActionCmdEnum> 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);
|
||||
}
|
||||
|
||||
}
|
@ -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<Integer, KcCmdEnum> 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);
|
||||
}
|
||||
}
|
@ -0,0 +1,223 @@
|
||||
package org.opentcs.communication.tcp;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.Socket;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class TcpClient
|
||||
implements AutoCloseable {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TcpClient.class);
|
||||
|
||||
// 配置参数
|
||||
private final String serverIp;
|
||||
private final int serverPort;
|
||||
private final int heartbeatInterval; // 心跳间隔(秒)
|
||||
private final int reconnectInterval; // 重连间隔(秒)
|
||||
|
||||
// 网络连接
|
||||
private Socket socket;
|
||||
private PrintWriter writer; //输入
|
||||
private BufferedReader reader; //输出
|
||||
|
||||
// 状态管理
|
||||
private final AtomicBoolean connected = new AtomicBoolean(false);
|
||||
private final AtomicBoolean running = new AtomicBoolean(true);
|
||||
|
||||
// 线程池
|
||||
private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
|
||||
|
||||
// 监听器接口
|
||||
public interface TcpListener {
|
||||
void onMessageReceived(String message);
|
||||
void onConnectionStatusChanged(boolean connected);
|
||||
}
|
||||
private TcpListener listener;
|
||||
|
||||
public TcpClient(String ip, int port) {
|
||||
this.serverIp = ip;
|
||||
this.serverPort = port;
|
||||
this.heartbeatInterval = 5;
|
||||
this.reconnectInterval = 3;
|
||||
}
|
||||
|
||||
public void setListener(TcpListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void connect() {
|
||||
executor.execute(() -> {
|
||||
while (running.get()) {
|
||||
try {
|
||||
initConnection();
|
||||
startHeartbeat();
|
||||
startReading();
|
||||
setConnected(true);
|
||||
|
||||
// 保持重连循环中断
|
||||
TimeUnit.SECONDS.sleep(Long.MAX_VALUE);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
catch (Exception e) {
|
||||
setConnected(false);
|
||||
System.err.println("Connection error: " + e.getMessage());
|
||||
LOG.debug("Connection error: " + e.getMessage());
|
||||
}
|
||||
|
||||
tryReconnect();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initConnection() throws IOException {
|
||||
socket = new Socket(serverIp, serverPort);
|
||||
socket.setKeepAlive(true);
|
||||
writer = new PrintWriter(socket.getOutputStream(), true);
|
||||
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动定时任务,每隔固定时间(heartbeatInterval)发送一次心跳包,只有在客户端处于连接状态(connected为true)时才发送。
|
||||
*/
|
||||
private void startHeartbeat() {
|
||||
executor.scheduleAtFixedRate(() -> {
|
||||
if (connected.get()) {
|
||||
sendData("HEARTBEAT");
|
||||
}
|
||||
}, heartbeatInterval, heartbeatInterval, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从服务器读取消息
|
||||
*/
|
||||
private void startReading() {
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
String inputLine;
|
||||
while ((inputLine = reader.readLine()) != null) {
|
||||
if (listener != null) {
|
||||
listener.onMessageReceived(inputLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
setConnected(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 重连 TCP 服务器
|
||||
*/
|
||||
private void tryReconnect() {
|
||||
if (!running.get()) return;
|
||||
|
||||
System.out.println("Attempting reconnect in " + reconnectInterval + "s...");
|
||||
LOG.debug("Attempting reconnect in " + reconnectInterval + "s...");
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(reconnectInterval);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送数据
|
||||
* @param data 数据
|
||||
*/
|
||||
public synchronized void sendData(String data) {
|
||||
if (connected.get()) {
|
||||
writer.println(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置连接状态
|
||||
* @param status 状态
|
||||
*/
|
||||
private void setConnected(boolean status) {
|
||||
connected.set(status);
|
||||
if (listener != null) {
|
||||
listener.onConnectionStatusChanged(status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取连接状态
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean getConnected() {
|
||||
return connected.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭连接
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
running.set(false);
|
||||
executor.shutdownNow();
|
||||
|
||||
try {
|
||||
if (socket != null) socket.close();
|
||||
if (writer != null) writer.close();
|
||||
if (reader != null) reader.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
System.err.println("Close error: " + e.getMessage());
|
||||
LOG.debug("Close error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 使用示例
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
// 创建客户端实例
|
||||
TcpClient client = new TcpClient("192.168.124.114", 60000);
|
||||
|
||||
// 设置监听器
|
||||
client.setListener(new TcpListener() {
|
||||
@Override
|
||||
public void onMessageReceived(String message) {
|
||||
System.out.println("[RESPONSE] " + message);
|
||||
LOG.debug("[RESPONSE] " + message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionStatusChanged(boolean connected) {
|
||||
System.out.println("[STATE] CONNECTION-STATUS: " + (connected ? "SUCCESS" : "FAIL"));
|
||||
LOG.debug("[STATE] CONNECTION-STATUS: " + (connected ? "SUCCESS" : "FAIL"));
|
||||
}
|
||||
});
|
||||
|
||||
// 启动连接
|
||||
client.connect();
|
||||
|
||||
// 等待连接建立
|
||||
Thread.sleep(2000);
|
||||
|
||||
// 发送测试数据
|
||||
new Thread(() -> {
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
String data = "TEST MESSAGE-" + i;
|
||||
System.out.println("[SEND] " + data);
|
||||
client.sendData(data);
|
||||
try {
|
||||
Thread.sleep(1500);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
@ -106,27 +106,27 @@ public class KCCommandDemo {
|
||||
// }
|
||||
// }
|
||||
|
||||
// {
|
||||
// //0x02(read操作)
|
||||
// AgvEvent agvEvent = readValue();
|
||||
// printInfo(agvEvent);
|
||||
// RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
|
||||
// if(rcv.isOk()){
|
||||
// System.out.println();
|
||||
// System.out.println("received transationId : "+ "isok:"+rcv.isOk());
|
||||
// printInfo(rcv);
|
||||
// ReadRsp readRsp = new ReadRsp(rcv.getDataBytes());
|
||||
// if(readRsp.isOk()){
|
||||
// //get and parse value
|
||||
// System.out.println("read ok");
|
||||
// }else {
|
||||
// System.out.println("read failed");
|
||||
// }
|
||||
// }else {
|
||||
// System.out.println();
|
||||
// System.out.println("received transationId : "+ "isok:"+rcv.isOk());
|
||||
// }
|
||||
// }
|
||||
{
|
||||
//0x02(read操作)
|
||||
AgvEvent agvEvent = readValue();
|
||||
printInfo(agvEvent);
|
||||
RcvEventPackage rcv = UDPClient.localAGV.send(agvEvent);
|
||||
if(rcv.isOk()){
|
||||
System.out.println();
|
||||
System.out.println("received transationId : "+ "isok:"+rcv.isOk());
|
||||
printInfo(rcv);
|
||||
ReadRsp readRsp = new ReadRsp(rcv.getDataBytes());
|
||||
if(readRsp.isOk()){
|
||||
//get and parse value
|
||||
System.out.println("read ok");
|
||||
}else {
|
||||
System.out.println("read failed");
|
||||
}
|
||||
}else {
|
||||
System.out.println();
|
||||
System.out.println("received transationId : "+ "isok:"+rcv.isOk());
|
||||
}
|
||||
}
|
||||
|
||||
// {
|
||||
// //0x03(切换手自动)
|
||||
@ -277,11 +277,11 @@ public class KCCommandDemo {
|
||||
// Thread.sleep(100);
|
||||
// ActImmediately.reset();
|
||||
// }
|
||||
{
|
||||
|
||||
float height = 1.3f;
|
||||
byte modeOfMovement = 0x01;
|
||||
ActImmediately.allotsOperation(height, modeOfMovement);
|
||||
// {
|
||||
//
|
||||
// float height = 1.3f;
|
||||
// byte modeOfMovement = 0x01;
|
||||
// ActImmediately.allotsOperation(height, modeOfMovement);
|
||||
|
||||
// //读取动作执行状态
|
||||
// ActImmediately.readActionState();
|
||||
@ -301,7 +301,7 @@ public class KCCommandDemo {
|
||||
// ActImmediately.readActionState();
|
||||
//
|
||||
// ActImmediately.ACTION_EXECUTE_STATE = false;
|
||||
}
|
||||
// }
|
||||
|
||||
// {
|
||||
// ActImmediately.reset();
|
||||
@ -592,7 +592,7 @@ public class KCCommandDemo {
|
||||
|
||||
public static AgvEvent manualLocation() {
|
||||
AgvEvent agvEvent = new AgvEvent(AgvEventConstant.CommandCode_ROBOT_SET_POSITION);
|
||||
RobotSetPosition robotSetPosition = new RobotSetPosition(11, 11, 11);
|
||||
RobotSetPosition robotSetPosition = new RobotSetPosition(3, 2, 1);
|
||||
byte[] bytes = robotSetPosition.toBytes();
|
||||
agvEvent.setBody(bytes);
|
||||
return agvEvent;
|
||||
|
@ -49,7 +49,7 @@ public class HybridNavigation
|
||||
/**
|
||||
* 订单映射最大订单ID.
|
||||
*/
|
||||
private static int currentMaxiOrderId = 50;
|
||||
private static int currentMaxiOrderId = 0;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,169 @@
|
||||
package org.opentcs.manage;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.opentcs.common.GuestUserCredentials;
|
||||
import org.opentcs.manage.entity.AgvInfo;
|
||||
import org.opentcs.manage.entity.AgvInfoParams;
|
||||
import org.opentcs.manage.entity.AgvStatus;
|
||||
import org.opentcs.util.KernelCommunication;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author xuzhiheng
|
||||
*/
|
||||
public class AdapterManage {
|
||||
|
||||
/**
|
||||
* LOG
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AdapterManage.class);
|
||||
/**
|
||||
* 创建调度线程池(单线程)
|
||||
*/
|
||||
private static final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
|
||||
/**
|
||||
* Kernel服务对象
|
||||
*/
|
||||
private static KernelCommunication kernel;
|
||||
/**
|
||||
* 记录对应通讯适配器数据
|
||||
* 基于线程安全HASHMAP
|
||||
*/
|
||||
private static final ConcurrentHashMap<String, AgvStatus> adapterStatusMap = new ConcurrentHashMap<>();
|
||||
/**
|
||||
* 设置自动关闭适配器时间阈值,单位:毫秒
|
||||
*/
|
||||
private static final Long AUTO_CLOSE_TIME = 2000L;
|
||||
|
||||
//开启通讯信息
|
||||
private static final String USER = GuestUserCredentials.USER;
|
||||
private static final String PASSWORD = GuestUserCredentials.PASSWORD;
|
||||
private static final String IP = GuestUserCredentials.IP;
|
||||
private static final Integer PORT = GuestUserCredentials.PORT;
|
||||
|
||||
//开启定时任务
|
||||
public void START() {
|
||||
scheduler.scheduleWithFixedDelay(task, 5000, 1000, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
//关闭定时任务
|
||||
public void STOP() {
|
||||
scheduler.shutdownNow();
|
||||
}
|
||||
|
||||
Runnable task = () -> {
|
||||
|
||||
kernel = new KernelCommunication(USER, PASSWORD, IP, PORT);
|
||||
|
||||
autoManageAdapterStatus();
|
||||
|
||||
kernel.logout();
|
||||
};
|
||||
|
||||
/**
|
||||
* 自动管理通讯适配器
|
||||
*/
|
||||
private void autoManageAdapterStatus() {
|
||||
Date date = new Date();
|
||||
long currentTime = date.getTime();
|
||||
|
||||
// System.out.println("autoManageAdapterStatus sout: " + adapterStatusMap);
|
||||
adapterStatusMap.forEach((key, value) -> {
|
||||
// LOG.info("adapterStatusMap starts name: {}", key);
|
||||
|
||||
AgvStatus agvStatus = new AgvStatus();
|
||||
agvStatus.setTime(value.getTime());
|
||||
|
||||
if (currentTime - value.getTime() > AUTO_CLOSE_TIME) {
|
||||
//当前时间减去记录时间大于阈值,自动关闭通讯适配器
|
||||
kernel.disableAdapter(key);
|
||||
// agvStatus.setStatus(AdapterStatus.DISABLE);
|
||||
adapterStatusMap.remove(key);
|
||||
LOG.info("disable the adapter: {}", key);
|
||||
} else {
|
||||
//通讯适配器当前状态为关闭,设置状态为开启时才会进入
|
||||
kernel.enableAdapter(key);
|
||||
agvStatus.setStatus(AdapterStatus.ENABLE);
|
||||
//更新记录数据
|
||||
adapterStatusMap.put(key, agvStatus);
|
||||
LOG.info("enable the adapter: {}", key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新通讯适配器状态
|
||||
* @param name 车辆名称
|
||||
*/
|
||||
public static void setAdapterStatus(String name) {
|
||||
AgvStatus newAgvStatus = new AgvStatus();
|
||||
|
||||
Date date = new Date();
|
||||
newAgvStatus.setTime(date.getTime());
|
||||
|
||||
if (!adapterStatusMap.isEmpty() && adapterStatusMap.containsKey(name)) {
|
||||
//已记录,只更新时间
|
||||
newAgvStatus.setStatus(AdapterStatus.ENABLE);
|
||||
} else {
|
||||
//未记录,初始化
|
||||
newAgvStatus.setStatus(AdapterStatus.DISABLE);
|
||||
}
|
||||
|
||||
adapterStatusMap.put(name, newAgvStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录对应通讯适配器最后一次上报数据
|
||||
* @param name 车辆名称
|
||||
* @param data 数据
|
||||
*/
|
||||
public static AgvInfo setAdapterVehicleModel(String name, String data) {
|
||||
|
||||
JSONObject jsonObject = JSON.parseObject(data);
|
||||
|
||||
AgvInfo agvInfo = new AgvInfo();
|
||||
agvInfo.setSender(jsonObject.getString("sender"));
|
||||
agvInfo.setReceiver(jsonObject.getString("receiver"));
|
||||
agvInfo.setType(jsonObject.getInteger("type"));
|
||||
agvInfo.setSerial_num(jsonObject.getInteger("serial_num"));
|
||||
agvInfo.setTime(jsonObject.getString("time"));
|
||||
agvInfo.setParams(getAgvInfoParams(jsonObject.getString("params")));
|
||||
|
||||
return agvInfo;
|
||||
}
|
||||
|
||||
private static AgvInfoParams getAgvInfoParams(String paramsStr) {
|
||||
JSONObject params = JSON.parseObject(paramsStr);
|
||||
AgvInfoParams agvInfoParams = new AgvInfoParams();
|
||||
agvInfoParams.setX(params.getFloat("x"));
|
||||
agvInfoParams.setY(params.getFloat("y"));
|
||||
agvInfoParams.setAngle(params.getFloat("angle"));
|
||||
agvInfoParams.setPoint(params.getInteger("point"));
|
||||
agvInfoParams.setPower(params.getFloat("power"));
|
||||
agvInfoParams.setError(params.getString("error"));
|
||||
agvInfoParams.setError_level(params.getInteger("error_level"));
|
||||
agvInfoParams.setCargo_status(params.getInteger("cargo_status"));
|
||||
agvInfoParams.setAgv_status(params.getInteger("agv_status"));
|
||||
agvInfoParams.setAgv_model(params.getInteger("agv_model"));
|
||||
agvInfoParams.setCharge_status(params.getInteger("charge_status"));
|
||||
agvInfoParams.setAction_status(params.getInteger("action_status"));
|
||||
agvInfoParams.setOrder_id(params.getInteger("order_id"));
|
||||
|
||||
return agvInfoParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* 状态管理
|
||||
*/
|
||||
public interface AdapterStatus {
|
||||
Boolean ENABLE = true;
|
||||
Boolean DISABLE = false;
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package org.opentcs.manage.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AgvActionStatus {
|
||||
|
||||
/**
|
||||
* 状态: true = 动作执行完成,false = 未知,不做处理
|
||||
*/
|
||||
private Boolean status;
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package org.opentcs.manage.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AgvInfo {
|
||||
|
||||
/**
|
||||
* 发送方
|
||||
*/
|
||||
private String sender;
|
||||
/**
|
||||
* 接收
|
||||
*/
|
||||
private String receiver;
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
private Integer type;
|
||||
/**
|
||||
* 序列号
|
||||
*/
|
||||
private Integer serial_num;
|
||||
/**
|
||||
* 车辆名称
|
||||
*/
|
||||
private String vehicle_name;
|
||||
/**
|
||||
* 时间
|
||||
*/
|
||||
private String time;
|
||||
/**
|
||||
* 详细数据
|
||||
*/
|
||||
private AgvInfoParams params;
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package org.opentcs.manage.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AgvInfoParams {
|
||||
/**
|
||||
* X坐标
|
||||
*/
|
||||
private Float x;
|
||||
/**
|
||||
* Y坐标
|
||||
*/
|
||||
private Float y;
|
||||
/**
|
||||
* 角度
|
||||
*/
|
||||
private Float angle;
|
||||
/**
|
||||
* 最终经过点
|
||||
*/
|
||||
private Integer point;
|
||||
/**
|
||||
* 电池电量
|
||||
*/
|
||||
private float power;
|
||||
/**
|
||||
* 异常信息
|
||||
*/
|
||||
private String error;
|
||||
/**
|
||||
* 异常等级:信息 = 0x00;警告 = 0x01;错误 = 0x02;
|
||||
*/
|
||||
private Integer error_level;
|
||||
/**
|
||||
* agv状态:空闲 = 0,运行 = 1,暂停 = 2,初始化中= 3,无法执行任务,时的状态,如(开机后无位置、下,载地图中、程序初始化中等情况),人工确认 = 4,未初始化=5,(未设置能力集)
|
||||
*/
|
||||
private Integer agv_status;
|
||||
/**
|
||||
* 载货状态:0=无货物,1=有货物
|
||||
*/
|
||||
private Integer cargo_status;
|
||||
/**
|
||||
* 工作模式:待机 = 0,手动 = 1,半自动 = 2,自动 = 3,示教 = 4,服务 = 5(升级程序中的工作,状态),维修 = 6
|
||||
*/
|
||||
private Integer agv_model;
|
||||
/**
|
||||
* 充电情况:放电=0,充电=1
|
||||
*/
|
||||
private Integer charge_status;
|
||||
/**
|
||||
* 执行动作状态:1=完成,2=执行中
|
||||
*/
|
||||
private Integer action_status;
|
||||
/**
|
||||
* 控制器最后执行订单ID,id=0即为未创建任务
|
||||
*/
|
||||
private Integer order_id;
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package org.opentcs.manage.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AgvStatus {
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Long time;
|
||||
/**
|
||||
* 通讯适配器修改状态:trye=开启,false=关闭
|
||||
*/
|
||||
private Boolean status;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package org.opentcs.park;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import org.opentcs.park.entity.Park;
|
||||
|
||||
public class ParkStrategy {
|
||||
|
||||
private static ArrayList<Park> allPark = new ArrayList<>();
|
||||
|
||||
|
||||
public static void setPark(Park park) {
|
||||
allPark.add(park);
|
||||
printPark();
|
||||
}
|
||||
|
||||
private static void printPark() {
|
||||
for (Park park : allPark) {
|
||||
System.out.println("print park:" + park.getName() + ", status:" + park.getStatus());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package org.opentcs.park.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Park {
|
||||
|
||||
/**
|
||||
* 休息点名称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 休息点状态: true=已被占用, false=空闲
|
||||
*/
|
||||
private Boolean status;
|
||||
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author xuzhiheng
|
||||
* @Date 2025-5-12
|
||||
* @description 交管策略实现类
|
||||
*/
|
||||
public class TrafficControl {
|
||||
|
||||
/**
|
||||
* 日志
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TrafficControl.class);
|
||||
/**
|
||||
* 已占用资源对象
|
||||
*/
|
||||
private static final ResourceLock resourceLock = new ResourceLock();
|
||||
/**
|
||||
* 轮廓算法
|
||||
*/
|
||||
private static final ContourAlgorithm contourAlgorithm = new ContourAlgorithm();
|
||||
/**
|
||||
* 交管避让权重算法
|
||||
*/
|
||||
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 = () -> {
|
||||
//轮廓避障算法
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 交通管制策略
|
||||
* @param vehiclePosition 车辆当前位置
|
||||
* @param finalOccupiedPosition 车辆最终占用点位
|
||||
* @param resource 资源
|
||||
* @param type 类型
|
||||
* @return 是否可执行
|
||||
*/
|
||||
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;
|
||||
// }
|
||||
|
||||
//todo: 轮廓算法,判断下一个点位是否会被其他车辆干涉
|
||||
// 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;
|
||||
// }
|
||||
|
||||
//有所有校验通过,返回true
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 步骤执行结束,释放对应资源
|
||||
* @param resource 资源
|
||||
* @param type 类型
|
||||
*/
|
||||
public static void trafficControlRelease(String resource, String type) {
|
||||
resourceLock.trafficControlRelease(resource, type);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package org.opentcs.traffic.common;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class AvoidanceAlgorithm {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AvoidanceAlgorithm.class);
|
||||
|
||||
/**
|
||||
* 思路:判断当前车辆是否和其他车辆路径有重叠
|
||||
*
|
||||
* @return true: 订单避让算法通过,false: 订单避让算法不通过
|
||||
*/
|
||||
public boolean weightCalculation() {
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package org.opentcs.traffic.common;
|
||||
|
||||
import java.awt.geom.Point2D;
|
||||
import org.locationtech.jts.geom.Coordinate;
|
||||
import org.locationtech.jts.geom.GeometryFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author xuzhiheng
|
||||
* @date 2025/5/12
|
||||
* @desc 轮廓算法
|
||||
*/
|
||||
public class ContourAlgorithm {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ContourAlgorithm.class);
|
||||
|
||||
/**
|
||||
* 多车轮廓干扰检测
|
||||
*
|
||||
* desc:当前车辆需要占用的下一个点位的x、y坐标。在点位坐标根据车辆长、宽计算车辆轮廓,
|
||||
* 通过上报获取所有车辆的x,y坐标,计算轮廓,
|
||||
* 最终通过剩余车辆的x、y坐标,判断是否与当前车辆轮廓重叠,重叠则不通过,否则通过。
|
||||
*
|
||||
* 使用算法时间节点:
|
||||
* 1.车辆运行中,每次递归都进行判断?------(运行效率可能会低,当车辆数量上升时服务器性能有要求)车辆运行中指定时间间隔后执行轮廓检测。
|
||||
* 优化方案:1:为减小压力,应该在车辆连接时计算车辆轮廓,,,,可行性:低。考虑:车辆轮廓不是根据实时坐标计算,是否有算法可以将车辆轮廓移到当前坐标上。会不会有误差???
|
||||
* 2:递归时间节点层面延长线程阻塞时间方法,,,可行性:高。
|
||||
* 待验证:算法实现?算法效率问题?
|
||||
* 2.车辆下发运动指令前检测?-------(运行效率高)下发前检测,车辆运动中无轮廓检测。可能会发生?当前车辆不知道其他车辆位置,可能会撞车。--------放弃,会有撞车风险
|
||||
*
|
||||
* @return true,可通过,false,不通过
|
||||
*/
|
||||
public boolean interferenceDetection(Point2D.Double center) {
|
||||
|
||||
// center.setLocation(0.0, 0.0);
|
||||
|
||||
GeometryFactory geometryFactory = new GeometryFactory();
|
||||
Coordinate[] coords = {
|
||||
|
||||
};
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,203 @@
|
||||
package org.opentcs.traffic.common;
|
||||
|
||||
import java.util.concurrent.ConcurrentSkipListSet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author xuzhiheng
|
||||
* @date 2025/5/12
|
||||
* @desc 公共资源锁,保证线程安全
|
||||
*/
|
||||
public class ResourceLock {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ResourceLock.class);
|
||||
|
||||
/**
|
||||
* 静态内部类,用于封装有序的线程安全集合(基于红黑树).
|
||||
* 已锁定资源集合.
|
||||
*/
|
||||
public static class DeclaredResource {
|
||||
|
||||
/**
|
||||
* 点资源集合
|
||||
*/
|
||||
public static final ConcurrentSkipListSet<String> points = new ConcurrentSkipListSet<>();
|
||||
|
||||
/**
|
||||
* 路线资源集合
|
||||
*/
|
||||
public static final ConcurrentSkipListSet<String> paths = new ConcurrentSkipListSet<>();
|
||||
|
||||
/**
|
||||
* 库位资源集合
|
||||
*/
|
||||
public static final ConcurrentSkipListSet<String> locations = new ConcurrentSkipListSet<>();
|
||||
|
||||
/**
|
||||
* 检查对应对象是否包含某个字符串(区分大小写)
|
||||
* @param target 目标
|
||||
* @param type 类型
|
||||
* @return bool
|
||||
*/
|
||||
public static boolean checkContains(String target, String type) {
|
||||
if (target == null) {
|
||||
LOG.debug("Error checkContains: Cannot check for null.");
|
||||
throw new RuntimeException("Error checkContains: Cannot check for null.");
|
||||
}
|
||||
|
||||
LOG.info("checkContains: {} {}", target, type);
|
||||
|
||||
return switch (type) {
|
||||
case ResourceTypeConstants.POINT -> points.contains(target);
|
||||
case ResourceTypeConstants.PATH -> paths.contains(target);
|
||||
case ResourceTypeConstants.LOCATION -> locations.contains(target);
|
||||
default -> {
|
||||
LOG.debug("checkContains Invalid resource type.");
|
||||
throw new RuntimeException("Error checkContains: Cannot check for null.");
|
||||
// yield false; //可能不需要抛出错误,根据具体情况修改
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加资源占用
|
||||
* @param target 目标
|
||||
* @param type 类型
|
||||
*/
|
||||
public static boolean resourceAdd(String target, String type) {
|
||||
if (target == null) {
|
||||
LOG.debug("Error resourceAdd: Cannot add for null.");
|
||||
throw new RuntimeException("Error resourceAdd: Cannot add for null.");
|
||||
}
|
||||
|
||||
LOG.info("resourceAdd: {} {}", target, type);
|
||||
|
||||
return switch (type) {
|
||||
case ResourceTypeConstants.POINT -> points.add(target);
|
||||
case ResourceTypeConstants.PATH -> paths.add(target);
|
||||
case ResourceTypeConstants.LOCATION -> locations.add(target);
|
||||
default -> {
|
||||
LOG.debug("resourceAdd Invalid resource type.");
|
||||
throw new RuntimeException("resourceAdd Invalid resource type.");
|
||||
// yield false; //可能不需要抛出错误,根据具体情况修改
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放资源占用
|
||||
* @param target 目标
|
||||
* @param type 类型
|
||||
*/
|
||||
public static boolean resourceRemove(String target, String type) {
|
||||
if (target == null) {
|
||||
LOG.debug("Error resourceRemove: Cannot remove for null.");
|
||||
throw new RuntimeException("Error resourceRemove: Cannot remove for null.");
|
||||
}
|
||||
|
||||
LOG.info("resourceRemove: {} {}", target, type);
|
||||
|
||||
return switch (type) {
|
||||
case ResourceTypeConstants.POINT -> points.remove(target);
|
||||
case ResourceTypeConstants.PATH -> paths.remove(target);
|
||||
case ResourceTypeConstants.LOCATION -> locations.remove(target);
|
||||
default -> {
|
||||
LOG.debug("resourceRemove Invalid resource type.");
|
||||
throw new RuntimeException("resourceRemove Invalid resource type.");
|
||||
// yield false; //可能不需要抛出错误,根据具体情况修改
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 占用交管资源
|
||||
* @param vehiclePosition 车辆当前位置
|
||||
* @param finalOccupiedPosition 车辆最终占用资源位置
|
||||
* @param resource 资源
|
||||
* @param type 类型
|
||||
*/
|
||||
public boolean trafficControlOccupy(String vehiclePosition, String finalOccupiedPosition, String resource, String type) {
|
||||
|
||||
//交管过程判断为:
|
||||
|
||||
//获取资源占用情况
|
||||
while (true) {
|
||||
boolean lockStatus = DeclaredResource.checkContains(resource, type);
|
||||
|
||||
if (!lockStatus) {
|
||||
//设置AGV状态为执行中
|
||||
break;
|
||||
}
|
||||
|
||||
//资源已被占用
|
||||
try {
|
||||
//如果车辆到达最后站用电位,下一个点位未解锁。修改AGV状态为交管中
|
||||
if (!finalOccupiedPosition.equals("0") && vehiclePosition.equals(finalOccupiedPosition)) {
|
||||
//设置AGV状态为交管中(需要考虑,车辆初始化时,位置占用问题)
|
||||
}
|
||||
|
||||
//进入线程阻塞500ms
|
||||
Thread.sleep(500);
|
||||
} catch (Exception e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
boolean addStatus = DeclaredResource.resourceAdd(resource, type);
|
||||
if (addStatus) {
|
||||
LOG.info("trafficControlInit Successfully add resource: {} type: {}", resource, type);
|
||||
return true;
|
||||
} else {
|
||||
LOG.debug("trafficControlInit Failed to add resource: {} type: {}", resource, type);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放资源占用(使用节点:手自动切换,车辆重启,调度断开车辆)
|
||||
* @param resource 资源
|
||||
* @param type 类型
|
||||
*/
|
||||
public void trafficControlRelease(String resource, String type) {
|
||||
boolean releaseStatus = DeclaredResource.resourceRemove(resource, type);
|
||||
if (!releaseStatus) {
|
||||
LOG.debug("trafficControlRelease Failed to release resource: {} type: {}", resource, type);
|
||||
throw new RuntimeException("trafficControlRelease Failed to release resource: " + resource + " type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//测试示例
|
||||
public static void main(String[] args) {
|
||||
// 测试线程安全的动态维护
|
||||
Runnable task = () -> {
|
||||
DeclaredResource.points.add("point_" + Thread.currentThread().getName());
|
||||
DeclaredResource.paths.add("path_" + Thread.currentThread().getName());
|
||||
DeclaredResource.locations.add("Location_" + Thread.currentThread().getName());
|
||||
};
|
||||
|
||||
// 启动多个线程验证线程安全性
|
||||
Thread t1 = new Thread(task);
|
||||
Thread t2 = new Thread(task);
|
||||
t1.start();
|
||||
t2.start();
|
||||
|
||||
// 等待线程执行完毕
|
||||
try {
|
||||
t1.join();
|
||||
t2.join();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// 输出结果
|
||||
System.out.println("Points: " + DeclaredResource.points);
|
||||
System.out.println("Paths: " + DeclaredResource.paths);
|
||||
System.out.println("Locations: " + DeclaredResource.locations);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package org.opentcs.traffic.common;
|
||||
|
||||
/**
|
||||
* @author xuzhiheng
|
||||
* @date 2025-5-12
|
||||
* @description 资源类型常量
|
||||
*/
|
||||
public interface ResourceTypeConstants {
|
||||
|
||||
/**
|
||||
* 点
|
||||
*/
|
||||
String POINT = "POINT";
|
||||
|
||||
/**
|
||||
* 路线
|
||||
*/
|
||||
String PATH = "PATH";
|
||||
|
||||
/**
|
||||
* 库位
|
||||
*/
|
||||
String LOCATION = "LOCATION";
|
||||
|
||||
}
|
@ -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<VehicleAttr> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
package org.opentcs.util;
|
||||
|
||||
import org.opentcs.access.KernelServicePortal;
|
||||
import org.opentcs.access.rmi.KernelServicePortalBuilder;
|
||||
import org.opentcs.components.kernel.services.VehicleService;
|
||||
import org.opentcs.data.ObjectUnknownException;
|
||||
import org.opentcs.data.model.Vehicle;
|
||||
|
||||
/**
|
||||
* @author xuzhiheng
|
||||
*/
|
||||
public class KernelCommunication {
|
||||
|
||||
private static KernelServicePortal servicePortal;
|
||||
|
||||
public KernelCommunication(String USER, String PASSWORD, String IP, Integer PORT) {
|
||||
servicePortal = new KernelServicePortalBuilder(USER, PASSWORD).build();
|
||||
servicePortal.login(IP, PORT);
|
||||
}
|
||||
|
||||
public void logout() {
|
||||
servicePortal.logout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 通讯适配器通讯------todo 测试发送数据失败,待修改。勿用!!!!!
|
||||
* @param name 车辆名称
|
||||
* @param message 数据
|
||||
*/
|
||||
public void sendToAdapter(String name, Object message) {
|
||||
VehicleService vehicleService = servicePortal.getVehicleService();
|
||||
Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, name);
|
||||
if (vehicle == null) {
|
||||
throw new ObjectUnknownException("sendToAdapter Unknown vehicle: " + name);
|
||||
}
|
||||
|
||||
System.out.println("sendToAdapter: " + message);
|
||||
// vehicleService.sendCommAdapterMessage(vehicle.getReference(), 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());
|
||||
}
|
||||
|
||||
}
|
28
opentcs-common/src/main/resources/mqtt.conf
Normal file
28
opentcs-common/src/main/resources/mqtt.conf
Normal file
@ -0,0 +1,28 @@
|
||||
# Moquette 配置文件
|
||||
|
||||
# 监听端口
|
||||
port 1883
|
||||
# 使用WebSocket的端口(可选)
|
||||
# websocket_port 8080
|
||||
|
||||
# 主机地址
|
||||
host 0.0.0.0
|
||||
|
||||
# 持久化存储设置
|
||||
persistent_store true
|
||||
# 存储路径(如果使用持久化)
|
||||
# store_file moquette_store.mapdb
|
||||
|
||||
# SSL配置(可选)
|
||||
# ssl_port 8883
|
||||
# jks_path serverkeystore.jks
|
||||
# key_store_password passw0rd
|
||||
# key_manager_password passw0rd
|
||||
|
||||
# 允许匿名登录(true表示允许,false表示需要用户名密码)
|
||||
allow_anonymous true
|
||||
|
||||
# 如果允许匿名登录为false,则需要配置认证文件
|
||||
# password_file password_file.conf
|
||||
|
||||
# 其他配置...
|
@ -20,6 +20,10 @@ import org.opentcs.access.KernelServicePortal;
|
||||
import org.opentcs.access.rmi.KernelServicePortalBuilder;
|
||||
import org.opentcs.components.kernel.services.PlantModelService;
|
||||
import org.opentcs.components.kernel.services.VehicleService;
|
||||
import org.opentcs.data.TCSObjectReference;
|
||||
import org.opentcs.data.model.Location;
|
||||
import org.opentcs.data.model.LocationType;
|
||||
import org.opentcs.data.model.PlantModel;
|
||||
import org.opentcs.data.model.Vehicle;
|
||||
|
||||
/**
|
||||
@ -128,4 +132,20 @@ class SameThreadExecutorServiceTest {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLocation() {
|
||||
KernelServicePortal servicePortal = new KernelServicePortalBuilder(GuestUserCredentials.USER, GuestUserCredentials.PASSWORD).build();
|
||||
servicePortal.login(GuestUserCredentials.IP, GuestUserCredentials.PORT);
|
||||
PlantModel plantModel = servicePortal.getPlantModelService().getPlantModel();
|
||||
Set<Location> locations = plantModel.getLocations();
|
||||
for (Location location : locations) {
|
||||
TCSObjectReference<LocationType> type = location.getType();
|
||||
String typeName = type.getName();
|
||||
|
||||
//location类型为充电桩,记录当前location到充电桩集合中
|
||||
System.out.println("LOCATION NAME: " + location.getName() + ", TYPE NAME: " + typeName);
|
||||
}
|
||||
servicePortal.logout();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,8 +15,18 @@ dependencies {
|
||||
api group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.18.0'
|
||||
api group: 'com.fasterxml.jackson.module', name: 'jackson-module-jsonSchema', version: '2.18.0'
|
||||
api group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.18.0'
|
||||
//fastjson
|
||||
implementation 'com.alibaba:fastjson:1.2.83'
|
||||
}
|
||||
|
||||
task release {
|
||||
dependsOn build
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile){
|
||||
options.encoding="utf-8"
|
||||
}
|
||||
|
||||
tasks.withType(Javadoc){
|
||||
options.encoding="utf-8"
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Binds JSON strings to objects and vice versa.
|
||||
@ -59,9 +60,14 @@ public class JsonBinder {
|
||||
public String toJson(Object object)
|
||||
throws IllegalStateException {
|
||||
try {
|
||||
HashMap<String, Object> wrapper = new HashMap<>();
|
||||
wrapper.put("data", object);
|
||||
wrapper.put("code", 200);
|
||||
wrapper.put("message", "success");
|
||||
|
||||
return objectMapper
|
||||
.writerWithDefaultPrettyPrinter()
|
||||
.writeValueAsString(object);
|
||||
.writeValueAsString(wrapper);
|
||||
}
|
||||
catch (JsonProcessingException exc) {
|
||||
throw new IllegalStateException("Could not produce JSON output", exc);
|
||||
|
@ -4,6 +4,7 @@ package org.opentcs.kernel.extensions.servicewebapi.v1;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.inject.Inject;
|
||||
import java.time.Instant;
|
||||
@ -32,6 +33,7 @@ import org.opentcs.kernel.extensions.servicewebapi.KernelExecutorWrapper;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.GetOrderSequenceResponseTO;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.GetTransportOrderResponseTO;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostOrderSequenceRequestTO;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostTransportOrderInfoRequestTo;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostTransportOrderRequestTO;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.posttransportorder.Destination;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.Property;
|
||||
@ -59,6 +61,62 @@ public class TransportOrderHandler {
|
||||
this.executorWrapper = requireNonNull(executorWrapper, "executorWrapper");
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据WMS任务创建订单序列和运输订单
|
||||
* @param name 订单序列名称
|
||||
* @param body 结构体
|
||||
* @return 创建的订单
|
||||
*/
|
||||
public List<TransportOrder> createWmsTask(String name, String body){
|
||||
|
||||
//解析data
|
||||
String type = JSON.parseObject(body).getString("type");
|
||||
String intendedVehicle = JSON.parseObject(body).getString("intendedVehicle") != null ? JSON.parseObject(body).getString("intendedVehicle") : null;
|
||||
Boolean dispensable = JSON.parseObject(body).getBoolean("dispensable") != null ? JSON.parseObject(body).getBoolean("dispensable") : false;
|
||||
// String wrappingSequence = JSON.parseObject(body).getString("wrappingSequence") != null ? JSON.parseObject(body).getString("wrappingSequence") : null;
|
||||
String destinationsStr = JSON.parseObject(body).getString("destinations");
|
||||
List<PostTransportOrderInfoRequestTo> dataList = JSON.parseArray(destinationsStr, PostTransportOrderInfoRequestTo.class);
|
||||
|
||||
//构建订单序列
|
||||
PostOrderSequenceRequestTO postOrderSequenceRequestTO = new PostOrderSequenceRequestTO();
|
||||
postOrderSequenceRequestTO.setIncompleteName(false);
|
||||
if (intendedVehicle != null) {
|
||||
postOrderSequenceRequestTO.setIntendedVehicle(intendedVehicle);
|
||||
}
|
||||
postOrderSequenceRequestTO.setType(type);
|
||||
postOrderSequenceRequestTO.setFailureFatal(true);
|
||||
//根据传入名称创建订单序列
|
||||
OrderSequence orderSequence = this.createOrderSequence(name, postOrderSequenceRequestTO);
|
||||
|
||||
//构建订单对象
|
||||
PostTransportOrderRequestTO postTransportOrderRequestTO = new PostTransportOrderRequestTO();
|
||||
postTransportOrderRequestTO.setIncompleteName(true);
|
||||
if (intendedVehicle != null) {
|
||||
postTransportOrderRequestTO.setIntendedVehicle(intendedVehicle);
|
||||
}
|
||||
postTransportOrderRequestTO.setDispensable(dispensable);
|
||||
postTransportOrderRequestTO.setDeadline(Instant.now());
|
||||
postTransportOrderRequestTO.setWrappingSequence(orderSequence.getName());
|
||||
postTransportOrderRequestTO.setType(type);
|
||||
//根据传入点位创建多个订单
|
||||
List<TransportOrder> orders = new ArrayList<>();
|
||||
for (PostTransportOrderInfoRequestTo destination : dataList) {
|
||||
List<Destination> destinationsList = new ArrayList<>();
|
||||
Destination newDestination = new Destination();
|
||||
newDestination.setLocationName(destination.getLocationName());
|
||||
newDestination.setOperation(destination.getOperation());
|
||||
destinationsList.add(newDestination);
|
||||
postTransportOrderRequestTO.setDestinations(destinationsList);
|
||||
TransportOrder order = this.createOrder("", postTransportOrderRequestTO);
|
||||
orders.add(order);
|
||||
}
|
||||
|
||||
//设置订单序列完整性,自动完成订单
|
||||
this.putOrderSequenceComplete(name);
|
||||
|
||||
return orders;
|
||||
}
|
||||
|
||||
public TransportOrder createOrder(String name, PostTransportOrderRequestTO order)
|
||||
throws ObjectUnknownException,
|
||||
ObjectExistsException,
|
||||
|
@ -8,6 +8,8 @@ import jakarta.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import org.opentcs.access.KernelRuntimeException;
|
||||
import org.opentcs.communication.http.service.ExecuteMove;
|
||||
import org.opentcs.communication.http.service.ExecuteOperation;
|
||||
import org.opentcs.data.ObjectExistsException;
|
||||
import org.opentcs.data.ObjectUnknownException;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.HttpConstants;
|
||||
@ -16,13 +18,11 @@ import org.opentcs.kernel.extensions.servicewebapi.RequestHandler;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.GetOrderSequenceResponseTO;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.GetPeripheralAttachmentInfoResponseTO;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.GetPeripheralJobResponseTO;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.GetTransportOrderResponseTO;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.GetVehicleAttachmentInfoResponseTO;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PlantModelTO;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostOrderSequenceRequestTO;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostPeripheralJobRequestTO;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostTopologyUpdateRequestTO;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostTransportOrderRequestTO;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostVehicleRoutesRequestTO;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostVehicleRoutesResponseTO;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PutVehicleAllowedOrderTypesTO;
|
||||
@ -279,6 +279,21 @@ public class V1RequestHandler
|
||||
"/peripheralJobs/dispatcher/trigger",
|
||||
this::handlePostPeripheralJobsDispatchTrigger
|
||||
);
|
||||
//新建接口,接收平台异步响应
|
||||
service.post(
|
||||
"/vehicles/receiveCallback",
|
||||
this::handlePostReceiveCallback
|
||||
);
|
||||
}
|
||||
|
||||
private Object handlePostReceiveCallback(Request request, Response response)
|
||||
throws KernelRuntimeException {
|
||||
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
|
||||
vehicleHandler.postReceiveCallback(
|
||||
request.body()
|
||||
);
|
||||
return "";
|
||||
// return jsonBinder.toJson("");
|
||||
}
|
||||
|
||||
private Object handlePostDispatcherTrigger(Request request, Response response)
|
||||
@ -356,14 +371,13 @@ public class V1RequestHandler
|
||||
IllegalArgumentException,
|
||||
IllegalStateException {
|
||||
response.type(HttpConstants.CONTENT_TYPE_APPLICATION_JSON_UTF8);
|
||||
|
||||
//构建响应json
|
||||
return jsonBinder.toJson(
|
||||
GetTransportOrderResponseTO.fromTransportOrder(
|
||||
transportOrderHandler.createOrder(
|
||||
request.params(":NAME"),
|
||||
jsonBinder.fromJson(request.body(), PostTransportOrderRequestTO.class)
|
||||
)
|
||||
)
|
||||
);
|
||||
transportOrderHandler.createWmsTask(
|
||||
request.params(":NAME"),
|
||||
request.body()
|
||||
));
|
||||
}
|
||||
|
||||
private Object handlePutTransportOrderIntendedVehicle(Request request, Response response)
|
||||
@ -415,7 +429,7 @@ public class V1RequestHandler
|
||||
ExecutionException {
|
||||
transportOrderHandler.putOrderSequenceComplete(request.params(":NAME"));
|
||||
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
|
||||
return "";
|
||||
return jsonBinder.toJson("");
|
||||
}
|
||||
|
||||
private Object handlePostImmediateAssignment(Request request, Response response)
|
||||
@ -438,6 +452,11 @@ public class V1RequestHandler
|
||||
|
||||
private Object handlePostWithdrawalByVehicle(Request request, Response response)
|
||||
throws ObjectUnknownException {
|
||||
|
||||
//初始化动作状态
|
||||
vehicleHandler.initActionStatus(request.params(":NAME"));
|
||||
|
||||
//根据车辆撤销任务
|
||||
orderDispatcherHandler.withdrawByVehicle(
|
||||
request.params(":NAME"),
|
||||
immediate(request),
|
||||
@ -456,6 +475,16 @@ public class V1RequestHandler
|
||||
|
||||
private Object handlePostVehicleRerouteRequest(Request request, Response response)
|
||||
throws ObjectUnknownException {
|
||||
//清理车辆映射订单ID
|
||||
ExecuteMove.resetOrder(request.params(":NAME"));
|
||||
|
||||
//可能会有动作阻塞,所以需要初始化动作状态
|
||||
vehicleHandler.initActionStatus(request.params(":NAME"));
|
||||
|
||||
//撤销订单 todo 订单撤销后可能会有延时继续运动,所以应该考虑是否需要等待订单撤销成功
|
||||
// ExecuteOperation.cancelOrder(request.params(":NAME"));
|
||||
|
||||
//立即重新路由
|
||||
orderDispatcherHandler.reroute(request.params(":NAME"), forced(request));
|
||||
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
|
||||
return "";
|
||||
|
@ -4,6 +4,8 @@ package org.opentcs.kernel.extensions.servicewebapi.v1;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Comparator;
|
||||
@ -26,11 +28,15 @@ import org.opentcs.data.model.Vehicle.EnergyLevelThresholdSet;
|
||||
import org.opentcs.data.order.Route;
|
||||
import org.opentcs.drivers.vehicle.VehicleCommAdapterDescription;
|
||||
import org.opentcs.drivers.vehicle.management.VehicleAttachmentInformation;
|
||||
import org.opentcs.drivers.vehicle.management.VehicleProcessModelTO;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.KernelExecutorWrapper;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.GetVehicleResponseTO;
|
||||
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostVehicleRoutesRequestTO;
|
||||
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.AgvActionStatus;
|
||||
import org.opentcs.manage.entity.AgvInfo;
|
||||
|
||||
/**
|
||||
* Handles requests related to vehicles.
|
||||
@ -59,6 +65,70 @@ public class VehicleHandler {
|
||||
this.executorWrapper = requireNonNull(executorWrapper, "executorWrapper");
|
||||
}
|
||||
|
||||
/**
|
||||
* 接收平台异步回调处理
|
||||
*/
|
||||
public void postReceiveCallback(Object data) {
|
||||
// System.out.println("jsonObject-----ssss: " + data.toString());
|
||||
|
||||
//截取平台响应的字符串
|
||||
String jsonStr;
|
||||
try {
|
||||
jsonStr = data.toString().split("=", 2)[1];
|
||||
} catch (Exception e) {
|
||||
jsonStr = data.toString();
|
||||
}
|
||||
JSONObject jsonObject = JSON.parseObject(jsonStr);
|
||||
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);
|
||||
}
|
||||
|
||||
VehicleProcessModelTO vehicleProcessModelTO = vehicleService.fetchProcessModel(vehicle.getReference());
|
||||
if (!vehicleProcessModelTO.isCommAdapterEnabled()) {
|
||||
//未开启通讯适配器,手动选择通讯适配器开启
|
||||
String adapterName = "org.opentcs.virtualvehicle.LoopbackCommunicationAdapterDescription";
|
||||
VehicleCommAdapterDescription newAdapter
|
||||
= vehicleService.fetchAttachmentInformation(vehicle.getReference())
|
||||
.getAvailableCommAdapters()
|
||||
.stream()
|
||||
.filter(description -> description.getClass().getName().equals(adapterName))
|
||||
.findAny()
|
||||
.orElseThrow(
|
||||
() -> new IllegalArgumentException("Unknown vehicle driver class name: " + adapterName)
|
||||
);
|
||||
vehicleService.attachCommAdapter(vehicle.getReference(), newAdapter);
|
||||
}
|
||||
|
||||
//将数据更新到线程安全的集合中,防止线程阻塞
|
||||
AdapterManage.setAdapterStatus(name);
|
||||
if (type == 1) { //上报agv详细信息
|
||||
AgvInfo agvInfo = AdapterManage.setAdapterVehicleModel(name, jsonStr);
|
||||
vehicleService.sendCommAdapterMessage(vehicle.getReference(), agvInfo);
|
||||
} else if (type == 5) { //上报动作完成
|
||||
//动作完成上报
|
||||
initActionStatus(name);
|
||||
// AgvActionStatus agvActionStatus = new AgvActionStatus();
|
||||
// agvActionStatus.setStatus(true);
|
||||
// vehicleService.sendCommAdapterMessage(vehicle.getReference(), agvActionStatus);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化动作完成状态
|
||||
* @param name 车辆名称
|
||||
*/
|
||||
public void initActionStatus(String name) {
|
||||
Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, name);
|
||||
AgvActionStatus agvActionStatus = new AgvActionStatus();
|
||||
agvActionStatus.setStatus(true);
|
||||
vehicleService.sendCommAdapterMessage(vehicle.getReference(), agvActionStatus);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find all vehicles orders and filters depending on the given parameters.
|
||||
*
|
||||
|
@ -0,0 +1,43 @@
|
||||
package org.opentcs.kernel.extensions.servicewebapi.v1.binding;
|
||||
|
||||
public class PostTransportOrderInfoRequestTo {
|
||||
|
||||
private String locationName;
|
||||
|
||||
private String point;
|
||||
|
||||
private String operation;
|
||||
|
||||
public String getLocationName() {
|
||||
return locationName;
|
||||
}
|
||||
|
||||
public void setLocationName(String locationName) {
|
||||
this.locationName = locationName;
|
||||
}
|
||||
|
||||
public String getPoint() {
|
||||
return point;
|
||||
}
|
||||
|
||||
public void setPoint(String point) {
|
||||
this.point = point;
|
||||
}
|
||||
|
||||
public String getOperation() {
|
||||
return operation;
|
||||
}
|
||||
|
||||
public void setOperation(String operation) {
|
||||
this.operation = operation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PostTransportOrderInfoRequestTo{" +
|
||||
"locationName='" + locationName + '\'' +
|
||||
", point='" + point + '\'' +
|
||||
", operation='" + operation + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -344,4 +344,12 @@ public class StandardRemoteVehicleService
|
||||
throw findSuitableExceptionFor(exc);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveCallback(ClientID clientId, TCSObjectReference<Vehicle> ref, Object data)
|
||||
throws RemoteException {
|
||||
System.out.println("receiveCallback org.opentcs.kernel.extensions.rmi:" + data);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ dependencies {
|
||||
implementation group: 'de.huxhorn.sulky', name: 'de.huxhorn.sulky.ulid', version: '8.3.0'
|
||||
|
||||
runtimeOnly group: 'org.slf4j', name: 'slf4j-jdk14', version: '2.0.16'
|
||||
//fastjson
|
||||
implementation 'com.alibaba:fastjson:1.2.83'
|
||||
}
|
||||
|
||||
distributions {
|
||||
@ -47,6 +49,14 @@ task release {
|
||||
dependsOn installDist
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile){
|
||||
options.encoding="utf-8"
|
||||
}
|
||||
|
||||
tasks.withType(Javadoc){
|
||||
options.encoding="utf-8"
|
||||
}
|
||||
|
||||
run {
|
||||
systemProperties(['java.util.logging.config.file':'./config/logging.config',\
|
||||
'opentcs.base':'.',\
|
||||
|
@ -14,7 +14,6 @@ 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.components.kernel.KernelExtension;
|
||||
@ -22,6 +21,7 @@ import org.opentcs.components.kernel.services.NotificationService;
|
||||
import org.opentcs.customizations.ApplicationEventBus;
|
||||
import org.opentcs.customizations.kernel.KernelExecutor;
|
||||
import org.opentcs.data.notification.UserNotification;
|
||||
import org.opentcs.manage.AdapterManage;
|
||||
import org.opentcs.util.event.EventBus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -70,6 +70,10 @@ public class StandardKernel
|
||||
* The kernel implementing the actual functionality for the current mode.
|
||||
*/
|
||||
private KernelState kernelState;
|
||||
/**
|
||||
* 通讯适配器管理类
|
||||
*/
|
||||
private final AdapterManage adapterManage = new AdapterManage();
|
||||
|
||||
/**
|
||||
* Creates a new kernel.
|
||||
@ -108,6 +112,13 @@ public class StandardKernel
|
||||
// Initial state is modelling.
|
||||
setState(State.MODELLING);
|
||||
|
||||
//开启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");
|
||||
@ -127,6 +138,13 @@ public class StandardKernel
|
||||
// Note that the actual shutdown of extensions should happen when the kernel
|
||||
// thread (see run()) finishes, not here.
|
||||
// Set the terminated flag and wake up this kernel's thread for termination.
|
||||
|
||||
//关闭MQTT服务
|
||||
// mqttBrokerServer.stopMqtt();
|
||||
|
||||
//关闭通讯适配器自动管理
|
||||
adapterManage.STOP();
|
||||
|
||||
initialized = false;
|
||||
terminationSemaphore.release();
|
||||
}
|
||||
|
@ -1032,6 +1032,14 @@ public class DefaultVehicleController
|
||||
Point currentVehiclePosition = originalCommand.getStep().getDestinationPoint();
|
||||
Deque<Set<TCSResource<?>>> allocatedResources
|
||||
= commandProcessingTracker.getAllocatedResources();
|
||||
|
||||
// KernelApplicationConfiguration.VehicleResourceManagementType vehicleResourceManagementType
|
||||
// = configuration.vehicleResourceManagementType();
|
||||
// System.out.println("vehicleResourceManagementType: " + vehicleResourceManagementType);
|
||||
//
|
||||
// long length = commAdapter.getProcessModel().getBoundingBox().getLength();
|
||||
// System.out.println("vehicle_length: " + length);
|
||||
|
||||
switch (configuration.vehicleResourceManagementType()) {
|
||||
case LENGTH_IGNORED:
|
||||
while (!allocatedResources.peek().contains(currentVehiclePosition)) {
|
||||
@ -1044,11 +1052,13 @@ public class DefaultVehicleController
|
||||
case LENGTH_RESPECTED:
|
||||
// Free resources allocated for executed commands, but keep as many as needed for the
|
||||
// vehicle's current length.
|
||||
long vehicleLeghtRadius = 3700 / 2; //车辆长度半径
|
||||
BoundingBox boundingBox = commAdapter.getProcessModel().getBoundingBox().withLength(vehicleLeghtRadius); //长度有问题
|
||||
int freeableResourceSetCount
|
||||
= ResourceMath.freeableResourceSetCount(
|
||||
SplitResources.from(allocatedResources, Set.of(currentVehiclePosition))
|
||||
.getResourcesPassed(),
|
||||
commAdapter.getProcessModel().getBoundingBox().getLength()
|
||||
boundingBox.getLength()
|
||||
);
|
||||
for (int i = 0; i < freeableResourceSetCount; i++) {
|
||||
Set<TCSResource<?>> oldResources = allocatedResources.poll();
|
||||
|
@ -51,6 +51,9 @@ import org.opentcs.data.order.TransportOrder;
|
||||
import org.opentcs.data.peripherals.PeripheralJob;
|
||||
import org.opentcs.data.peripherals.PeripheralOperation;
|
||||
import org.opentcs.drivers.vehicle.LoadHandlingDevice;
|
||||
import org.opentcs.manage.AdapterManage;
|
||||
import org.opentcs.park.ParkStrategy;
|
||||
import org.opentcs.park.entity.Park;
|
||||
import org.opentcs.util.event.EventHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -169,6 +172,8 @@ public class PlantModelManager
|
||||
ObjectUnknownException {
|
||||
LOG.info("Plant model is being created: {}", to.getName());
|
||||
|
||||
// AdapterManage.kernelStatus = true;
|
||||
|
||||
clear();
|
||||
setName(to.getName());
|
||||
setProperties(to.getProperties());
|
||||
@ -194,6 +199,9 @@ public class PlantModelManager
|
||||
}
|
||||
|
||||
createVisualLayout(to.getVisualLayout());
|
||||
|
||||
// AdapterManage.kernelStatus = true;
|
||||
LOG.info("Plant model created successfully: {}", to.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1500,6 +1508,7 @@ public class PlantModelManager
|
||||
throws ObjectUnknownException,
|
||||
ObjectExistsException {
|
||||
LocationType type = getObjectRepo().getObject(LocationType.class, to.getTypeName());
|
||||
// System.out.println("test print location:" + to.getName() + ", type:" + type.getName());
|
||||
Location newLocation = new Location(to.getName(), type.getReference())
|
||||
.withPosition(to.getPosition())
|
||||
.withLocked(to.isLocked())
|
||||
|
@ -451,6 +451,7 @@ public class TransportOrderPoolManager
|
||||
throws ObjectUnknownException {
|
||||
TransportOrder order = getObjectRepo().getObject(TransportOrder.class, ref);
|
||||
// Make sure only orders in a final state are removed.
|
||||
LOG.info("removeTransportOrder name: {}, state: {}", order.getName(), order.getState());
|
||||
checkArgument(
|
||||
order.getState().isFinalState(),
|
||||
"Transport order %s is not in a final state.",
|
||||
|
@ -14,7 +14,8 @@ kernelapp.rerouteOnDriveOrderFinished = false
|
||||
kernelapp.vehicleResourceManagementType = LENGTH_RESPECTED
|
||||
|
||||
orderpool.sweepInterval = 60000
|
||||
orderpool.sweepAge = 86400000
|
||||
orderpool.sweepAge = 60000
|
||||
#orderpool.sweepAge = 86400000
|
||||
|
||||
rmikernelinterface.enable = true
|
||||
rmikernelinterface.useSsl = false
|
||||
@ -45,7 +46,7 @@ servicewebapi.enable = true
|
||||
servicewebapi.useSsl = false
|
||||
servicewebapi.bindAddress = 0.0.0.0
|
||||
servicewebapi.bindPort = 55200
|
||||
servicewebapi.accessKey =
|
||||
servicewebapi.accessKey =
|
||||
servicewebapi.statusEventsCapacity = 1000
|
||||
|
||||
defaultdispatcher.dismissUnroutableTransportOrders = true
|
||||
@ -79,6 +80,8 @@ virtualvehicle.rechargePercentagePerSecond = 1.0
|
||||
virtualvehicle.simulationTimeFactor = 1.0
|
||||
virtualvehicle.vehicleLengthLoaded = 1000
|
||||
virtualvehicle.vehicleLengthUnloaded = 1000
|
||||
virtualvehicle.chargingStrategyName = threshold_charging_strategy
|
||||
virtualvehicle.energyDrainRatePerSecond = 10.0
|
||||
|
||||
virtualperipheral.enable = true
|
||||
|
||||
|
@ -42,7 +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);
|
||||
keySuggestions.add(LoopbackAdapterConstants.AGV_DRIVE_POSE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -10,6 +10,7 @@ import jakarta.inject.Inject;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.opentcs.communication.http.service.ExecuteOperation;
|
||||
import org.opentcs.components.Lifecycle;
|
||||
import org.opentcs.components.kernel.Router;
|
||||
import org.opentcs.components.kernel.services.InternalTransportOrderService;
|
||||
@ -355,6 +356,13 @@ public class TransportOrderUtil
|
||||
// vehicle reports the remaining movements as finished.
|
||||
updateTransportOrderState(order.getReference(), TransportOrder.State.WITHDRAWN);
|
||||
|
||||
//撤销订单,获取订单状态
|
||||
TransportOrder.State state = order.getState();
|
||||
if (state == TransportOrder.State.BEING_PROCESSED) {
|
||||
//订单执行中,需要撤销下发给控制器的任务
|
||||
ExecuteOperation.cancelOrder(vehicle.getName());
|
||||
}
|
||||
|
||||
VehicleController vehicleController
|
||||
= vehicleControllerPool.getVehicleController(vehicle.getName());
|
||||
|
||||
|
@ -7,11 +7,14 @@ import static java.util.Objects.requireNonNull;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import org.opentcs.common.GuestUserCredentials;
|
||||
import org.opentcs.communication.http.HttpClient;
|
||||
import org.opentcs.components.kernel.Router;
|
||||
import org.opentcs.components.kernel.services.TCSObjectService;
|
||||
import org.opentcs.data.model.Point;
|
||||
@ -133,12 +136,12 @@ public class OrderAssigner {
|
||||
);
|
||||
|
||||
AssignmentState assignmentState = new AssignmentState();
|
||||
if (availableVehicles.size() < availableOrders.size()) {
|
||||
if (availableVehicles.size() < availableOrders.size()) { //车数量 < 订单数
|
||||
availableVehicles.stream()
|
||||
.sorted(vehicleComparator)
|
||||
.forEach(vehicle -> tryAssignOrder(vehicle, availableOrders, assignmentState));
|
||||
}
|
||||
else {
|
||||
else { //车数量 >= 订单数
|
||||
availableOrders.stream()
|
||||
.sorted(orderComparator)
|
||||
.forEach(order -> tryAssignVehicle(order, availableVehicles, assignmentState));
|
||||
@ -194,6 +197,22 @@ public class OrderAssigner {
|
||||
.sorted(orderCandidateComparator)
|
||||
.findFirst()
|
||||
.ifPresent(candidate -> assignOrder(candidate, assignmentState));
|
||||
|
||||
AssignmentCandidate assignmentCandidate = ordersSplitByFilter.get(Boolean.TRUE).stream()
|
||||
.map(CandidateFilterResult::getCandidate)
|
||||
.sorted(orderCandidateComparator)
|
||||
.findFirst().orElse(null);
|
||||
|
||||
if (assignmentCandidate != null
|
||||
&& assignmentCandidate.getTransportOrder().getWrappingSequence() != null) {
|
||||
//已分配车辆,调用接口告诉wms系统
|
||||
HashMap<String, String> dataMap = new HashMap<>();
|
||||
dataMap.put("task_code", assignmentCandidate.getTransportOrder().getWrappingSequence().getName());
|
||||
dataMap.put("vehicle_name", vehicle.getName());
|
||||
|
||||
HttpClient httpClient = new HttpClient();
|
||||
httpClient.sendCommand(GuestUserCredentials.WMS_URL, dataMap);
|
||||
}
|
||||
}
|
||||
|
||||
private void tryAssignVehicle(
|
||||
@ -227,15 +246,44 @@ public class OrderAssigner {
|
||||
)
|
||||
.collect(Collectors.partitioningBy(filterResult -> !filterResult.isFiltered()));
|
||||
|
||||
// // 调试输出:分组统计结果
|
||||
// System.out.println("Candidate groups: Passed=" + ordersSplitByFilter.get(Boolean.TRUE).size() + ", Filtered=" + ordersSplitByFilter.get(Boolean.FALSE).size());
|
||||
//
|
||||
// // 调试输出:显示所有通过过滤的候选(排序前)
|
||||
// List<AssignmentCandidate> passedCandidates = ordersSplitByFilter.get(Boolean.TRUE).stream()
|
||||
// .map(CandidateFilterResult::getCandidate)
|
||||
// .collect(Collectors.toList());
|
||||
//
|
||||
// passedCandidates.forEach(c -> System.out.println("passedCandidates - order: " + c.getTransportOrder() + ",vehicle:" + c.getVehicle() + ",Costs: " + c.getCompleteRoutingCosts()));
|
||||
|
||||
ordersSplitByFilter.get(Boolean.FALSE).stream()
|
||||
.map(CandidateFilterResult::toFilterResult)
|
||||
.forEach(filterResult -> assignmentState.addFilteredOrder(filterResult));
|
||||
|
||||
|
||||
ordersSplitByFilter.get(Boolean.TRUE).stream()
|
||||
.map(CandidateFilterResult::getCandidate)
|
||||
.sorted(vehicleCandidateComparator)
|
||||
// .sorted(vehicleCandidateComparator) 框架原本排序有问题,修改为按照路由成本排序
|
||||
.sorted(Comparator.comparingLong(AssignmentCandidate::getCompleteRoutingCosts))
|
||||
.findFirst()
|
||||
.ifPresent(candidate -> assignOrder(candidate, assignmentState));
|
||||
|
||||
AssignmentCandidate assignmentCandidate = ordersSplitByFilter.get(Boolean.TRUE).stream()
|
||||
.map(CandidateFilterResult::getCandidate)
|
||||
.sorted(Comparator.comparingLong(AssignmentCandidate::getCompleteRoutingCosts))
|
||||
.findFirst().orElse(null);
|
||||
|
||||
if (assignmentCandidate != null
|
||||
&& assignmentCandidate.getVehicle() != null
|
||||
&& order.getWrappingSequence() != null) {
|
||||
//已分配车辆,调用接口告诉wms系统
|
||||
HashMap<String, String> dataMap = new HashMap<>();
|
||||
dataMap.put("task_code", order.getWrappingSequence().getName());
|
||||
dataMap.put("vehicle_name", assignmentCandidate.getVehicle().getName());
|
||||
|
||||
HttpClient httpClient = new HttpClient();
|
||||
httpClient.sendCommand(GuestUserCredentials.WMS_URL, dataMap);
|
||||
}
|
||||
}
|
||||
|
||||
private void assignOrder(AssignmentCandidate candidate, AssignmentState assignmentState) {
|
||||
|
Loading…
Reference in New Issue
Block a user