All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opentcs.kernel.workingset.TransportOrderPoolManager Maven / Gradle / Ivy

/**
 * Copyright (c) The openTCS Authors.
 *
 * This program is free software and subject to the MIT license. (For details,
 * see the licensing information (LICENSE.txt) you should have received with
 * this copy of the software.)
 */
package org.opentcs.kernel.workingset;

import static java.util.Objects.requireNonNull;
import static org.opentcs.util.Assertions.checkArgument;

import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import jakarta.inject.Inject;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import org.opentcs.access.to.order.DestinationCreationTO;
import org.opentcs.access.to.order.OrderSequenceCreationTO;
import org.opentcs.access.to.order.TransportOrderCreationTO;
import org.opentcs.components.kernel.ObjectNameProvider;
import org.opentcs.customizations.ApplicationEventBus;
import org.opentcs.data.ObjectExistsException;
import org.opentcs.data.ObjectUnknownException;
import org.opentcs.data.TCSObject;
import org.opentcs.data.TCSObjectEvent;
import org.opentcs.data.TCSObjectReference;
import org.opentcs.data.model.Location;
import org.opentcs.data.model.Location.Link;
import org.opentcs.data.model.LocationType;
import org.opentcs.data.model.Point;
import org.opentcs.data.model.Vehicle;
import org.opentcs.data.order.DriveOrder;
import org.opentcs.data.order.DriveOrder.Destination;
import org.opentcs.data.order.OrderSequence;
import org.opentcs.data.order.TransportOrder;
import org.opentcs.util.event.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Keeps all {@code TransportOrder}s and provides methods to create and manipulate them.
 * 

* Note that no synchronization is done inside this class. Concurrent access of instances of this * class must be synchronized externally. *

*/ public class TransportOrderPoolManager extends TCSObjectManager { /** * This class's Logger. */ private static final Logger LOG = LoggerFactory.getLogger(TransportOrderPoolManager.class); /** * Provides names for transport orders and order sequences. */ private final ObjectNameProvider objectNameProvider; /** * Creates a new instance. * * @param objectRepo The object repo. * @param eventHandler The event handler to publish events to. * @param orderNameProvider Provides names for transport orders. */ @Inject public TransportOrderPoolManager( @Nonnull TCSObjectRepository objectRepo, @Nonnull @ApplicationEventBus EventHandler eventHandler, @Nonnull ObjectNameProvider orderNameProvider ) { super(objectRepo, eventHandler); this.objectNameProvider = requireNonNull(orderNameProvider, "orderNameProvider"); } /** * Removes all transport orders from this pool. */ public void clear() { List> objects = new ArrayList<>(); objects.addAll(getObjectRepo().getObjects(OrderSequence.class)); objects.addAll(getObjectRepo().getObjects(TransportOrder.class)); for (TCSObject curObject : objects) { getObjectRepo().removeObject(curObject.getReference()); emitObjectEvent( null, curObject, TCSObjectEvent.Type.OBJECT_REMOVED ); } } /** * Adds a new transport order to the pool. * This method implicitly adds the transport order to its wrapping sequence, if any. * * @param to The transfer object from which to create the new transport order. * @return The newly created transport order. * @throws ObjectExistsException If an object with the new object's name already exists. * @throws ObjectUnknownException If any object referenced in the TO does not exist. * @throws IllegalArgumentException One of: *
    *
  1. The order is supposed to be part of an order sequence, but * the sequence is already complete.
  2. *
  3. The type of the transport order and the order sequence differ.
  4. *
  5. The intended vehicle of the transport order and the order sequence differ.
  6. *
  7. A destination operation is not a valid operation on the destination object.
  8. *
*/ public TransportOrder createTransportOrder(TransportOrderCreationTO to) throws ObjectUnknownException, ObjectExistsException, IllegalArgumentException { TransportOrder newOrder = new TransportOrder( nameFor(to), toDriveOrders(to.getDestinations()) ) .withCreationTime(Instant.now()) .withPeripheralReservationToken(to.getPeripheralReservationToken()) .withIntendedVehicle(toVehicleReference(to.getIntendedVehicleName())) .withType(to.getType()) .withDeadline(to.getDeadline()) .withDispensable(to.isDispensable()) .withWrappingSequence(getWrappingSequence(to)) .withDependencies(getDependencies(to)) .withProperties(to.getProperties()); LOG.info( "Transport order is being created: {} -- {}", newOrder.getName(), newOrder.getAllDriveOrders() ); getObjectRepo().addObject(newOrder); emitObjectEvent(newOrder, null, TCSObjectEvent.Type.OBJECT_CREATED); if (newOrder.getWrappingSequence() != null) { OrderSequence sequence = getObjectRepo().getObject( OrderSequence.class, newOrder.getWrappingSequence() ); OrderSequence prevSeq = sequence; sequence = sequence.withOrder(newOrder.getReference()); getObjectRepo().replaceObject(sequence); emitObjectEvent(sequence, prevSeq, TCSObjectEvent.Type.OBJECT_MODIFIED); } // Return the newly created transport order. return newOrder; } /** * Sets a transport order's state. * * @param ref A reference to the transport order to be modified. * @param newState The transport order's new state. * @return The modified transport order. * @throws ObjectUnknownException If the referenced transport order is not * in this pool. */ public TransportOrder setTransportOrderState( TCSObjectReference ref, TransportOrder.State newState ) throws ObjectUnknownException { TransportOrder previousState = getObjectRepo().getObject(TransportOrder.class, ref); checkArgument( !previousState.getState().isFinalState(), "Transport order %s already in a final state, not changing %s -> %s.", ref.getName(), previousState.getState(), newState ); LOG.info( "Transport order's state changes: {} -- {} -> {}", previousState.getName(), previousState.getState(), newState ); TransportOrder order = previousState.withState(newState); getObjectRepo().replaceObject(order); emitObjectEvent( order, previousState, TCSObjectEvent.Type.OBJECT_MODIFIED ); return order; } /** * Sets a transport order's processing vehicle. * * @param orderRef A reference to the transport order to be modified. * @param vehicleRef A reference to the vehicle processing the order. * @param driveOrders The drive orders containing the data to be copied into this transport * order's drive orders. * @return The modified transport order. * @throws ObjectUnknownException If the referenced transport order is not * in this pool. * @throws IllegalArgumentException If the destinations of the given drive * orders do not match the destinations of the drive orders in this transport * order. */ public TransportOrder setTransportOrderProcessingVehicle( TCSObjectReference orderRef, TCSObjectReference vehicleRef, List driveOrders ) throws ObjectUnknownException, IllegalArgumentException { TransportOrder order = getObjectRepo().getObject(TransportOrder.class, orderRef); LOG.info( "Transport order's processing vehicle changes: {} -- {} -> {}", order.getName(), toObjectName(order.getProcessingVehicle()), toObjectName(vehicleRef) ); TransportOrder previousState = order; if (vehicleRef == null) { order = order.withProcessingVehicle(null); getObjectRepo().replaceObject(order); } else { Vehicle vehicle = getObjectRepo().getObject(Vehicle.class, vehicleRef); order = order.withProcessingVehicle(vehicle.getReference()) .withDriveOrders(driveOrders) .withCurrentDriveOrderIndex(0); getObjectRepo().replaceObject(order); if (order.getCurrentDriveOrder() != null) { order = order.withCurrentDriveOrderState(DriveOrder.State.TRAVELLING); getObjectRepo().replaceObject(order); } } emitObjectEvent( order, previousState, TCSObjectEvent.Type.OBJECT_MODIFIED ); return order; } /** * Copies drive order data from a list of drive orders to the given transport * order's future drive orders. * * @param orderRef A reference to the transport order to be modified. * @param newOrders The drive orders containing the data to be copied into * this transport order's drive orders. * @return The modified transport order. * @throws ObjectUnknownException If the referenced transport order is not * in this pool. * @throws IllegalArgumentException If the destinations of the given drive * orders do not match the destinations of the drive orders in this transport * order. */ public TransportOrder setTransportOrderDriveOrders( TCSObjectReference orderRef, List newOrders ) throws ObjectUnknownException, IllegalArgumentException { TransportOrder previousState = getObjectRepo().getObject(TransportOrder.class, orderRef); LOG.debug( "Transport order's drive orders change: {} -- {} -> {}", previousState.getName(), previousState.getAllDriveOrders(), newOrders ); TransportOrder order = previousState.withDriveOrders(newOrders); getObjectRepo().replaceObject(order); emitObjectEvent( order, previousState, TCSObjectEvent.Type.OBJECT_MODIFIED ); return order; } /** * Updates a transport order's current drive order. * Marks the current drive order as finished, adds it to the list of past * drive orders and sets the current drive order to the next one of the list * of future drive orders (or null, if that list is empty). * If the current drive order is null because all drive orders * have been finished already or none has been started, yet, nothing happens. * * @param ref A reference to the transport order to be modified. * @return The modified transport order. * @throws ObjectUnknownException If the referenced transport order is not * in this pool. */ public TransportOrder setTransportOrderNextDriveOrder(TCSObjectReference ref) throws ObjectUnknownException { TransportOrder previousState = getObjectRepo().getObject(TransportOrder.class, ref); TransportOrder order = previousState; // First, mark the current drive order as FINISHED and send an event. // Then, shift drive orders and send a second event. // Then, mark the current drive order as TRAVELLING and send another event. if (order.getCurrentDriveOrder() != null) { LOG.info( "Transport order's drive order finished: {} -- {}", order.getName(), order.getCurrentDriveOrder().getDestination() ); order = order.withCurrentDriveOrderState(DriveOrder.State.FINISHED); getObjectRepo().replaceObject(order); TransportOrder newState = order; emitObjectEvent( newState, previousState, TCSObjectEvent.Type.OBJECT_MODIFIED ); previousState = newState; order = order.withCurrentDriveOrderIndex(order.getCurrentDriveOrderIndex() + 1) .withCurrentRouteStepIndex(TransportOrder.ROUTE_STEP_INDEX_DEFAULT); getObjectRepo().replaceObject(order); newState = order; emitObjectEvent( newState, previousState, TCSObjectEvent.Type.OBJECT_MODIFIED ); previousState = newState; if (order.getCurrentDriveOrder() != null) { order = order.withCurrentDriveOrderState(DriveOrder.State.TRAVELLING); getObjectRepo().replaceObject(order); newState = order; emitObjectEvent( newState, previousState, TCSObjectEvent.Type.OBJECT_MODIFIED ); previousState = newState; } } emitObjectEvent( order, previousState, TCSObjectEvent.Type.OBJECT_MODIFIED ); return order; } /** * Sets a transport order's index of the last route step travelled for the currently processed * drive order. * * @param ref A reference to the transport order to be modified. * @param index The new index. * @return The modified transport order. * @throws ObjectUnknownException If the referenced transport order does not exist. */ public TransportOrder setTransportOrderCurrentRouteStepIndex( TCSObjectReference ref, int index ) throws ObjectUnknownException { TransportOrder previousState = getObjectRepo().getObject(TransportOrder.class, ref); LOG.debug( "Transport order's route step index changes: {} -- {} -> {}", previousState.getName(), previousState.getCurrentRouteStepIndex(), index ); TransportOrder order = previousState.withCurrentRouteStepIndex(index); getObjectRepo().replaceObject(order); emitObjectEvent( order, previousState, TCSObjectEvent.Type.OBJECT_MODIFIED ); return order; } /** * Set a transport order's intended vehicle. * * @param orderRef A reference to the transport order to be modified. * @param vehicleRef A reference to the vehicle intended for the transport order. * @return The modified transport order. * @throws ObjectUnknownException If the referenced transport order is not * in this pool or if the intended vehicle is not null and not in this this pool. * @throws IllegalArgumentException If the transport order is not in the dispatchable state. */ public TransportOrder setTransportOrderIntendedVehicle( TCSObjectReference orderRef, TCSObjectReference vehicleRef ) throws ObjectUnknownException, IllegalArgumentException { TransportOrder order = getObjectRepo().getObject(TransportOrder.class, orderRef); if (!canSetIntendedVehicle(order)) { throw new IllegalArgumentException( String.format( "Cannot set intended vehicle '%s' for transport order '%s' in state '%s'", toObjectName(vehicleRef), order.getName(), order.getState() ) ); } if (vehicleRef != null && getObjectRepo().getObjectOrNull(Vehicle.class, vehicleRef) == null) { throw new ObjectUnknownException("Unknown vehicle: " + vehicleRef.getName()); } LOG.info( "Transport order's intended vehicle changes: {} -- {} -> {}", order.getName(), toObjectName(order.getIntendedVehicle()), toObjectName(vehicleRef) ); TransportOrder previousState = order; order = order.withIntendedVehicle(vehicleRef); getObjectRepo().replaceObject(order); emitObjectEvent( order, previousState, TCSObjectEvent.Type.OBJECT_MODIFIED ); return order; } /** * Removes the referenced transport order from this pool. * * @param ref A reference to the transport order to be removed. * @return The removed transport order. * @throws ObjectUnknownException If the referenced transport order is not * in this pool. */ public TransportOrder removeTransportOrder(TCSObjectReference ref) throws ObjectUnknownException { TransportOrder order = getObjectRepo().getObject(TransportOrder.class, ref); // Make sure only orders in a final state are removed. checkArgument( order.getState().isFinalState(), "Transport order %s is not in a final state.", order.getName() ); getObjectRepo().removeObject(ref); emitObjectEvent( null, order, TCSObjectEvent.Type.OBJECT_REMOVED ); return order; } /** * Adds a new order sequence to the pool. * * @param to The transfer object from which to create the new order sequence. * @return The newly created order sequence. * @throws ObjectExistsException If an object with the new object's name already exists. * @throws ObjectUnknownException If any object referenced in the TO does not exist. */ public OrderSequence createOrderSequence(OrderSequenceCreationTO to) throws ObjectExistsException, ObjectUnknownException { OrderSequence newSequence = new OrderSequence(nameFor(to)) .withType(to.getType()) .withIntendedVehicle(toVehicleReference(to.getIntendedVehicleName())) .withFailureFatal(to.isFailureFatal()) .withProperties(to.getProperties()); LOG.info("Order sequence is being created: {}", newSequence.getName()); getObjectRepo().addObject(newSequence); emitObjectEvent( newSequence, null, TCSObjectEvent.Type.OBJECT_CREATED ); // Return the newly created transport order. return newSequence; } /** * Sets an order sequence's finished index. * * @param seqRef A reference to the order sequence to be modified. * @param index The sequence's new finished index. * @return The modified order sequence. * @throws ObjectUnknownException If the referenced transport order is not * in this pool. */ public OrderSequence setOrderSequenceFinishedIndex( TCSObjectReference seqRef, int index ) throws ObjectUnknownException { OrderSequence previousState = getObjectRepo().getObject(OrderSequence.class, seqRef); LOG.debug( "Order sequence's finished index changes: {} -- {} -> {}", previousState.getName(), previousState.getFinishedIndex(), index ); OrderSequence sequence = previousState.withFinishedIndex(index); getObjectRepo().replaceObject(sequence); emitObjectEvent( sequence, previousState, TCSObjectEvent.Type.OBJECT_MODIFIED ); return sequence; } /** * Sets an order sequence's complete flag. * * @param seqRef A reference to the order sequence to be modified. * @return The modified order sequence. * @throws ObjectUnknownException If the referenced transport order is not * in this pool. */ public OrderSequence setOrderSequenceComplete(TCSObjectReference seqRef) throws ObjectUnknownException { OrderSequence previousState = getObjectRepo().getObject(OrderSequence.class, seqRef); LOG.info("Order sequence being marked as complete: {}", previousState.getName()); OrderSequence sequence = previousState.withComplete(true); getObjectRepo().replaceObject(sequence); emitObjectEvent( sequence, previousState, TCSObjectEvent.Type.OBJECT_MODIFIED ); return sequence; } /** * Sets an order sequence's finished flag. * * @param seqRef A reference to the order sequence to be modified. * @return The modified order sequence. * @throws ObjectUnknownException If the referenced transport order is not * in this pool. */ public OrderSequence setOrderSequenceFinished(TCSObjectReference seqRef) throws ObjectUnknownException { OrderSequence previousState = getObjectRepo().getObject(OrderSequence.class, seqRef); LOG.info("Order sequence being marked as finished: {}", previousState.getName()); OrderSequence sequence = previousState.withFinished(true); getObjectRepo().replaceObject(sequence); emitObjectEvent( sequence, previousState, TCSObjectEvent.Type.OBJECT_MODIFIED ); return sequence; } /** * Sets an order sequence's processing vehicle. * * @param seqRef A reference to the order sequence to be modified. * @param vehicleRef A reference to the vehicle processing the order sequence. * @return The modified order sequence. * @throws ObjectUnknownException If the referenced transport order is not * in this pool. */ public OrderSequence setOrderSequenceProcessingVehicle( TCSObjectReference seqRef, TCSObjectReference vehicleRef ) throws ObjectUnknownException { OrderSequence previousState = getObjectRepo().getObject(OrderSequence.class, seqRef); LOG.info( "Order sequence's processing vehicle changes: {} -- {} -> {}", previousState.getName(), toObjectName(previousState.getProcessingVehicle()), toObjectName(vehicleRef) ); OrderSequence sequence = previousState; if (vehicleRef == null) { sequence = sequence.withProcessingVehicle(null); getObjectRepo().replaceObject(sequence); } else { Vehicle vehicle = getObjectRepo().getObject(Vehicle.class, vehicleRef); sequence = sequence.withProcessingVehicle(vehicle.getReference()); getObjectRepo().replaceObject(sequence); } emitObjectEvent( sequence, previousState, TCSObjectEvent.Type.OBJECT_MODIFIED ); return sequence; } /** * Removes the referenced order sequence from this pool. * * @param ref A reference to the order sequence to be removed. * @return The removed order sequence. * @throws ObjectUnknownException If the referenced order sequence is not in this pool. */ public OrderSequence removeOrderSequence(TCSObjectReference ref) throws ObjectUnknownException { OrderSequence previousState = getObjectRepo().getObject(OrderSequence.class, ref); OrderSequence sequence = previousState; // XXX Any sanity checks here? getObjectRepo().removeObject(ref); emitObjectEvent( null, previousState, TCSObjectEvent.Type.OBJECT_REMOVED ); return sequence; } /** * Removes a completed order sequence including its transport orders. * * @param ref A reference to the order sequence. * @throws ObjectUnknownException If the referenced order sequence is not in this pool. * @throws IllegalArgumentException If the order sequence is not finished, yet. */ public void removeFinishedOrderSequenceAndOrders(TCSObjectReference ref) throws ObjectUnknownException, IllegalArgumentException { OrderSequence previousState = getObjectRepo().getObject(OrderSequence.class, ref); checkArgument( previousState.isFinished(), "Order sequence %s is not finished", previousState.getName() ); OrderSequence sequence = previousState; getObjectRepo().removeObject(ref); emitObjectEvent(null, previousState, TCSObjectEvent.Type.OBJECT_REMOVED); // Also remove all orders in the sequence. for (TCSObjectReference orderRef : sequence.getOrders()) { removeTransportOrder(orderRef); } } private Set> getDependencies(TransportOrderCreationTO to) throws ObjectUnknownException { Set> result = new HashSet<>(); for (String dependencyName : to.getDependencyNames()) { result.add(getObjectRepo().getObject(TransportOrder.class, dependencyName).getReference()); } return result; } private TCSObjectReference getWrappingSequence(TransportOrderCreationTO to) throws ObjectUnknownException, IllegalArgumentException { if (to.getWrappingSequence() == null) { return null; } OrderSequence sequence = getObjectRepo().getObject( OrderSequence.class, to.getWrappingSequence() ); checkArgument(!sequence.isComplete(), "Order sequence %s is already complete", sequence); checkArgument( Objects.equals(to.getType(), sequence.getType()), "Order sequence %s has different type than order %s: %s != %s", sequence, to.getName(), sequence.getType(), to.getType() ); checkArgument( Objects.equals(to.getIntendedVehicleName(), getIntendedVehicleName(sequence)), "Order sequence %s has different intended vehicle than order %s: %s != %s", sequence, to.getName(), sequence.getIntendedVehicle(), to.getIntendedVehicleName() ); return sequence.getReference(); } private TCSObjectReference toVehicleReference(String vehicleName) throws ObjectUnknownException { if (vehicleName == null) { return null; } Vehicle vehicle = getObjectRepo().getObject(Vehicle.class, vehicleName); return vehicle.getReference(); } private List toDriveOrders(List dests) throws ObjectUnknownException, IllegalArgumentException { List result = new ArrayList<>(dests.size()); for (DestinationCreationTO destTo : dests) { TCSObject destObject = getObjectRepo().getObjectOrNull(destTo.getDestLocationName()); if (destObject instanceof Point) { if (!isValidOperationOnPoint(destTo.getDestOperation())) { throw new IllegalArgumentException( destTo.getDestOperation() + " is not a valid operation for point destination " + destObject.getName() ); } } else if (destObject instanceof Location) { if (!isValidLocationDestination(destTo, (Location) destObject)) { throw new IllegalArgumentException( destTo.getDestOperation() + " is not a valid operation for location destination " + destObject.getName() ); } } else { throw new ObjectUnknownException(destTo.getDestLocationName()); } result.add( new DriveOrder( new DriveOrder.Destination(destObject.getReference()) .withOperation(destTo.getDestOperation()) .withProperties(destTo.getProperties()) ) ); } return result; } private boolean isValidOperationOnPoint(String operation) { return operation.equals(Destination.OP_MOVE) || operation.equals(Destination.OP_PARK); } private boolean isValidLocationDestination(DestinationCreationTO dest, Location location) { LocationType type = getObjectRepo() .getObjectOrNull(LocationType.class, location.getType().getName()); return type != null && isValidOperationOnLocationType(dest.getDestOperation(), type) && location.getAttachedLinks().stream() .anyMatch(link -> isValidOperationOnLink(dest.getDestOperation(), link)); } private boolean isValidOperationOnLink(String operation, Link link) { return link.getAllowedOperations().isEmpty() || link.getAllowedOperations().contains(operation) || operation.equals(Destination.OP_NOP); } private boolean isValidOperationOnLocationType(String operation, LocationType type) { return type.isAllowedOperation(operation) || operation.equals(Destination.OP_NOP); } @Nullable private String getIntendedVehicleName(OrderSequence sequence) { return sequence.getIntendedVehicle() == null ? null : sequence.getIntendedVehicle().getName(); } @Nonnull private String nameFor( @Nonnull TransportOrderCreationTO to ) { if (to.hasIncompleteName()) { return objectNameProvider.apply(to); } else { return to.getName(); } } @Nonnull private String nameFor( @Nonnull OrderSequenceCreationTO to ) { if (to.hasIncompleteName()) { return objectNameProvider.apply(to); } else { return to.getName(); } } @Nonnull private String toObjectName(TCSObjectReference ref) { return ref == null ? "null" : ref.getName(); } private boolean canSetIntendedVehicle(TransportOrder order) { return order.hasState(TransportOrder.State.RAW) || order.hasState(TransportOrder.State.ACTIVE) || order.hasState(TransportOrder.State.DISPATCHABLE); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy