77import org .opentcs .driver .api .dto .VehicleStatus ;
88import org .opentcs .driver .registry .DriverRegistry ;
99import org .opentcs .kernel .api .OrderLifecycleApi ;
10- import org .opentcs .kernel .api .dto .OrderStateDTO ;
1110import org .opentcs .kernel .api .dto .PositionDTO ;
1211import org .opentcs .kernel .api .dto .TransportOrderDTO ;
1312import org .opentcs .kernel .api .dto .VehicleDTO ;
1413import org .opentcs .vehicle .application .bo .VehicleBO ;
1514import org .opentcs .vehicle .application .bo .VehicleCrudBO ;
15+ import org .opentcs .vehicle .application .bo .OpsActionResultBO ;
1616import org .opentcs .kernel .application .TransportOrderRegistry ;
1717import org .opentcs .kernel .application .VehicleRegistry ;
1818import org .opentcs .kernel .domain .order .TransportOrder ;
1919import org .opentcs .kernel .domain .vehicle .Vehicle ;
2020import org .opentcs .kernel .domain .vehicle .VehiclePosition ;
2121import org .opentcs .kernel .domain .vehicle .VehicleState ;
22+ import org .opentcs .vehicle .controller .req .GoChargeRequest ;
23+ import org .opentcs .vehicle .controller .req .MapSwitchRequest ;
24+ import org .opentcs .vehicle .controller .req .ModeSwitchRequest ;
25+ import org .opentcs .vehicle .controller .req .MoveRequest ;
2226import org .opentcs .vehicle .persistence .entity .VehicleEntity ;
2327import org .opentcs .vehicle .persistence .service .VehicleRepository ;
2428
2832import org .springframework .transaction .annotation .Transactional ;
2933
3034import jakarta .annotation .PostConstruct ;
35+ import java .time .LocalDateTime ;
3136import java .util .ArrayList ;
3237import java .util .HashMap ;
3338import java .util .List ;
3439import java .util .Map ;
40+ import java .util .UUID ;
41+ import java .util .concurrent .CopyOnWriteArrayList ;
3542import java .util .stream .Collectors ;
3643
3744/**
@@ -48,6 +55,7 @@ public class VehicleApplicationService {
4855 private final TransportOrderRegistry orderRegistry ;
4956 private final OrderLifecycleApi orderLifecycleApi ;
5057 private final DriverRegistry driverRegistry ;
58+ private final List <Map <String , Object >> opsActionRecords = new CopyOnWriteArrayList <>();
5159
5260 /**
5361 * 启动时向驱动层注册状态监听器,自动检测订单完成事件。
@@ -242,6 +250,121 @@ public void sendInstantAction(String vehicleId, InstantAction action) {
242250 log .info ("即时动作已发送到车辆 {}: {}" , vehicleId , action .getActionType ());
243251 }
244252
253+ public OpsActionResultBO switchMode (String vehicleName , ModeSwitchRequest request ) {
254+ String targetMode = request .getTargetMode () == null ? "AUTOMATIC" : request .getTargetMode ().toUpperCase ();
255+ String actionType = "AUTOMATIC" .equals (targetMode ) ? "RESUME" : "PAUSE" ;
256+
257+ Map <String , String > params = new HashMap <>();
258+ params .put ("targetMode" , targetMode );
259+ params .put ("executePolicy" , nvl (request .getExecutePolicy (), "REJECT_IF_BUSY" ));
260+ params .put ("reason" , nvl (request .getReason (), "" ));
261+ return executeOpsAction (vehicleName , "MODE_SWITCH" , actionType , params );
262+ }
263+
264+ public OpsActionResultBO switchMap (String vehicleName , MapSwitchRequest request ) {
265+ Map <String , String > params = new HashMap <>();
266+ params .put ("targetMapId" , nvl (request .getTargetMapId (), "" ));
267+ params .put ("targetMapVersion" , nvl (request .getTargetMapVersion (), "" ));
268+ params .put ("initPosition" , nvl (request .getInitPosition (), "" ));
269+ params .put ("fallbackMapId" , nvl (request .getFallbackMapId (), "" ));
270+ return executeOpsAction (vehicleName , "MAP_SWITCH" , "enableMap" , params );
271+ }
272+
273+ public OpsActionResultBO goCharge (String vehicleName , GoChargeRequest request ) {
274+ Map <String , String > params = new HashMap <>();
275+ params .put ("chargePolicy" , nvl (request .getChargePolicy (), "NEAREST" ));
276+ params .put ("stationId" , nvl (request .getStationId (), "" ));
277+ params .put ("interruptPolicy" , nvl (request .getInterruptPolicy (), "WAIT_CURRENT_TASK" ));
278+ if (request .getMinSocThreshold () != null ) {
279+ params .put ("minSocThreshold" , String .valueOf (request .getMinSocThreshold ()));
280+ }
281+ return executeOpsAction (vehicleName , "GO_CHARGE" , "CHARGE" , params );
282+ }
283+
284+ public OpsActionResultBO moveVehicle (String vehicleName , MoveRequest request ) {
285+ String moveType = nvl (request .getMoveType (), "MOVE_TO_NODE" );
286+ Map <String , String > params = new HashMap <>();
287+ params .put ("moveType" , moveType );
288+ params .put ("targetNodeId" , nvl (request .getTargetNodeId (), "" ));
289+ params .put ("mapId" , nvl (request .getMapId (), "" ));
290+ params .put ("confirmRisk" , String .valueOf (Boolean .TRUE .equals (request .getConfirmRisk ())));
291+ if (request .getX () != null ) {
292+ params .put ("x" , String .valueOf (request .getX ()));
293+ }
294+ if (request .getY () != null ) {
295+ params .put ("y" , String .valueOf (request .getY ()));
296+ }
297+ if (request .getTheta () != null ) {
298+ params .put ("theta" , String .valueOf (request .getTheta ()));
299+ }
300+
301+ String actionType = "INIT_POSITION" .equalsIgnoreCase (moveType ) ? "initPosition" : "MOVE" ;
302+ return executeOpsAction (vehicleName , "MOVE" , actionType , params );
303+ }
304+
305+ public Map <String , Object > precheck (String vehicleName , String actionType ) {
306+ Map <String , Object > result = new HashMap <>();
307+ boolean online = vehicleRegistry .isOnline (vehicleName );
308+ Vehicle vehicle = vehicleRegistry .getVehicleDomain (vehicleName );
309+
310+ result .put ("vehicleName" , vehicleName );
311+ result .put ("actionType" , actionType );
312+ result .put ("online" , online );
313+ result .put ("state" , vehicle == null ? "UNKNOWN" : vehicle .getState ().name ());
314+ result .put ("allow" , online );
315+ result .put ("reasonCode" , online ? null : "OPS_AMR_001" );
316+ result .put ("reasonMessage" , online ? null : "车辆离线,禁止执行运维动作" );
317+ return result ;
318+ }
319+
320+ public List <Map <String , Object >> listOpsActionRecords (String vehicleName ) {
321+ if (vehicleName == null || vehicleName .isBlank ()) {
322+ return new ArrayList <>(opsActionRecords );
323+ }
324+ return opsActionRecords .stream ()
325+ .filter (record -> vehicleName .equals (record .get ("vehicleName" )))
326+ .collect (Collectors .toList ());
327+ }
328+
329+ private OpsActionResultBO executeOpsAction (String vehicleName ,
330+ String actionCategory ,
331+ String actionType ,
332+ Map <String , String > parameters ) {
333+ if (!vehicleRegistry .isOnline (vehicleName )) {
334+ throw new RuntimeException ("车辆离线: " + vehicleName );
335+ }
336+
337+ String actionId = "OPS-" + System .currentTimeMillis ();
338+ String traceId = UUID .randomUUID ().toString ().replace ("-" , "" );
339+
340+ InstantAction action = new InstantAction (actionId , actionType );
341+ action .setParameters (parameters );
342+ driverRegistry .sendInstantAction (vehicleName , action );
343+
344+ Map <String , Object > record = new HashMap <>();
345+ record .put ("actionId" , actionId );
346+ record .put ("traceId" , traceId );
347+ record .put ("vehicleName" , vehicleName );
348+ record .put ("actionCategory" , actionCategory );
349+ record .put ("actionType" , actionType );
350+ record .put ("requestPayload" , parameters );
351+ record .put ("executeStatus" , "ACCEPTED" );
352+ record .put ("operatedAt" , LocalDateTime .now ().toString ());
353+ opsActionRecords .add (0 , record );
354+
355+ OpsActionResultBO result = new OpsActionResultBO ();
356+ result .setActionId (actionId );
357+ result .setAccepted (true );
358+ result .setStatus ("PENDING" );
359+ result .setTraceId (traceId );
360+ result .setEstimatedFinishTime (LocalDateTime .now ().plusMinutes (1 ).toString ());
361+ return result ;
362+ }
363+
364+ private String nvl (String value , String defaultValue ) {
365+ return value == null || value .isBlank () ? defaultValue : value ;
366+ }
367+
245368 /**
246369 * 处理车辆状态变更(从驱动层回调)
247370 */
0 commit comments