
org.opentcs.drivers.vehicle.VehicleProcessModel Maven / Gradle / Ivy
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.drivers.vehicle;
import static java.util.Objects.requireNonNull;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import org.opentcs.data.TCSObjectReference;
import org.opentcs.data.model.BoundingBox;
import org.opentcs.data.model.Pose;
import org.opentcs.data.model.Triple;
import org.opentcs.data.model.Vehicle;
import org.opentcs.data.notification.UserNotification;
import org.opentcs.util.annotations.ScheduledApiChange;
/**
* An observable model of a vehicle's and its comm adapter's attributes.
*/
public class VehicleProcessModel {
/**
* The maximum number of notifications we want to keep.
*/
private static final int MAX_NOTIFICATION_COUNT = 100;
/**
* A copy of the kernel's Vehicle instance.
*/
private final Vehicle vehicle;
/**
* A reference to the vehicle.
*/
private final TCSObjectReference vehicleReference;
/**
* Used for implementing property change events.
*/
@SuppressWarnings("this-escape")
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
/**
* The vehicle properties set by the driver.
* (I.e. this map does not contain properties/values set by any other components!)
*/
private final Map vehicleProperties = new HashMap<>();
/**
* The transport order properties set by the driver.
* (I.e. this map does not contain properties/values set by any other components!)
*/
private final Map transportOrderProperties = new HashMap<>();
/**
* Whether the comm adapter is currently enabled.
*/
private boolean commAdapterEnabled;
/**
* Whether the comm adapter is currently connected to the vehicle.
*/
private boolean commAdapterConnected;
/**
* The name of the vehicle's current position.
*/
private String position;
/**
* User notifications published by the comm adapter.
*/
private final Queue notifications = new ArrayDeque<>();
/**
* The vehicle's pose.
*/
private Pose pose = new Pose(null, Double.NaN);
/**
* The vehicle's energy level.
*/
private int energyLevel = 100;
/**
* The vehicle's load handling devices (state).
*/
private List loadHandlingDevices = new ArrayList<>();
/**
* The vehicle's current state.
*/
private Vehicle.State state = Vehicle.State.UNKNOWN;
/**
* The vehicle's current bounding box.
*/
private BoundingBox boundingBox;
/**
* Creates a new instance.
*
* @param attachedVehicle The vehicle attached to the new instance.
*/
public VehicleProcessModel(
@Nonnull
Vehicle attachedVehicle
) {
this.vehicle = requireNonNull(attachedVehicle, "attachedVehicle");
this.vehicleReference = vehicle.getReference();
this.boundingBox = vehicle.getBoundingBox();
}
/**
* Registers a new property change listener with this model.
*
* @param listener The listener to be registered.
*/
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
/**
* Unregisters a property change listener from this model.
*
* @param listener The listener to be unregistered.
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
}
/**
* Returns a reference to the vehicle.
*
* @return A reference to the vehicle.
*/
@Nonnull
public TCSObjectReference getReference() {
return vehicleReference;
}
/**
* Returns the vehicle's name.
*
* @return The vehicle's name.
*/
@Nonnull
public String getName() {
return vehicleReference.getName();
}
/**
* Returns user notifications published by the comm adapter.
*
* @return The notifications.
*/
@Nonnull
public Queue getNotifications() {
return notifications;
}
/**
* Publishes an user notification.
*
* @param notification The notification to be published.
*/
public void publishUserNotification(
@Nonnull
UserNotification notification
) {
requireNonNull(notification, "notification");
notifications.add(notification);
while (notifications.size() > MAX_NOTIFICATION_COUNT) {
notifications.remove();
}
getPropertyChangeSupport().firePropertyChange(
Attribute.USER_NOTIFICATION.name(),
null,
notification
);
}
/**
* Publishes an event via the kernel's event mechanism.
*
* @param event The event to be published.
*/
public void publishEvent(
@Nonnull
VehicleCommAdapterEvent event
) {
requireNonNull(event, "event");
getPropertyChangeSupport().firePropertyChange(
Attribute.COMM_ADAPTER_EVENT.name(),
null,
event
);
}
/**
* Indicates whether the comm adapter is currently enabled or not.
*
* @return true
if, and only if, the comm adapter is currently enabled.
*/
public boolean isCommAdapterEnabled() {
return commAdapterEnabled;
}
/**
* Sets the comm adapter's enabled flag.
*
* @param commAdapterEnabled The new value.
*/
public void setCommAdapterEnabled(boolean commAdapterEnabled) {
boolean oldValue = this.commAdapterEnabled;
this.commAdapterEnabled = commAdapterEnabled;
getPropertyChangeSupport().firePropertyChange(
Attribute.COMM_ADAPTER_ENABLED.name(),
oldValue,
commAdapterEnabled
);
}
/**
* Indicates whether the comm adapter is currently connected or not.
*
* @return true
if, and only if, the comm adapter is currently connected.
*/
public boolean isCommAdapterConnected() {
return commAdapterConnected;
}
/**
* Sets the comm adapter's connected flag.
*
* @param commAdapterConnected The new value.
*/
public void setCommAdapterConnected(boolean commAdapterConnected) {
boolean oldValue = this.commAdapterConnected;
this.commAdapterConnected = commAdapterConnected;
getPropertyChangeSupport().firePropertyChange(
Attribute.COMM_ADAPTER_CONNECTED.name(),
oldValue,
commAdapterConnected
);
}
/**
* Returns the vehicle's current position.
*
* @return The position.
*/
@Nullable
public String getPosition() {
return position;
}
/**
* Updates the vehicle's current position.
*
* @param position The new position
*/
public void setPosition(
@Nullable
String position
) {
// Otherwise update the position, notify listeners and let the kernel know.
String oldValue = this.position;
this.position = position;
getPropertyChangeSupport().firePropertyChange(
Attribute.POSITION.name(),
oldValue,
position
);
}
/**
* Returns the vehicle's precise position.
*
* @return The vehicle's precise position.
* @deprecated Use {@link #getPose()} instead.
*/
@Deprecated
@ScheduledApiChange(when = "7.0", details = "Will be removed.")
@Nullable
public Triple getPrecisePosition() {
return pose.getPosition();
}
/**
* Sets the vehicle's precise position.
*
* @param position The new position.
* @deprecated Use {@link #setPose(Pose)}} instead.
*/
@Deprecated
@ScheduledApiChange(when = "7.0", details = "Will be removed.")
public void setPrecisePosition(
@Nullable
Triple position
) {
setPose(pose.withPosition(position));
}
/**
* Returns the vehicle's current orientation angle.
*
* @return The vehicle's current orientation angle.
* @see Vehicle#getOrientationAngle()
* @deprecated Use {@link #getPose()} instead.
*/
@Deprecated
@ScheduledApiChange(when = "7.0", details = "Will be removed.")
public double getOrientationAngle() {
return pose.getOrientationAngle();
}
/**
* Sets the vehicle's current orientation angle.
*
* @param angle The new angle
* @deprecated Use {@link #setPose(Pose)} instead.
*/
@Deprecated
@ScheduledApiChange(when = "7.0", details = "Will be removed.")
public void setOrientationAngle(double angle) {
setPose(pose.withOrientationAngle(angle));
}
/**
* Returns the vehicle's pose.
*
* @return The vehicle's pose.
*/
@Nonnull
public Pose getPose() {
return pose;
}
/**
* Sets the vehicle's pose.
*
* @param pose The new pose
*/
public void setPose(
@Nonnull
Pose pose
) {
requireNonNull(pose, "pose");
Pose oldPose = this.pose;
this.pose = pose;
getPropertyChangeSupport().firePropertyChange(
Attribute.POSE.name(),
oldPose,
pose
);
getPropertyChangeSupport().firePropertyChange(
Attribute.PRECISE_POSITION.name(),
oldPose.getPosition(),
pose.getPosition()
);
getPropertyChangeSupport().firePropertyChange(
Attribute.ORIENTATION_ANGLE.name(),
oldPose.getOrientationAngle(),
pose.getOrientationAngle()
);
}
/**
* Returns the vehicle's current energy level.
*
* @return The vehicle's current energy level.
*/
public int getEnergyLevel() {
return energyLevel;
}
/**
* Sets the vehicle's current energy level.
*
* @param newLevel The new level.
*/
public void setEnergyLevel(int newLevel) {
int oldValue = this.energyLevel;
this.energyLevel = newLevel;
getPropertyChangeSupport().firePropertyChange(
Attribute.ENERGY_LEVEL.name(),
oldValue,
newLevel
);
}
/**
* Returns the vehicle's load handling devices.
*
* @return The vehicle's load handling devices.
*/
@Nonnull
public List getLoadHandlingDevices() {
return loadHandlingDevices;
}
/**
* Sets the vehicle's load handling devices.
*
* @param devices The new devices
*/
public void setLoadHandlingDevices(
@Nonnull
List devices
) {
List devs = new ArrayList<>(devices);
List oldValue = this.loadHandlingDevices;
this.loadHandlingDevices = devs;
getPropertyChangeSupport().firePropertyChange(
Attribute.LOAD_HANDLING_DEVICES.name(),
oldValue,
devs
);
}
/**
* Sets a property of the vehicle.
*
* @param key The property's key.
* @param value The property's new value.
*/
public void setProperty(
@Nonnull
String key,
@Nullable
String value
) {
requireNonNull(key, "key");
// Check whether the new value is the same as the last one we set. If yes, ignore the update,
// as it would cause unnecessary churn in the kernel.
// Note that this assumes that other components do not modify properties set by this driver.
String oldValue = vehicleProperties.get(key);
if (Objects.equals(value, oldValue)) {
return;
}
vehicleProperties.put(key, value);
getPropertyChangeSupport().firePropertyChange(
Attribute.VEHICLE_PROPERTY.name(),
null,
new VehiclePropertyUpdate(key, value)
);
}
/**
* Returns the vehicle's current state.
*
* @return The state
*/
@Nonnull
public Vehicle.State getState() {
return state;
}
/**
* Sets the vehicle's current state.
*
* @param newState The new state
*/
public void setState(
@Nonnull
Vehicle.State newState
) {
Vehicle.State oldState = this.state;
this.state = newState;
getPropertyChangeSupport().firePropertyChange(Attribute.STATE.name(), oldState, newState);
if (oldState != Vehicle.State.ERROR && newState == Vehicle.State.ERROR) {
publishUserNotification(
new UserNotification(
getName(),
"Vehicle state changed to ERROR",
UserNotification.Level.NOTEWORTHY
)
);
}
else if (oldState == Vehicle.State.ERROR && newState != Vehicle.State.ERROR) {
publishUserNotification(
new UserNotification(
getName(),
"Vehicle state is no longer ERROR",
UserNotification.Level.NOTEWORTHY
)
);
}
}
/**
* Returns the vehicle's current length.
*
* @return The vehicle's current length.
* @deprecated Use {@link #getBoundingBox()} instead.
*/
@Deprecated
@ScheduledApiChange(when = "7.0", details = "Will be removed.")
public int getLength() {
return (int) boundingBox.getLength();
}
/**
* Sets the vehicle's current length.
*
* @param length The new length.
* @deprecated Use {@link #setBoundingBox(BoundingBox)} instead.
*/
@Deprecated
@ScheduledApiChange(when = "7.0", details = "Will be removed.")
public void setLength(int length) {
setBoundingBox(getBoundingBox().withLength(length));
}
/**
* Returns the vehicle's current bounding box.
*
* @return The vehicle's current bounding box.
*/
@Nonnull
public BoundingBox getBoundingBox() {
return boundingBox;
}
/**
* Sets the vehicle's current bounding box.
*
* @param boundingBox The new bounding box.
*/
public void setBoundingBox(
@Nonnull
BoundingBox boundingBox
) {
requireNonNull(boundingBox, "boundingBox");
BoundingBox oldValue = this.boundingBox;
this.boundingBox = boundingBox;
getPropertyChangeSupport().firePropertyChange(
Attribute.BOUNDING_BOX.name(),
oldValue,
boundingBox
);
getPropertyChangeSupport().firePropertyChange(
Attribute.LENGTH.name(),
oldValue.getLength(),
boundingBox.getLength()
);
}
/**
* Sets a property of the transport order the vehicle is currently processing.
*
* @param key The property's key.
* @param value The property's new value.
*/
public void setTransportOrderProperty(
@Nonnull
String key,
@Nullable
String value
) {
requireNonNull(key, "key");
// Check whether the new value is the same as the last one we set. If yes, ignore the update,
// as it would cause unnecessary churn in the kernel.
// Note that this assumes that other components do not modify properties set by this driver.
String oldValue = transportOrderProperties.get(key);
if (Objects.equals(value, oldValue)) {
return;
}
transportOrderProperties.put(key, value);
getPropertyChangeSupport().firePropertyChange(
Attribute.TRANSPORT_ORDER_PROPERTY.name(),
null,
new TransportOrderPropertyUpdate(key, value)
);
}
/**
* Notifies observers that the given command has been added to the comm adapter's command queue.
*
* @param enqueuedCommand The command that has been added to the queue.
*/
public void commandEnqueued(
@Nonnull
MovementCommand enqueuedCommand
) {
getPropertyChangeSupport().firePropertyChange(
Attribute.COMMAND_ENQUEUED.name(),
null,
enqueuedCommand
);
}
/**
* Notifies observers that the given command has been sent to the associated vehicle.
*
* @param sentCommand The command that has been sent to the vehicle.
*/
public void commandSent(
@Nonnull
MovementCommand sentCommand
) {
getPropertyChangeSupport().firePropertyChange(
Attribute.COMMAND_SENT.name(),
null,
sentCommand
);
}
/**
* Notifies observers that the given command has been executed by the comm adapter/vehicle.
*
* @param executedCommand The command that has been executed.
*/
public void commandExecuted(
@Nonnull
MovementCommand executedCommand
) {
getPropertyChangeSupport().firePropertyChange(
Attribute.COMMAND_EXECUTED.name(),
null,
executedCommand
);
}
/**
* Notifies observers that the given command could not be executed by the comm adapter/vehicle.
*
* @param failedCommand The command that could not be executed.
*/
public void commandFailed(
@Nonnull
MovementCommand failedCommand
) {
getPropertyChangeSupport().firePropertyChange(
Attribute.COMMAND_FAILED.name(),
null,
failedCommand
);
}
/**
* Notifies observers that the vehicle would like to have its integration level changed.
*
* @param level The integration level to change to.
*/
public void integrationLevelChangeRequested(
@Nonnull
Vehicle.IntegrationLevel level
) {
getPropertyChangeSupport().firePropertyChange(
Attribute.INTEGRATION_LEVEL_CHANGE_REQUESTED.name(),
null,
level
);
}
/**
* Notifies observers that the vehicle would like to have its current transport order withdrawn.
*
* @param forced Whether a forced withdrawal is requested.
*/
public void transportOrderWithdrawalRequested(boolean forced) {
getPropertyChangeSupport().firePropertyChange(
Attribute.TRANSPORT_ORDER_WITHDRAWAL_REQUESTED.name(),
null,
forced
);
}
protected PropertyChangeSupport getPropertyChangeSupport() {
return pcs;
}
/**
* A notification object sent to observers to indicate a change of a property.
*/
public static class PropertyUpdate {
/**
* The property's key.
*/
private final String key;
/**
* The property's new value.
*/
private final String value;
/**
* Creates a new instance.
*
* @param key The key.
* @param value The new value.
*/
public PropertyUpdate(String key, String value) {
this.key = requireNonNull(key, "key");
this.value = value;
}
/**
* Returns the property's key.
*
* @return The property's key.
*/
public String getKey() {
return key;
}
/**
* Returns the property's new value.
*
* @return The property's new value.
*/
public String getValue() {
return value;
}
}
/**
* A notification object sent to observers to indicate a change of a vehicle's property.
*/
public static class VehiclePropertyUpdate
extends
PropertyUpdate {
/**
* Creates a new instance.
*
* @param key The property's key.
* @param value The new value.
*/
public VehiclePropertyUpdate(String key, String value) {
super(key, value);
}
}
/**
* A notification object sent to observers to indicate a change of a transport order's property.
*/
public static class TransportOrderPropertyUpdate
extends
PropertyUpdate {
/**
* Creates a new instance.
*
* @param key The property's key.
* @param value The new value.
*/
public TransportOrderPropertyUpdate(String key, String value) {
super(key, value);
}
}
/**
* Notification arguments to indicate some change.
*/
public enum Attribute {
/**
* Indicates a change of the comm adapter's enabled setting.
*/
COMM_ADAPTER_ENABLED,
/**
* Indicates a change of the comm adapter's connected setting.
*/
COMM_ADAPTER_CONNECTED,
/**
* Indicates a change of the vehicle's position.
*/
POSITION,
/**
* Indicates a change of the vehicle's precise position.
*/
@Deprecated
@ScheduledApiChange(when = "7.0", details = "Will be removed.")
PRECISE_POSITION,
/**
* Indicates a change of the vehicle's orientation angle.
*/
@Deprecated
@ScheduledApiChange(when = "7.0", details = "Will be removed.")
ORIENTATION_ANGLE,
/**
* Indicates a change of the vehicle's pose.
*/
POSE,
/**
* Indicates a change of the vehicle's energy level.
*/
ENERGY_LEVEL,
/**
* Indicates a change of the vehicle's load handling devices.
*/
LOAD_HANDLING_DEVICES,
/**
* Indicates a change of the vehicle's state.
*/
STATE,
/**
* Indicates a change of the vehicle's length.
*/
@Deprecated
@ScheduledApiChange(when = "7.0", details = "Will be removed.")
LENGTH,
/**
* Indicates a change of the vehicle's bounding box.
*/
BOUNDING_BOX,
/**
* Indicates a new user notification was published.
*/
USER_NOTIFICATION,
/**
* Indicates a new comm adapter event was published.
*/
COMM_ADAPTER_EVENT,
/**
* Indicates a command was enqueued.
*/
COMMAND_ENQUEUED,
/**
* Indicates a command was sent.
*/
COMMAND_SENT,
/**
* Indicates a command was executed successfully.
*/
COMMAND_EXECUTED,
/**
* Indicates a command failed.
*/
COMMAND_FAILED,
/**
* Indicates a change of a vehicle property.
*/
VEHICLE_PROPERTY,
/**
* Indicates a change of a transport order property.
*/
TRANSPORT_ORDER_PROPERTY,
/**
* Indicates a request to change the integration level of the vehicle.
*/
INTEGRATION_LEVEL_CHANGE_REQUESTED,
/**
* Indicates a request to withdraw the vehicles current transport order.
*/
TRANSPORT_ORDER_WITHDRAWAL_REQUESTED;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy