org.opentcs.kernel.workingset.PlantModelManager Maven / Gradle / Ivy
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.workingset;
import static java.util.Objects.requireNonNull;
import static org.opentcs.util.Assertions.checkState;
import jakarta.annotation.Nonnull;
import jakarta.inject.Inject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.opentcs.access.to.model.BlockCreationTO;
import org.opentcs.access.to.model.BoundingBoxCreationTO;
import org.opentcs.access.to.model.CoupleCreationTO;
import org.opentcs.access.to.model.LocationCreationTO;
import org.opentcs.access.to.model.LocationTypeCreationTO;
import org.opentcs.access.to.model.PathCreationTO;
import org.opentcs.access.to.model.PlantModelCreationTO;
import org.opentcs.access.to.model.PointCreationTO;
import org.opentcs.access.to.model.VehicleCreationTO;
import org.opentcs.access.to.model.VisualLayoutCreationTO;
import org.opentcs.access.to.peripherals.PeripheralOperationCreationTO;
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.Block;
import org.opentcs.data.model.BoundingBox;
import org.opentcs.data.model.Couple;
import org.opentcs.data.model.Location;
import org.opentcs.data.model.LocationType;
import org.opentcs.data.model.Path;
import org.opentcs.data.model.PeripheralInformation;
import org.opentcs.data.model.Point;
import org.opentcs.data.model.Pose;
import org.opentcs.data.model.TCSResource;
import org.opentcs.data.model.TCSResourceReference;
import org.opentcs.data.model.Vehicle;
import org.opentcs.data.model.Vehicle.EnergyLevelThresholdSet;
import org.opentcs.data.model.visualization.VisualLayout;
import org.opentcs.data.order.OrderSequence;
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.util.event.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Presents a view on the topology of a plant model contained in a
* {@link TCSObjectRepository}.
*
* Note that no synchronization is done inside this class. Concurrent access of instances of this
* class must be synchronized externally.
*
*/
public class PlantModelManager
extends
TCSObjectManager {
/**
* This class's Logger.
*/
private static final Logger LOG = LoggerFactory.getLogger(PlantModelManager.class);
/**
* This model's name.
*/
private String name = "";
/**
* This model's properties.
*/
private Map properties = new HashMap<>();
/**
* Creates a new model.
*
* @param objectRepo The object repo.
* @param eventHandler The event handler to publish events to.
*/
@Inject
public PlantModelManager(
@Nonnull
TCSObjectRepository objectRepo,
@Nonnull
@ApplicationEventBus
EventHandler eventHandler
) {
super(objectRepo, eventHandler);
}
/**
* Returns this model's name.
*
* @return This model's name.
*/
public String getName() {
return name;
}
/**
* Sets this model's name.
*
* @param name This model's new name.
*/
public void setName(String name) {
this.name = requireNonNull(name, "name");
}
/**
* Returns this model's properties.
*
* @return This model's properties.
*/
public Map getProperties() {
return properties;
}
/**
* Sets this model's properties.
*
* @param properties The properties.
*/
public void setProperties(Map properties) {
this.properties = requireNonNull(properties, "properties");
}
/**
* Removes all model objects from this model and the object pool by which it is backed.
*/
public void clear() {
List> objects = new ArrayList<>();
objects.addAll(getObjectRepo().getObjects(VisualLayout.class));
objects.addAll(getObjectRepo().getObjects(Vehicle.class));
objects.addAll(getObjectRepo().getObjects(Block.class));
objects.addAll(getObjectRepo().getObjects(Path.class));
objects.addAll(getObjectRepo().getObjects(Location.class));
objects.addAll(getObjectRepo().getObjects(LocationType.class));
objects.addAll(getObjectRepo().getObjects(Point.class));
for (TCSObject> curObject : objects) {
getObjectRepo().removeObject(curObject.getReference());
emitObjectEvent(
null,
curObject,
TCSObjectEvent.Type.OBJECT_REMOVED
);
}
}
/**
* Creates new plant model objects with unique IDs and all other attributes taken from the given
* transfer object.
*
* @param to The transfer object from which to create the new objects.
* @throws ObjectExistsException If an object with a new object's name already exists.
* @throws ObjectUnknownException If any object referenced in the TO does not exist.
*/
public void createPlantModelObjects(PlantModelCreationTO to)
throws ObjectExistsException,
ObjectUnknownException {
LOG.info("Plant model is being created: {}", to.getName());
clear();
setName(to.getName());
setProperties(to.getProperties());
for (PointCreationTO point : to.getPoints()) {
createPoint(point);
}
for (LocationTypeCreationTO locType : to.getLocationTypes()) {
createLocationType(locType);
}
for (LocationCreationTO loc : to.getLocations()) {
createLocation(loc);
}
for (PathCreationTO path : to.getPaths()) {
createPath(path);
}
for (BlockCreationTO block : to.getBlocks()) {
createBlock(block);
}
for (VehicleCreationTO vehicle : to.getVehicles()) {
createVehicle(vehicle);
}
createVisualLayout(to.getVisualLayout());
}
/**
* Locks/Unlocks a path.
*
* @param ref A reference to the path to be modified.
* @param newLocked If true
, this path will be locked when the
* method call returns; if false
, this path will be unlocked.
* @return The modified path.
* @throws ObjectUnknownException If the referenced path does not exist.
*/
public Path setPathLocked(TCSObjectReference ref, boolean newLocked)
throws ObjectUnknownException {
Path previousState = getObjectRepo().getObject(Path.class, ref);
LOG.debug(
"Path's locked state changes: {} -- {} -> {}",
previousState.getName(),
previousState.isLocked(),
newLocked
);
Path path = previousState.withLocked(newLocked);
getObjectRepo().replaceObject(path.withLocked(newLocked));
emitObjectEvent(
path,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return path;
}
/**
* Locks/Unlocks a location.
*
* @param ref A reference to the location to be modified.
* @param newLocked If {@code true}, this path will be locked when the method call returns;
* if {@code false}, this path will be unlocked.
* @return The modified location.
* @throws ObjectUnknownException If the referenced location does not exist.
*/
public Location setLocationLocked(TCSObjectReference ref, boolean newLocked)
throws ObjectUnknownException {
Location previousState = getObjectRepo().getObject(Location.class, ref);
LOG.debug(
"Location's locked state changes: {} -- {} -> {}",
previousState.getName(),
previousState.isLocked(),
newLocked
);
Location location = previousState.withLocked(newLocked);
getObjectRepo().replaceObject(location);
emitObjectEvent(
location,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return location;
}
/**
* Sets a location's reservation token.
*
* @param ref A reference to the location to be modified.
* @param newToken The new reservation token.
* @return The modified location.
* @throws ObjectUnknownException If the referenced location does not exist.
*/
public Location setLocationReservationToken(TCSObjectReference ref, String newToken)
throws ObjectUnknownException {
Location previousState = getObjectRepo().getObject(Location.class, ref);
LOG.debug(
"Location's reservation token changes: {} -- {} -> {}",
previousState.getName(),
previousState.getPeripheralInformation().getReservationToken(),
newToken
);
Location location = previousState.withPeripheralInformation(
previousState.getPeripheralInformation().withReservationToken(newToken)
);
getObjectRepo().replaceObject(location);
emitObjectEvent(
location,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return location;
}
/**
* Sets a location's processing state.
*
* @param ref A reference to the location to be modified.
* @param newState The new processing state.
* @return The modified location.
* @throws ObjectUnknownException If the referenced location does not exist.
*/
public Location setLocationProcState(
TCSObjectReference ref,
PeripheralInformation.ProcState newState
)
throws ObjectUnknownException {
Location previousState = getObjectRepo().getObject(Location.class, ref);
LOG.debug(
"Location's proc state changes: {} -- {} -> {}",
previousState.getName(),
previousState.getPeripheralInformation().getProcState(),
newState
);
Location location = previousState.withPeripheralInformation(
previousState.getPeripheralInformation().withProcState(newState)
);
getObjectRepo().replaceObject(location);
emitObjectEvent(
location,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return location;
}
/**
* Sets a location's state.
*
* @param ref A reference to the location to be modified.
* @param newState The new state.
* @return The modified location.
* @throws ObjectUnknownException If the referenced location does not exist.
*/
public Location setLocationState(
TCSObjectReference ref,
PeripheralInformation.State newState
)
throws ObjectUnknownException {
Location previousState = getObjectRepo().getObject(Location.class, ref);
LOG.debug(
"Location's state changes: {} -- {} -> {}",
previousState.getName(),
previousState.getPeripheralInformation().getState(),
newState
);
Location location = previousState.withPeripheralInformation(
previousState.getPeripheralInformation().withState(newState)
);
getObjectRepo().replaceObject(location);
emitObjectEvent(
location,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return location;
}
/**
* Sets a location's peripheral job.
*
* @param ref A reference to the location to be modified.
* @param newJob The new peripheral job.
* @return The modified location.
* @throws ObjectUnknownException If the referenced location does not exist.
*/
public Location setLocationPeripheralJob(
TCSObjectReference ref,
TCSObjectReference newJob
)
throws ObjectUnknownException {
Location previousState = getObjectRepo().getObject(Location.class, ref);
LOG.debug(
"Location's peripheral job changes: {} -- {} -> {}",
previousState.getName(),
previousState.getPeripheralInformation().getPeripheralJob(),
newJob
);
Location location = previousState.withPeripheralInformation(
previousState.getPeripheralInformation().withPeripheralJob(newJob)
);
getObjectRepo().replaceObject(location);
emitObjectEvent(
location,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return location;
}
/**
* Sets a vehicle's energy level.
*
* @param ref A reference to the vehicle to be modified.
* @param energyLevel The vehicle's new energy level.
* @return The modified vehicle.
* @throws ObjectUnknownException If the referenced vehicle does not exist.
*/
public Vehicle setVehicleEnergyLevel(
TCSObjectReference ref,
int energyLevel
)
throws ObjectUnknownException {
Vehicle previousState = getObjectRepo().getObject(Vehicle.class, ref);
LOG.debug(
"Vehicle's energy level changes: {} -- {} -> {}",
previousState.getName(),
previousState.getEnergyLevel(),
energyLevel
);
Vehicle vehicle = previousState.withEnergyLevel(energyLevel);
getObjectRepo().replaceObject(vehicle);
emitObjectEvent(
vehicle,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return vehicle;
}
/**
* Sets the energy level threshold set for a given vehicle.
*
* @param ref Reference to the vehicle.
* @param energyLevelThresholdSet The energy level threshold set.
* @return The modified vehicle.
* @throws ObjectUnknownException The vehicle reference is not known.
*/
public Vehicle setVehicleEnergyLevelThresholdSet(
TCSObjectReference ref,
EnergyLevelThresholdSet energyLevelThresholdSet
)
throws ObjectUnknownException {
Vehicle previousState = getObjectRepo().getObject(Vehicle.class, ref);
LOG.info(
"Vehicle's energy level threshold set changes: {} -- {} -> {}",
previousState.getName(),
previousState.getEnergyLevelThresholdSet(),
energyLevelThresholdSet
);
Vehicle vehicle = previousState.withEnergyLevelThresholdSet(energyLevelThresholdSet);
getObjectRepo().replaceObject(vehicle);
emitObjectEvent(
vehicle,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return vehicle;
}
/**
* Sets a vehicle's recharge operation.
*
* @param ref A reference to the vehicle to be modified.
* @param rechargeOperation The vehicle's new recharge operation.
* @return The modified vehicle.
* @throws ObjectUnknownException If the referenced vehicle does not exist.
*/
public Vehicle setVehicleRechargeOperation(
TCSObjectReference ref,
String rechargeOperation
)
throws ObjectUnknownException {
Vehicle previousState = getObjectRepo().getObject(Vehicle.class, ref);
LOG.info(
"Vehicle's recharge operation changes: {} -- {} -> {}",
previousState.getName(),
previousState.getRechargeOperation(),
rechargeOperation
);
Vehicle vehicle = previousState.withRechargeOperation(rechargeOperation);
getObjectRepo().replaceObject(vehicle);
emitObjectEvent(
vehicle,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return vehicle;
}
/**
* Sets 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.
* @return The modified vehicle.
* @throws ObjectUnknownException If the referenced vehicle does not exist.
*/
public Vehicle setVehicleLoadHandlingDevices(
TCSObjectReference ref,
List devices
)
throws ObjectUnknownException {
Vehicle previousState = getObjectRepo().getObject(Vehicle.class, ref);
LOG.debug(
"Vehicle's load handling devices change: {} -- {} -> {}",
previousState.getName(),
previousState.getLoadHandlingDevices(),
devices
);
Vehicle vehicle = previousState.withLoadHandlingDevices(devices);
getObjectRepo().replaceObject(vehicle);
emitObjectEvent(
vehicle,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return vehicle;
}
/**
* Sets a vehicle's state.
*
* @param ref A reference to the vehicle to be modified.
* @param newState The vehicle's new state.
* @return The modified vehicle.
* @throws ObjectUnknownException If the referenced vehicle does not exist.
*/
public Vehicle setVehicleState(
TCSObjectReference ref,
Vehicle.State newState
)
throws ObjectUnknownException {
Vehicle previousState = getObjectRepo().getObject(Vehicle.class, ref);
LOG.debug(
"Vehicle's state changes: {} -- {} -> {}",
previousState.getName(),
previousState.getState(),
newState
);
Vehicle vehicle = previousState.withState(newState);
getObjectRepo().replaceObject(vehicle);
emitObjectEvent(
vehicle,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return vehicle;
}
/**
* Sets a vehicle integration level.
*
* @param ref A reference to the vehicle to be modified.
* @param integrationLevel The vehicle's new integration level.
* @return The modified vehicle.
* @throws ObjectUnknownException If the referenced vehicle does not exist.
*/
public Vehicle setVehicleIntegrationLevel(
TCSObjectReference ref,
Vehicle.IntegrationLevel integrationLevel
)
throws ObjectUnknownException {
Vehicle previousState = getObjectRepo().getObject(Vehicle.class, ref);
LOG.info(
"Vehicle's integration level changes: {} -- {} -> {}",
previousState.getName(),
previousState.getIntegrationLevel(),
integrationLevel
);
Vehicle vehicle = previousState.withIntegrationLevel(integrationLevel);
getObjectRepo().replaceObject(vehicle);
emitObjectEvent(
vehicle,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return vehicle;
}
/**
* Sets a vehicle's paused state.
*
* @param ref A reference to the vehicle to be modified.
* @param paused The vehicle's new paused state.
* @return The modified vehicle.
* @throws ObjectUnknownException If the referenced vehicle does not exist.
*/
public Vehicle setVehiclePaused(
TCSObjectReference ref,
boolean paused
)
throws ObjectUnknownException {
Vehicle previousState = getObjectRepo().getObject(Vehicle.class, ref);
LOG.info(
"Vehicle's paused state changes: {} -- {} -> {}",
previousState.getName(),
previousState.isPaused(),
paused
);
Vehicle vehicle = previousState.withPaused(paused);
getObjectRepo().replaceObject(vehicle);
emitObjectEvent(
vehicle,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return vehicle;
}
/**
* Sets a vehicle's processing state.
*
* @param ref A reference to the vehicle to be modified.
* @param newState The vehicle's new processing state.
* @return The modified vehicle.
* @throws ObjectUnknownException If the referenced vehicle does not exist.
*/
public Vehicle setVehicleProcState(
TCSObjectReference ref,
Vehicle.ProcState newState
)
throws ObjectUnknownException {
Vehicle previousState = getObjectRepo().getObject(Vehicle.class, ref);
LOG.debug(
"Vehicle's proc state changes: {} -- {} -> {}",
previousState.getName(),
previousState.getProcState(),
newState
);
Vehicle vehicle = previousState.withProcState(newState);
getObjectRepo().replaceObject(vehicle);
emitObjectEvent(
vehicle,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return vehicle;
}
/**
* Sets the allowed order types for a given vehicle.
*
* @param ref Reference to the vehicle.
* @param allowedOrderTypes Set of allowed order types.
* @return The vehicle with the allowed order types.
* @throws ObjectUnknownException The vehicle reference is not known.
*/
public Vehicle setVehicleAllowedOrderTypes(
TCSObjectReference ref,
Set allowedOrderTypes
)
throws ObjectUnknownException {
Vehicle previousState = getObjectRepo().getObject(Vehicle.class, ref);
LOG.info(
"Vehicle's allowed order types change: {} -- {} -> {}",
previousState.getName(),
previousState.getAllowedOrderTypes(),
allowedOrderTypes
);
Vehicle vehicle = previousState.withAllowedOrderTypes(allowedOrderTypes);
getObjectRepo().replaceObject(vehicle);
emitObjectEvent(
vehicle,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return vehicle;
}
/**
* Sets a vehicle's envelope key.
*
* @param ref A reference to the vehicle to be modified.
* @param envelopeKey The vehicle's new envelope key.
* @return The modified vehicle.
* @throws ObjectUnknownException If the referenced vehicle does not exist.
*/
public Vehicle setVehicleEnvelopeKey(
TCSObjectReference ref,
String envelopeKey
)
throws ObjectUnknownException {
Vehicle previousState = getObjectRepo().getObject(Vehicle.class, ref);
LOG.info(
"Vehicle's envelope key change: {} -- {} -> {}",
previousState.getName(),
previousState.getEnvelopeKey(),
envelopeKey
);
Vehicle vehicle = previousState.withEnvelopeKey(envelopeKey);
getObjectRepo().replaceObject(vehicle);
emitObjectEvent(
vehicle,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return vehicle;
}
/**
* Sets a vehicle's bounding box.
*
* @param ref A reference to the vehicle to be modified.
* @param boundingBox The vehicle's new bounding box.
* @return The modified vehicle.
* @throws ObjectUnknownException If the referenced vehicle does not exist.
*/
public Vehicle setVehicleBoundingBox(
TCSObjectReference ref,
BoundingBox boundingBox
)
throws ObjectUnknownException {
Vehicle previousState = getObjectRepo().getObject(Vehicle.class, ref);
LOG.debug(
"Vehicle's bounding box change: {} -- {} -> {}",
previousState.getName(),
previousState.getBoundingBox(),
boundingBox
);
Vehicle vehicle = previousState.withBoundingBox(boundingBox);
getObjectRepo().replaceObject(vehicle);
emitObjectEvent(
vehicle,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return vehicle;
}
/**
* Sets a vehicle's position.
*
* @param ref A reference to the vehicle to be modified.
* @param newPosRef A reference to the point the vehicle is occupying.
* @return The modified vehicle.
* @throws ObjectUnknownException If the referenced vehicle does not exist.
*/
public Vehicle setVehiclePosition(
TCSObjectReference ref,
TCSObjectReference newPosRef
)
throws ObjectUnknownException {
Vehicle vehicle = getObjectRepo().getObject(Vehicle.class, ref);
LOG.debug(
"Vehicle's position changes: {} -- {} -> {}",
vehicle.getName(),
vehicle.getCurrentPosition() == null ? null : vehicle.getCurrentPosition().getName(),
newPosRef == null ? null : newPosRef.getName()
);
Vehicle previousVehicleState = vehicle;
// If the vehicle was occupying a point before, clear it and send an event.
if (vehicle.getCurrentPosition() != null) {
Point oldVehiclePos = getObjectRepo().getObject(Point.class, vehicle.getCurrentPosition());
Point previousPointState = oldVehiclePos;
oldVehiclePos = oldVehiclePos.withOccupyingVehicle(null);
getObjectRepo().replaceObject(oldVehiclePos);
emitObjectEvent(
oldVehiclePos,
previousPointState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
}
// If the vehicle is occupying a point now, set that and send an event.
if (newPosRef != null) {
Point newVehiclePos = getObjectRepo().getObject(Point.class, newPosRef);
Point previousPointState = newVehiclePos;
newVehiclePos = newVehiclePos.withOccupyingVehicle(ref);
getObjectRepo().replaceObject(newVehiclePos);
emitObjectEvent(
newVehiclePos,
previousPointState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
}
vehicle = vehicle.withCurrentPosition(newPosRef);
getObjectRepo().replaceObject(vehicle);
emitObjectEvent(
vehicle,
previousVehicleState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return vehicle;
}
/**
* Sets a vehicle's next position.
*
* @param ref A reference to the vehicle to be modified.
* @param newPosition A reference to the point the vehicle is expected to
* occupy next.
* @return The modified vehicle.
* @throws ObjectUnknownException If the referenced vehicle does not exist.
*/
public Vehicle setVehicleNextPosition(
TCSObjectReference ref,
TCSObjectReference newPosition
)
throws ObjectUnknownException {
Vehicle previousState = getObjectRepo().getObject(Vehicle.class, ref);
LOG.debug(
"Vehicle's next position changes: {} -- {} -> {}",
previousState.getName(),
previousState.getNextPosition(),
newPosition
);
Vehicle vehicle = previousState.withNextPosition(newPosition);
getObjectRepo().replaceObject(vehicle);
emitObjectEvent(
vehicle,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return vehicle;
}
/**
* Sets a vehicle's pose.
*
* @param ref A reference to the vehicle to be modified.
* @param pose The vehicle's pose.
* @return The modified vehicle.
* @throws ObjectUnknownException If the referenced vehicle does not exist.
*/
public Vehicle setVehiclePose(
TCSObjectReference ref,
@Nonnull
Pose pose
)
throws ObjectUnknownException {
requireNonNull(pose, "pose");
Vehicle previousState = getObjectRepo().getObject(Vehicle.class, ref);
LOG.trace(
"Vehicle's pose changes: {} -- {} -> {}",
previousState.getName(),
previousState.getPose(),
pose
);
Vehicle vehicle = previousState.withPose(pose);
getObjectRepo().replaceObject(vehicle);
emitObjectEvent(
vehicle,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return vehicle;
}
/**
* Sets 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.
* @return The modified vehicle.
* @throws ObjectUnknownException If the referenced vehicle does not exist.
*/
public Vehicle setVehicleTransportOrder(
TCSObjectReference vehicleRef,
TCSObjectReference orderRef
)
throws ObjectUnknownException {
Vehicle vehicle = getObjectRepo().getObject(Vehicle.class, vehicleRef);
Vehicle previousState = vehicle;
LOG.debug(
"Vehicle's transport order changes: {} -- {} -> {}",
previousState.getName(),
previousState.getTransportOrder(),
orderRef
);
if (orderRef == null) {
vehicle = vehicle.withTransportOrder(null);
getObjectRepo().replaceObject(vehicle);
}
else {
TransportOrder order = getObjectRepo().getObject(TransportOrder.class, orderRef);
vehicle = vehicle.withTransportOrder(order.getReference());
getObjectRepo().replaceObject(vehicle);
}
emitObjectEvent(
vehicle,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return vehicle;
}
/**
* Sets a vehicle's order sequence.
*
* @param vehicleRef A reference to the vehicle to be modified.
* @param seqRef A reference to the order sequence the vehicle processes.
* @return The modified vehicle.
* @throws ObjectUnknownException If the referenced vehicle does not exist.
*/
public Vehicle setVehicleOrderSequence(
TCSObjectReference vehicleRef,
TCSObjectReference seqRef
)
throws ObjectUnknownException {
Vehicle vehicle = getObjectRepo().getObject(Vehicle.class, vehicleRef);
Vehicle previousState = vehicle;
LOG.debug(
"Vehicle's order sequence changes: {} -- {} -> {}",
previousState.getName(),
previousState.getOrderSequence(),
seqRef
);
if (seqRef == null) {
vehicle = vehicle.withOrderSequence(null);
getObjectRepo().replaceObject(vehicle);
}
else {
OrderSequence seq = getObjectRepo().getObject(OrderSequence.class, seqRef);
vehicle = vehicle.withOrderSequence(seq.getReference());
getObjectRepo().replaceObject(vehicle);
}
emitObjectEvent(
vehicle,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return vehicle;
}
/**
* Sets a vehicle's claimed resources.
*
* @param vehicleRef A reference to the vehicle to be modified.
* @param resources The new resources.
* @return The modified vehicle.
* @throws ObjectUnknownException If the referenced vehicle does not exist.
*/
public Vehicle setVehicleClaimedResources(
TCSObjectReference vehicleRef,
List>> resources
)
throws ObjectUnknownException {
Vehicle previousState = getObjectRepo().getObject(Vehicle.class, vehicleRef);
LOG.debug(
"Vehicle's claimed resources change: {} -- {} -> {}",
previousState.getName(),
previousState.getClaimedResources(),
resources
);
Vehicle vehicle = previousState.withClaimedResources(unmodifiableCopy(resources));
getObjectRepo().replaceObject(vehicle);
emitObjectEvent(
vehicle,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return vehicle;
}
/**
* Sets a vehicle's allocated resources.
*
* @param vehicleRef A reference to the vehicle to be modified.
* @param resources The new resources.
* @return The modified vehicle.
* @throws ObjectUnknownException If the referenced vehicle does not exist.
*/
public Vehicle setVehicleAllocatedResources(
TCSObjectReference vehicleRef,
List>> resources
)
throws ObjectUnknownException {
Vehicle previousState = getObjectRepo().getObject(Vehicle.class, vehicleRef);
LOG.debug(
"Vehicle's allocated resources change: {} -- {} -> {}",
previousState.getName(),
previousState.getAllocatedResources(),
resources
);
Vehicle vehicle = previousState.withAllocatedResources(unmodifiableCopy(resources));
getObjectRepo().replaceObject(vehicle);
emitObjectEvent(
vehicle,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return vehicle;
}
/**
* Returns a PlantModelCreationTO for this model.
*
* @return A PlantModelCreationTO for this model.
*/
public PlantModelCreationTO createPlantModelCreationTO() {
return new PlantModelCreationTO(name)
.withProperties(getProperties())
.withPoints(getPoints())
.withPaths(getPaths())
.withVehicles(getVehicles())
.withLocationTypes(getLocationTypes())
.withLocations(getLocations())
.withBlocks(getBlocks())
.withVisualLayout(getVisualLayout());
}
/**
* Expands a set of resources A to a set of resources B.
* B contains the resources in A with blocks expanded to
* their actual members.
* The given set is not modified.
*
* @param resources The set of resources to be expanded.
* @return The given set with resources expanded.
* @throws ObjectUnknownException If an object referenced in the given set
* does not exist.
*/
public Set> expandResources(
@Nonnull
Set> resources
)
throws ObjectUnknownException {
requireNonNull(resources, "resources");
Set blocks = getObjectRepo().getObjects(Block.class);
// First, collect the given references plus references to all members of blocks that contain the
// given references in a set.
// We could look up all resources and add them to the result immediately, but by first
// collecting all references, we ensure that we look up each resource only once.
Set> refsToLookUp = new HashSet<>();
for (TCSResourceReference> resourceRef : resources) {
refsToLookUp.add(resourceRef);
blocks.stream()
.filter(block -> block.getMembers().contains(resourceRef))
.flatMap(block -> block.getMembers().stream())
.forEach(memberRef -> refsToLookUp.add(memberRef));
}
// Look up and return the actual resources.
return refsToLookUp.stream()
.map(memberRef -> (TCSResource>) getObjectRepo().getObject(memberRef))
.collect(Collectors.toSet());
}
private List mapPeripheralOperationTOs(
List creationTOs
) {
return creationTOs.stream()
.map(
operationTO -> new PeripheralOperation(
getObjectRepo().getObject(
Location.class,
operationTO.getLocationName()
).getReference(),
operationTO.getOperation(),
operationTO.getExecutionTrigger(),
operationTO.isCompletionRequired()
)
)
.collect(Collectors.toList());
}
/**
* Returns a list of {@link PointCreationTO Points} for all points in a model.
*
* @return A list of {@link PointCreationTO Points} for all points in a model.
*/
private List getPoints() {
Set points = getObjectRepo().getObjects(Point.class);
List result = new ArrayList<>();
for (Point curPoint : points) {
result.add(
new PointCreationTO(curPoint.getName())
.withPose(
new Pose(
curPoint.getPose().getPosition(),
curPoint.getPose().getOrientationAngle()
)
)
.withType(curPoint.getType())
.withVehicleEnvelopes(curPoint.getVehicleEnvelopes())
.withMaxVehicleBoundingBox(
new BoundingBoxCreationTO(
curPoint.getMaxVehicleBoundingBox().getLength(),
curPoint.getMaxVehicleBoundingBox().getWidth(),
curPoint.getMaxVehicleBoundingBox().getHeight()
)
.withReferenceOffset(
new CoupleCreationTO(
curPoint.getMaxVehicleBoundingBox().getReferenceOffset().getX(),
curPoint.getMaxVehicleBoundingBox().getReferenceOffset().getY()
)
)
)
.withProperties(curPoint.getProperties())
.withLayout(
new PointCreationTO.Layout(
curPoint.getLayout().getPosition(),
curPoint.getLayout().getLabelOffset(),
curPoint.getLayout().getLayerId()
)
)
);
}
return result;
}
/**
* Returns a list of {@link PathCreationTO Paths} for all paths in a model.
*
* @param model The model data.
* @return A list of {@link PathCreationTO Paths} for all paths in a model.
*/
private List getPaths() {
Set paths = getObjectRepo().getObjects(Path.class);
List result = new ArrayList<>();
for (Path curPath : paths) {
result.add(
new PathCreationTO(
curPath.getName(),
curPath.getSourcePoint().getName(),
curPath.getDestinationPoint().getName()
)
.withLength(curPath.getLength())
.withMaxVelocity(curPath.getMaxVelocity())
.withMaxReverseVelocity(curPath.getMaxReverseVelocity())
.withLocked(curPath.isLocked())
.withPeripheralOperations(getPeripheralOperations(curPath))
.withVehicleEnvelopes(curPath.getVehicleEnvelopes())
.withProperties(curPath.getProperties())
.withLayout(
new PathCreationTO.Layout(
curPath.getLayout().getConnectionType(),
curPath.getLayout().getControlPoints(),
curPath.getLayout().getLayerId()
)
)
);
}
return result;
}
private List getPeripheralOperations(Path path) {
return path.getPeripheralOperations().stream()
.map(
op -> new PeripheralOperationCreationTO(op.getOperation(), op.getLocation().getName())
.withExecutionTrigger(op.getExecutionTrigger())
.withCompletionRequired(op.isCompletionRequired())
)
.collect(Collectors.toList());
}
/**
* Returns a list of {@link VehicleCreationTO Vehicles} for all vehicles in a model.
*
* @param model The model data.
* @return A list of {@link VehicleCreationTO Vehicles} for all vehicles in a model.
*/
private List getVehicles() {
Set vehicles = getObjectRepo().getObjects(Vehicle.class);
List result = new ArrayList<>();
for (Vehicle vehicle : vehicles) {
result.add(
new VehicleCreationTO(vehicle.getName())
.withBoundingBox(
new BoundingBoxCreationTO(
vehicle.getBoundingBox().getLength(),
vehicle.getBoundingBox().getWidth(),
vehicle.getBoundingBox().getHeight()
)
.withReferenceOffset(
new CoupleCreationTO(
vehicle.getBoundingBox().getReferenceOffset().getX(),
vehicle.getBoundingBox().getReferenceOffset().getY()
)
)
)
.withEnergyLevelThresholdSet(
new VehicleCreationTO.EnergyLevelThresholdSet(
vehicle.getEnergyLevelThresholdSet().getEnergyLevelCritical(),
vehicle.getEnergyLevelThresholdSet().getEnergyLevelGood(),
vehicle.getEnergyLevelThresholdSet().getEnergyLevelSufficientlyRecharged(),
vehicle.getEnergyLevelThresholdSet().getEnergyLevelFullyRecharged()
)
)
.withMaxVelocity(vehicle.getMaxVelocity())
.withMaxReverseVelocity(vehicle.getMaxReverseVelocity())
.withEnvelopeKey(vehicle.getEnvelopeKey())
.withProperties(vehicle.getProperties())
.withLayout(new VehicleCreationTO.Layout(vehicle.getLayout().getRouteColor()))
);
}
return result;
}
/**
* Returns a list of {@link LocationTypeCreationTO LocationTypes} for all location types in a
* model.
*
* @param model The model data.
* @return A list of {@link LocationTypeCreationTO LocationTypes} for all location types in a
* model.
*/
private List getLocationTypes() {
Set locTypes = getObjectRepo().getObjects(LocationType.class);
List result = new ArrayList<>();
for (LocationType curType : locTypes) {
result.add(
new LocationTypeCreationTO(curType.getName())
.withAllowedOperations(curType.getAllowedOperations())
.withAllowedPeripheralOperations(curType.getAllowedPeripheralOperations())
.withProperties(curType.getProperties())
.withLayout(
new LocationTypeCreationTO.Layout(curType.getLayout().getLocationRepresentation())
)
);
}
return result;
}
/**
* Returns a list of {@link LocationCreationTO Locations} for all locations in a model.
*
* @param model The model data.
* @return A list of {@link LocationCreationTO Locations} for all locations in a model.
*/
private List getLocations() {
Set locations = getObjectRepo().getObjects(Location.class);
List result = new ArrayList<>();
for (Location curLoc : locations) {
result.add(
new LocationCreationTO(
curLoc.getName(),
curLoc.getType().getName(),
curLoc.getPosition()
)
.withLinks(
curLoc.getAttachedLinks().stream()
.collect(
Collectors.toMap(
link -> link.getPoint().getName(),
Location.Link::getAllowedOperations
)
)
)
.withLocked(curLoc.isLocked())
.withProperties(curLoc.getProperties())
.withLayout(
new LocationCreationTO.Layout(
curLoc.getLayout().getPosition(),
curLoc.getLayout().getLabelOffset(),
curLoc.getLayout().getLocationRepresentation(),
curLoc.getLayout().getLayerId()
)
)
);
}
return result;
}
/**
* Returns a list of {@link BlockCreationTO Blocks} for all blocks in a model.
*
* @param model The model data.
* @return A list of {@link BlockCreationTO Blocks} for all blocks in a model.
*/
private List getBlocks() {
Set blocks = getObjectRepo().getObjects(Block.class);
List result = new ArrayList<>();
for (Block curBlock : blocks) {
result.add(
new BlockCreationTO(curBlock.getName())
.withMemberNames(
curBlock.getMembers().stream()
.map(member -> member.getName())
.collect(Collectors.toSet())
)
.withType(curBlock.getType())
.withProperties(curBlock.getProperties())
.withLayout(new BlockCreationTO.Layout(curBlock.getLayout().getColor()))
);
}
return result;
}
/**
* Returns a {@link VisualLayoutCreationTO} for the visual layouts in a model.
*
* @param model The model data.
* @return A {@link VisualLayoutCreationTO} for the visual layouts in a model.
*/
private VisualLayoutCreationTO getVisualLayout() {
Set layouts = getObjectRepo().getObjects(VisualLayout.class);
checkState(
layouts.size() == 1,
"There has to be one, and only one, visual layout. Number of visual layouts: %d",
layouts.size()
);
VisualLayout layout = layouts.iterator().next();
return new VisualLayoutCreationTO(layout.getName())
.withScaleX(layout.getScaleX())
.withScaleY(layout.getScaleY())
.withProperties(layout.getProperties())
.withLayers(layout.getLayers())
.withLayerGroups(layout.getLayerGroups());
}
/**
* Creates a new visual layout with a unique name and all other attributes set
* to default values.
*
* @param to The transfer object from which to create the new layout.
* @return The newly created layout.
* @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.
*/
private VisualLayout createVisualLayout(VisualLayoutCreationTO to)
throws ObjectUnknownException,
ObjectExistsException {
VisualLayout newLayout = new VisualLayout(to.getName())
.withScaleX(to.getScaleX())
.withScaleY(to.getScaleY())
.withLayers(to.getLayers())
.withLayerGroups(to.getLayerGroups());
getObjectRepo().addObject(newLayout);
emitObjectEvent(
newLayout,
null,
TCSObjectEvent.Type.OBJECT_CREATED
);
// Return the newly created layout.
return newLayout;
}
/**
* Creates a new point with a unique name and all other attributes set to
* default values.
*
* @param to The transfer object from which to create the new point.
* @return The newly created point.
* @throws ObjectExistsException If an object with the point's name already exists.
*/
private Point createPoint(PointCreationTO to)
throws ObjectExistsException {
// Get a unique ID for the new point and create an instance.
Point newPoint = new Point(to.getName())
.withPose(new Pose(to.getPose().getPosition(), to.getPose().getOrientationAngle()))
.withType(to.getType())
.withVehicleEnvelopes(to.getVehicleEnvelopes())
.withMaxVehicleBoundingBox(
new BoundingBox(
to.getMaxVehicleBoundingBox().getLength(),
to.getMaxVehicleBoundingBox().getWidth(),
to.getMaxVehicleBoundingBox().getHeight()
)
.withReferenceOffset(
new Couple(
to.getMaxVehicleBoundingBox().getReferenceOffset().getX(),
to.getMaxVehicleBoundingBox().getReferenceOffset().getY()
)
)
)
.withProperties(to.getProperties())
.withLayout(
new Point.Layout(
to.getLayout().getPosition(),
to.getLayout().getLabelOffset(),
to.getLayout().getLayerId()
)
);
getObjectRepo().addObject(newPoint);
emitObjectEvent(newPoint, null, TCSObjectEvent.Type.OBJECT_CREATED);
// Return the newly created point.
return newPoint;
}
/**
* Creates a new path from the given transfer object.
*
* @param to The transfer object from which to create the new path.
* @return The newly created path.
* @throws ObjectUnknownException If the referenced point does not exist.
* @throws ObjectExistsException If an object with the same name as the path already exists.
*/
private Path createPath(PathCreationTO to)
throws ObjectUnknownException,
ObjectExistsException {
requireNonNull(to, "to");
Point srcPoint = getObjectRepo().getObject(Point.class, to.getSrcPointName());
Point destPoint = getObjectRepo().getObject(Point.class, to.getDestPointName());
Path newPath = new Path(
to.getName(),
srcPoint.getReference(),
destPoint.getReference()
)
.withLength(to.getLength())
.withMaxVelocity(to.getMaxVelocity())
.withMaxReverseVelocity(to.getMaxReverseVelocity())
.withPeripheralOperations(mapPeripheralOperationTOs(to.getPeripheralOperations()))
.withVehicleEnvelopes(to.getVehicleEnvelopes())
.withProperties(to.getProperties())
.withLocked(to.isLocked())
.withLayout(
new Path.Layout(
to.getLayout().getConnectionType(),
to.getLayout().getControlPoints(),
to.getLayout().getLayerId()
)
);
getObjectRepo().addObject(newPath);
emitObjectEvent(
newPath,
null,
TCSObjectEvent.Type.OBJECT_CREATED
);
addPointOutgoingPath(srcPoint.getReference(), newPath.getReference());
addPointIncomingPath(destPoint.getReference(), newPath.getReference());
return newPath;
}
/**
* Creates a new location type with a unique name and all other attributes set
* to their default values.
*
* @param to The transfer object from which to create the new location type.
* @return The newly created location type.
* @throws ObjectExistsException If an object with the new object's name already exists.
*/
private LocationType createLocationType(LocationTypeCreationTO to)
throws ObjectExistsException {
LocationType newType = new LocationType(to.getName())
.withAllowedOperations(to.getAllowedOperations())
.withAllowedPeripheralOperations(to.getAllowedPeripheralOperations())
.withProperties(to.getProperties())
.withLayout(new LocationType.Layout(to.getLayout().getLocationRepresentation()));
getObjectRepo().addObject(newType);
emitObjectEvent(
newType,
null,
TCSObjectEvent.Type.OBJECT_CREATED
);
return newType;
}
/**
* Creates a new location with a unique name and all other attributes set to
* default values.
*
* @param to The transfer object from which to create the new location type.
* @return The newly created location.
* @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.
*/
private Location createLocation(LocationCreationTO to)
throws ObjectUnknownException,
ObjectExistsException {
LocationType type = getObjectRepo().getObject(LocationType.class, to.getTypeName());
Location newLocation = new Location(to.getName(), type.getReference())
.withPosition(to.getPosition())
.withLocked(to.isLocked())
.withProperties(to.getProperties())
.withLayout(
new Location.Layout(
to.getLayout().getPosition(),
to.getLayout().getLabelOffset(),
to.getLayout().getLocationRepresentation(),
to.getLayout().getLayerId()
)
);
Set locationLinks = new HashSet<>();
for (Map.Entry> linkEntry : to.getLinks().entrySet()) {
Point point = getObjectRepo().getObject(Point.class, linkEntry.getKey());
Location.Link link = new Location.Link(newLocation.getReference(), point.getReference())
.withAllowedOperations(linkEntry.getValue());
locationLinks.add(link);
}
newLocation = newLocation.withAttachedLinks(locationLinks);
getObjectRepo().addObject(newLocation);
emitObjectEvent(
newLocation,
null,
TCSObjectEvent.Type.OBJECT_CREATED
);
// Add the location's links to the respective points, too.
for (Location.Link link : locationLinks) {
Point point = getObjectRepo().getObject(Point.class, link.getPoint());
Set pointLinks = new HashSet<>(point.getAttachedLinks());
pointLinks.add(link);
Point previousPointState = point;
point = point.withAttachedLinks(pointLinks);
getObjectRepo().replaceObject(point);
emitObjectEvent(
point,
previousPointState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
}
return newLocation;
}
/**
* Creates a new vehicle with a unique name and all other attributes set to
* their default values.
*
* @param to The transfer object from which to create the new group.
* @return The newly created group.
* @throws ObjectExistsException If an object with the new object's name already exists.
*/
private Vehicle createVehicle(VehicleCreationTO to)
throws ObjectExistsException {
Vehicle newVehicle = new Vehicle(to.getName())
.withBoundingBox(
new BoundingBox(
to.getBoundingBox().getLength(),
to.getBoundingBox().getWidth(),
to.getBoundingBox().getHeight()
)
.withReferenceOffset(
new Couple(
to.getBoundingBox().getReferenceOffset().getX(),
to.getBoundingBox().getReferenceOffset().getY()
)
)
)
.withEnergyLevelThresholdSet(
new EnergyLevelThresholdSet(
to.getEnergyLevelThresholdSet().getEnergyLevelCritical(),
to.getEnergyLevelThresholdSet().getEnergyLevelGood(),
to.getEnergyLevelThresholdSet().getEnergyLevelSufficientlyRecharged(),
to.getEnergyLevelThresholdSet().getEnergyLevelFullyRecharged()
)
)
.withMaxVelocity(to.getMaxVelocity())
.withMaxReverseVelocity(to.getMaxReverseVelocity())
.withEnvelopeKey(to.getEnvelopeKey())
.withProperties(to.getProperties())
.withLayout(new Vehicle.Layout(to.getLayout().getRouteColor()));
getObjectRepo().addObject(newVehicle);
emitObjectEvent(
newVehicle,
null,
TCSObjectEvent.Type.OBJECT_CREATED
);
return newVehicle;
}
/**
* Creates a new block with a unique name and all other attributes set to
* default values.
*
* @param to The transfer object from which to create the new block.
* @return The newly created block.
* @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.
*/
private Block createBlock(BlockCreationTO to)
throws ObjectExistsException,
ObjectUnknownException {
Set> members = new HashSet<>();
for (String memberName : to.getMemberNames()) {
TCSObject> object = getObjectRepo().getObject(memberName);
if (!(object instanceof TCSResource)) {
throw new ObjectUnknownException(memberName);
}
members.add(((TCSResource) object).getReference());
}
Block newBlock = new Block(to.getName())
.withType(to.getType())
.withMembers(members)
.withProperties(to.getProperties())
.withLayout(new Block.Layout(to.getLayout().getColor()));
getObjectRepo().addObject(newBlock);
emitObjectEvent(
newBlock,
null,
TCSObjectEvent.Type.OBJECT_CREATED
);
// Return the newly created block.
return newBlock;
}
/**
* Adds an incoming path to a point.
*
* @param pointRef A reference to the point to be modified.
* @param pathRef A reference to the path.
* @return The modified point.
* @throws ObjectUnknownException If the referenced point or path do not
* exist.
*/
private Point addPointIncomingPath(
TCSObjectReference pointRef,
TCSObjectReference pathRef
)
throws ObjectUnknownException {
Point point = getObjectRepo().getObject(Point.class, pointRef);
Path path = getObjectRepo().getObject(Path.class, pathRef);
// Check if the point really is the path's destination point.
if (!path.getDestinationPoint().equals(point.getReference())) {
throw new IllegalArgumentException("Point is not the path's destination.");
}
Path previousState = path;
Set> incomingPaths = new HashSet<>(point.getIncomingPaths());
incomingPaths.add(path.getReference());
point = point.withIncomingPaths(incomingPaths);
getObjectRepo().replaceObject(point);
emitObjectEvent(
point,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return point;
}
/**
* Adds an outgoing path to a point.
*
* @param pointRef A reference to the point to be modified.
* @param pathRef A reference to the path.
* @return The modified point.
* @throws ObjectUnknownException If the referenced point or path do not
* exist.
*/
private Point addPointOutgoingPath(
TCSObjectReference pointRef,
TCSObjectReference pathRef
)
throws ObjectUnknownException {
Point point = getObjectRepo().getObject(Point.class, pointRef);
Path path = getObjectRepo().getObject(Path.class, pathRef);
// Check if the point really is the path's source.
if (!path.getSourcePoint().equals(point.getReference())) {
throw new IllegalArgumentException("Point is not the path's source.");
}
Path previousState = path;
Set> outgoingPaths = new HashSet<>(point.getOutgoingPaths());
outgoingPaths.add(path.getReference());
point = point.withOutgoingPaths(outgoingPaths);
getObjectRepo().replaceObject(point);
emitObjectEvent(
point,
previousState,
TCSObjectEvent.Type.OBJECT_MODIFIED
);
return point;
}
private static List>> unmodifiableCopy(
List>> resources
) {
List>> result = new ArrayList<>();
for (Set> resSet : resources) {
result.add(Set.copyOf(resSet));
}
return Collections.unmodifiableList(result);
}
}