vda-5050-lib
Version:
Universal VDA 5050 library for Node.js and browsers
556 lines (555 loc) • 22.9 kB
TypeScript
/*! Copyright (c) 2021 Siemens AG. Licensed under the MIT License. */
import { Action, ActionContext, ActionScope, ActionStatus, AgvAdapter, AgvAdapterDebugger, AgvAdapterOptions, AgvController, AgvPosition, AttachContext, BatteryState, DetachContext, Edge, ErrorReference, Headerless, Load, Node, OperatingMode, RouteTraversableContext, SafetyStatus, State, StopTraverseContext, TraverseEdgeContext, Velocity } from "..";
/**
* Represents the internal vehicle state of a virtual AGV.
*
* @category AGV Adapter
*/
export interface VirtualAgvState {
isDriving: boolean;
isPaused: boolean;
position: AgvPosition & {
lastNodeId: string;
};
velocity: Velocity;
batteryState: BatteryState;
safetyState: SafetyStatus;
operatingMode: OperatingMode;
currentLoad: Load;
}
/**
* Defines all possible states and state transitions of a node, edge, or
* instant action supported by a virtual AGV.
*
* @category AGV Adapter
*/
export interface VirtualActionDefinition {
/**
* Type of action.
*/
actionType: string;
/**
* Valid scopes of the action, any combination of `"instant"`, `"node"`, or
* `"edge"`.
*/
actionScopes: ActionScope | ActionScope[];
/**
* Defines constraint functions for action parameters (optional).
*
* To constraint the value of a specific action parameter key-value pair,
* specify a function that returns `true` if the parameter's actual value is
* valid; `false` otherwise. If the action parameter key is not specified,
* `undefined` is passed as action parameter value to the constraint
* function.
*
* An action is only executable if all the specified action parameter
* constraints are satified.
*/
actionParameterConstraints?: {
[actionParameterKey: string]: (actionParameterValue: string | number | boolean | any[], scope: ActionScope, allActionParams: {
[actionParameterKey: string]: string | number | boolean | any[];
}) => boolean;
};
/**
* Defines a function that is invoked to check whether a given action /
* scope is executable in the context of an active order, if any (optional).
*
* Returns an error description string if action is not executable;
* `undefined` or empty string otherwise.
*
* @remarks This check is performed immediately before the action is
* executed by the adapter, so it can take the current vehicle state into
* account.
*/
actionExecutable?: (action: Action, scope: ActionScope, activeOrderId: string) => string;
/**
* Defines all possible states and transitions of an action.
*/
transitions: VirtualActionTransitions;
}
/**
* A specification format to define all possible states of an action, its
* transitions, and side effects.
*
* @remarks The action state PAUSED is not part of the format. State transitions
* from/to this state are handled internally by the adapter.
*
* @category AGV Adapter
*/
export type VirtualActionTransitions = {
/**
* Defines the initial state (mandatory for all actions).
*/
ON_INIT: {
/**
* The initial status to transition to when the action is being
* executed.
*
* @remarks This transition must always be present. Value must be
* INITIALIZING or RUNNING for node and edge actions. Value must be
* INITIALIZING, RUNNING, or FINISHED for instant actions.
*/
next: ActionStatus.Initializing | ActionStatus.Running | ActionStatus.Finished;
};
/**
* Define status change information for a node or edge action that can be
* canceled by interrupting an initializing, running or paused action (for
* interruptable node and edge actions only).
*
* After cancelation the action transitions to status FAILED automatically.
*/
ON_CANCEL?: {
/**
* Specifies a function to return a partial state that must be updated
* when the action is canceled (optional).
*/
linkedState?: (context: ActionContext) => Partial<Headerless<State>>;
};
/**
* Define status change information for an edge action to be terminated (for
* edge actions only, mandatory for edge actions).
*/
ON_TERMINATE?: {
/**
* The next status to transition to after an edge action has been
* terminated.
*/
next: ActionStatus.Finished | ActionStatus.Failed;
/**
* Specifies a function to return a partial state that must be updated
* when the edge action is terminated (optional).
*/
linkedState?: (context: ActionContext) => Partial<Headerless<State>>;
};
/**
* Defines INITIALIZING action status (optional for node, edge, and instant
* actions).
*/
[ActionStatus.Initializing]?: {
/**
* Time in seconds to stay in this status.
*
* Specify either a number of seconds or a tuple with the name of the
* action parameter whose numeric value should be taken and a default
* value to be taken if this action parameter is not existing.
*/
durationTime: number | [string, number];
/**
* The next status to transition to after the duration time elapses.
*/
next: ActionStatus.Paused | ActionStatus.Running | ActionStatus.Failed;
/**
* Specifies a function to return a partial state to be updated when
* this status is entered (optional).
*/
linkedState?: (context: ActionContext) => Partial<Headerless<State>>;
};
/**
* Defines RUNNING action status (mandatory for node and edge actions,
* optional for instant actions).
*/
[ActionStatus.Running]?: {
/**
* Time in seconds to stay in this status.
*
* Specify either a number of seconds or a tuple with the name of the
* action parameter whose numeric value should be taken and a default
* value to be taken if this action parameter is not existing.
*/
durationTime: number | [string, number];
/**
* The next status to transition to after the duration time elapses.
*/
next: ActionStatus.Paused | ActionStatus.Finished | ActionStatus.Failed;
/**
* Specifies a function to return a partial state to be updated when
* this status is entered (optional).
*/
linkedState?: (context: ActionContext) => Partial<Headerless<State>>;
};
/**
* Defines FINISHED action status.
*/
[ActionStatus.Finished]: {
/**
* A result reported by invoking the given function in FINISHED action
* status.
*/
resultDescription: (context: ActionContext) => string;
/**
* Specifies a function to return a partial state to be updated when
* this status is entered (optional).
*/
linkedState?: (context: ActionContext) => Partial<Headerless<State>>;
};
/**
* Defines FAILED action status.
*/
[ActionStatus.Failed]: {
/**
* A FAILED action may report a corresponding error state with an error
* description reported by invoking the given function (optional).
*
* If not specified or if the function returns `undefined` or an empty
* string, only the action state change is reported, but not an error
* state.
*/
errorDescription?: (context: ActionContext) => string;
/**
* Specifies a function to return a partial state to be updated when
* this status is entered (optional).
*/
linkedState?: (context: ActionContext) => Partial<Headerless<State>>;
};
};
/**
* Defines configuration options of the `VirtualAgvAdapter`.
*
* @category AGV Adapter
*/
export interface VirtualAgvAdapterOptions extends AgvAdapterOptions {
/**
* The initial position of the virtual AGV when it is instantiated.
*
* Position coordinates are relative to the world coordinate system using a
* map with the given mapId. Theta defines the initial orientation in the
* range [-Pi ... Pi].
*
* If not specified, the position defaults to `{ mapId: "local", x: 0, y: 0,
* theta: 0, lastNodeId: "0" }`.
*/
initialPosition?: {
mapId: string;
x: number;
y: number;
theta: number;
lastNodeId: string;
};
/**
* Specifies the AGV's normal deviation x/y tolerance (in meter) if no
* deviation is allowed, i.e. if `NodePosition.allowedDeviationXy` is 0 or
* not specified.
*
* If not specified, value defaults to 0.5 meter.
*/
agvNormalDeviationXyTolerance?: number;
/**
* Specifies the AGV's normal deviation theta tolerance (in radian) if no
* deviation is allowed, i.e. if `NodePosition.allowedDeviationTheta` is 0
* or not specified.
*
* If not specified, value defaults to 0,349066 radians (20 degree).
*/
agvNormalDeviationThetaTolerance?: number;
/**
* The target driving speed of the AGV measured in meter per second
* (optional).
*
* If not specified, value defaults to 2 m/s.
*
* @remarks
* A virtual AGV is assumed to have the same forward and cornering speed, as
* well as infinite acceleration and deceleration. If the specified speed is
* greater than the maximum speed on a order's edge the speed is adjusted
* accordingly.
*
* The options `vehicleSpeed`, `vehicleSpeedDistribution`, and
* `vehicleTimeDistribution` are mutually exclusive. Specify at most one of
* them. If none is specified, the default value of the option
* `vehicleSpeed` is applied.
*/
vehicleSpeed?: number;
/**
* The driving speed distribution function of the AGV returning a series of
* independent, identically distributed random speed values (measured in
* meter per second) from a given distribution (optional).
*
* The driving speed can follow a probabilistic distribution such as a
* Normal (Gaussian) or Poisson distribution. The given function is invoked
* once per edge to yield the target speed of the AGV while traversing the
* edge.
*
* The first value of the returned tuple is the random target speed; the
* second one is the constant mean value of the speed distribution.
*
* @remarks The options `vehicleSpeed`, `vehicleSpeedDistribution`, and
* `vehicleTimeDistribution` are mutually exclusive. Specify at most one of
* them. If none is specified, the default value of the option
* `vehicleSpeed` is applied.
*/
vehicleSpeedDistribution?: () => [number, number];
/**
* The driving time distribution function of the AGV returning a series of
* independent, identically distributed random time values (measured in
* second) from a given distribution (optional).
*
* The driving time can follow a probabilistic distribution such as a Normal
* (Gaussian) or Poisson distribution. The given function is invoked once
* per edge to yield the target time of the AGV for traversing the edge.
*
* The first value of the returned tuple is the random target time for
* traversing an edge; the second one is the constant mean value of the
* driving time distribution.
*
* @remarks The options `vehicleSpeed`, `vehicleSpeedDistribution`, and
* `vehicleTimeDistribution` are mutually exclusive. Specify at most one of
* them. If none is specified, the default value of the option
* `vehicleSpeed` is applied.
*/
vehicleTimeDistribution?: () => [number, number];
/**
* Maximum reach in meter of an AGV with a fully charged battery (optional).
*
* If not specified, value defaults to 28800 meter (i.e. 4 hours travel time
* at a speed of 2m/s).
*
* @remarks This option doesn't take the actual speed of the AGV into
* account. To keep it simple it is just a rough approximation of the real
* physics.
*/
batteryMaxReach?: number;
/**
* Initial battery state of charge as a percentage number between 0 and 100
* (optional).
*
* If not specified, value defaults to 100 percent.
*/
initialBatteryCharge?: number;
/**
* Time in hours to charge an empty battery to 100% (optional).
*
* If not specified, value defaults to 1 hour.
*/
fullBatteryChargeTime?: number;
/**
* State of charge value in percent below which the AGV stops driving and
* and reports a corresponding error state with error type
* `"batteryLowError"` and error level FATAL.
*
* If not specified, value defaults to 1 percent.
*
* @remarks While charging `"batteryLowError"` is removed from state again
* as soon as charge advances 10% above this threshold.
*/
lowBatteryChargeThreshold?: number;
/**
* Rate in ticks per second at which periodic motion and state updates are
* triggered internally (optional).
*
* If not specified, value defaults to 5 ticks/sec.
*/
tickRate?: number;
/**
* Factor by which vehicle motion and execution of actions is speeded up
* (optional).
*
* If not specified, the value defaults to 1, i.e. no time lapse mode is
* active.
*
* @remarks Useful to speed up order execution in simulation and test
* environments.
*/
timeLapse?: number;
}
/**
* An AGV adapter that implements a virtual AGV supporting free autonomous
* navigation along edges, and a basic, yet extensible set of actions.
*
* This adapter is meant to be used as a template for realizing your own
* adapter, for simulation purposes, integration testing, and in other kind of
* environments where real AGVs are not available or must be mocked.
*
* The following actions are supported:
* - noop [instant, node, edge]
* - pick/drop [node],
* - initPosition [instant, node]
* - startPause/stopPause [instant]
* - startCharging/stopCharging [instant, node]
* - cancelOrder [instant, supported by AgvController]
* - stateRequest [instant, supported by AgvController]
* - factsheetRequest [instant, supported by AgvController]
* - orderExecutionTime [instant (custom)]
*
* The actions noop, pick, drop, startCharging, and stopCharging accept an
* optional action parameter named "duration" that specifies the number of
* seconds to stay in action state RUNNING. If not specified all these actions
* stay running for 5 seconds. Note that pick and drop actions stay in status
* INITIALIZING for 1 additional second.
*
* @remarks
* To be executable by the virtual AGV an order must specify `nodePosition` for
* all nodes except for the first one as VDA 5050 requires the vehicle to be
* already positioned on the first node (within a given deviation range). The
* property `nodeId` alone is not usable as a symbolic position.
*
* By default, when the virtual AGV is started, it is positioned at `{ x: 0, y:
* 0, theta: 0, lastNodeId: "0" }` relative to a map with mapId `local`. You can
* override this pose by supplying the option
* `VirtualAgvAdapterOptions.initialPosition` when instantiating the virtual
* AGV. In addition, you can override or reset this pose using the instant or
* node action `initPosition`, specifying `x`, `y`, `theta`, `mapId`,
* `lastNodeId`, and `lastNodeSequenceId` (optional, defaults to zero) as action
* parameters.
*
* The virtual AGV provides a constant safety state where no e-stop is activated
* and where the protective field is never violated. The operating mode of the
* virtual AGV is always automatic, i.e. it is fully controlled by master
* control.
*
* The virtual AGV can only pick and carry one load at a time. Before picking
* another load the current load must have been dropped.
*
* A charging action is executed on a charging spot while the vehicle is
* standing, either as an instant action or as a node action. Charging mode is
* either terminated explicitely by action stopCharging or automatically when
* the battery is fully charged.
*
* The AGV's remaining battery reach is reported in the `State.batteryState`
* property unless the vehicle time distribution mode is active according to the
* option `vehicleTimeDistribution`. When the AGV's battery runs low according
* to the option `lowBatteryChargeThreshold` it stops driving and reports an
* error of type `"batteryLowError"`. The master control must then initiate
* further actions, e.g. cancel any active order or start charging. The battery
* low error is removed from State as soon as battery charge advances 10% above
* the configured threshold.
*
* The custom action `orderExecutionTime` expects an action parameter key
* `orders` with an array of VDA 5050 headerless Order objects as parameter
* value. The action finishes immediately reporting the estimated order
* execution times in seconds as values in a comma-separated string format via
* the `resultDescription` of the corresponding action state. The calculated
* estimates include the effective duration of action processing on the order's
* nodes (taking action blocking types and concurrent actions into account) as
* well as the travel time on the order's edges, including both base and horizon
* nodes and edges.
*
* To support benchmarking and performance measurement based on statistics the
* virtual AGV also supports probabilistic distribution of driving speed or
* driving time by corresponding adapter options.
*
* @category AGV Adapter
*/
export declare class VirtualAgvAdapter implements AgvAdapter {
readonly debug: AgvAdapterDebugger;
private readonly _controller;
private readonly _options;
private readonly _actionStateMachines;
private _vehicleState;
private _tick;
private _tickIntervalId;
private _traverseContext;
private _batteryLowError;
constructor(controller: AgvController, adapterOptions: VirtualAgvAdapterOptions, debug: AgvAdapterDebugger);
/**
* Gets the Virtual AGV adapter configuration options as a readonly object
* with default values filled in for options not specified in the
* configuration.
*/
get options(): Readonly<Required<VirtualAgvAdapterOptions>>;
get controller(): AgvController;
get name(): string;
get apiVersion(): number;
attach(context: AttachContext): void;
detach(context: DetachContext): void;
executeAction(context: ActionContext): void;
finishEdgeAction(context: ActionContext): void;
cancelAction(context: ActionContext): void;
isActionExecutable(context: ActionContext): ErrorReference[];
isNodeWithinDeviationRange(node: Node): ErrorReference[];
isRouteTraversable(context: RouteTraversableContext): ErrorReference[];
/**
* Traverses the given edge using a basic free navigation algorithm where the
* AGV drives with constant speed on a straight line from the edge's start point
* to the edge's end point. This algorithm ignores obstacle detection and
* collision avoidance.
*/
traverseEdge(context: TraverseEdgeContext): void;
stopTraverse(context: StopTraverseContext): void;
/**
* Gets the vehicle state as a readonly object.
*/
protected get vehicleState(): Readonly<VirtualAgvState>;
/**
* Gets the default set of action definitions supported by the virtual AGV.
*
* @remarks Can be overwritten or extended by subclasses.
*/
protected get actionDefinitions(): VirtualActionDefinition[];
/**
* Vehicle starts driving with the given velocity.
*
* @param vx velocity in x direction
* @param vy velocity in y direction
* @param reportImmediately true if velocity update should be reported
* immediately; false otherwise
*/
protected startDriving(vx: number, vy: number, reportImmediately?: boolean): void;
/**
* Vehicle stops driving.
*
* @param reportImmediately true if velocity update should be reported
* immediately; false otherwise
*/
protected stopDriving(reportImmediately?: boolean): void;
/**
* Gets duration of given action in seconds.
*
* @param action an order action
* @returns duration of action (in seconds)
*/
protected getNodeActionDuration(action: Action): number;
/**
* Gets target speed of vehicle depending on related adapter options.
*
* @param useMean whether to use the constant mean speed or the random speed
* if a driving speed or time distribution has been specified in the adapter
* options; otherwise this parameter is ignored
* @param distance the target distance to travel; only used if driving time
* distribution has been specified in the adapter options
* @param maxSpeed a speed limit that must not be exceeded (optional, only
* used if no driving distribution function has been specified in the
* adapter options)
* @returns target speed of vehicle depending on the given parameters and
* adapter options
*/
protected getTargetSpeed(useMean: boolean, distance: number, maxSpeed?: number): number;
/**
* Determines whether the given order could be executed potentially by
* checking whether the order route is traversable and all node actions are
* potentially executable.
*
* @param nodes nodes of order
* @param edges edges of order
* @returns true if order can be executed potentially; false otherwise
*/
protected canExecuteOrder(nodes: Node[], edges: Edge[]): boolean;
/**
* Updates battery state of vehicle according to the given travel distance.
*
* @param dx distance travelled in x direction
* @param dy distance travelled in y direction
*/
protected updateBatteryState(dx: number, dy: number): void;
/**
* Gets battery reach of vehicle for the given state of charge.
*
* @param charge battery state of charge (in percent)
* @returns battery reach according to given state of charge
*/
protected getBatteryReach(charge: number): number;
private _optionsWithDefaults;
private _getActionDefinition;
private _finalizeAction;
private _onTick;
private _advanceTraverse;
private _advanceBatteryCharge;
private _loadAdded;
private _loadRemoved;
private _initPosition;
private _startPause;
private _stopPause;
private _startCharging;
private _stopCharging;
private _calculateEstimatedOrderExecutionTimes;
private _calculateEstimatedOrderExecutionTime;
}