Initial commit
Some checks failed
Gradle Build / build (push) Has been cancelled

This commit is contained in:
CaiXiang
2024-11-30 18:36:13 +08:00
commit aa56926258
2134 changed files with 232943 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
apply from: "${rootDir}/gradle/java-project.gradle"
apply from: "${rootDir}/gradle/java-codequality.gradle"
apply from: "${rootDir}/gradle/guice-project.gradle"
apply from: "${rootDir}/gradle/publishing-java.gradle"
dependencies {
api project(':opentcs-api-injection')
api project(':opentcs-common')
api group: 'com.sparkjava', name: 'spark-core', version: '2.9.4'
api group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.18.0'
api group: 'com.fasterxml.jackson.module', name: 'jackson-module-jsonSchema', version: '2.18.0'
api group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.18.0'
}
task release {
dependsOn build
}

View File

@@ -0,0 +1,40 @@
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapAnnotationArgs=WRAP_IF_LONG
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineMethodParams=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapAfterDotInChainedMethodCalls=false
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineDisjunctiveCatchTypes=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineFor=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineImplements=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapFor=WRAP_IF_LONG
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.sortMembersByVisibility=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.visibilityOrder=PUBLIC;PROTECTED;DEFAULT;PRIVATE
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.placeFinallyOnNewLine=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapMethodParams=WRAP_IF_LONG
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.enable-indent=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineArrayInit=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineCallArgs=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapDisjunctiveCatchTypes=WRAP_IF_LONG
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.keepGettersAndSettersTogether=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapExtendsImplementsList=WRAP_ALWAYS
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapThrowsKeyword=WRAP_ALWAYS
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapExtendsImplementsKeyword=WRAP_ALWAYS
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classMembersOrder=STATIC FIELD;FIELD;STATIC_INIT;CONSTRUCTOR;INSTANCE_INIT;STATIC METHOD;METHOD;STATIC CLASS;CLASS
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapEnumConstants=WRAP_ALWAYS
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapCommentText=false
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapThrowsList=WRAP_IF_LONG
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapAssert=WRAP_IF_LONG
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.importGroupsOrder=*
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.continuationIndentSize=4
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.placeElseOnNewLine=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.placeCatchOnNewLine=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineAnnotationArgs=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineTryResources=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.preserveNewLinesInComments=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineParenthesized=true
netbeans.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineThrows=true
netbeans.org-netbeans-modules-editor-indent.CodeStyle.project.text-line-wrap=none
netbeans.org-netbeans-modules-editor-indent.CodeStyle.project.indent-shift-width=2
netbeans.org-netbeans-modules-editor-indent.CodeStyle.project.spaces-per-tab=2
netbeans.org-netbeans-modules-editor-indent.CodeStyle.project.tab-size=2
netbeans.org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width=100
netbeans.org-netbeans-modules-editor-indent.CodeStyle.project.expand-tabs=true
netbeans.org-netbeans-modules-editor-indent.CodeStyle.usedProfile=project

View File

@@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.adminwebapi;
import jakarta.inject.Singleton;
import org.opentcs.customizations.kernel.KernelInjectionModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Configures the admin web API extension.
*/
public class AdminWebApiModule
extends
KernelInjectionModule {
/**
* This class's logger.
*/
private static final Logger LOG = LoggerFactory.getLogger(AdminWebApiModule.class);
/**
* Creates a new instance.
*/
public AdminWebApiModule() {
}
@Override
protected void configure() {
AdminWebApiConfiguration configuration
= getConfigBindingProvider().get(
AdminWebApiConfiguration.PREFIX,
AdminWebApiConfiguration.class
);
if (!configuration.enable()) {
LOG.info("Admin web API disabled by configuration.");
return;
}
bind(AdminWebApiConfiguration.class)
.toInstance(configuration);
extensionsBinderAllModes().addBinding()
.to(AdminWebApi.class)
.in(Singleton.class);
}
}

View File

@@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi;
import jakarta.inject.Singleton;
import org.opentcs.customizations.kernel.KernelInjectionModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Configures the service web API extension.
*/
public class ServiceWebApiModule
extends
KernelInjectionModule {
/**
* This class's logger.
*/
private static final Logger LOG = LoggerFactory.getLogger(ServiceWebApiModule.class);
/**
* Creates a new instance.
*/
public ServiceWebApiModule() {
}
@Override
protected void configure() {
ServiceWebApiConfiguration configuration
= getConfigBindingProvider().get(
ServiceWebApiConfiguration.PREFIX,
ServiceWebApiConfiguration.class
);
if (!configuration.enable()) {
LOG.info("Service web API disabled by configuration.");
return;
}
bind(ServiceWebApiConfiguration.class)
.toInstance(configuration);
extensionsBinderAllModes().addBinding()
.to(ServiceWebApi.class)
.in(Singleton.class);
}
}

View File

@@ -0,0 +1,5 @@
# SPDX-FileCopyrightText: The openTCS Authors
# SPDX-License-Identifier: MIT
org.opentcs.kernel.extensions.adminwebapi.AdminWebApiModule
org.opentcs.kernel.extensions.servicewebapi.ServiceWebApiModule

View File

@@ -0,0 +1,98 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.adminwebapi;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import org.opentcs.components.kernel.KernelExtension;
import org.opentcs.kernel.extensions.adminwebapi.v1.V1RequestHandler;
import org.opentcs.kernel.extensions.servicewebapi.HttpConstants;
import spark.Service;
/**
* Provides an HTTP interface for basic administration needs.
*/
public class AdminWebApi
implements
KernelExtension {
/**
* The interface configuration.
*/
private final AdminWebApiConfiguration configuration;
/**
* Handles requests for API version 1.
*/
private final V1RequestHandler v1RequestHandler;
/**
* The actual HTTP service.
*/
private Service service;
/**
* Whether this kernel extension is initialized.
*/
private boolean initialized;
/**
* Creates a new instance.
*
* @param configuration The interface configuration.
* @param v1RequestHandler Handles requests for API version 1.
*/
@Inject
public AdminWebApi(
AdminWebApiConfiguration configuration,
V1RequestHandler v1RequestHandler
) {
this.configuration = requireNonNull(configuration, "configuration");
this.v1RequestHandler = requireNonNull(v1RequestHandler, "v1RequestHandler");
}
@Override
public void initialize() {
if (isInitialized()) {
return;
}
service = Service.ignite()
.ipAddress(configuration.bindAddress())
.port(configuration.bindPort());
service.path("/v1", () -> {
service.get("/version", v1RequestHandler::handleGetVersion);
service.get("/status", v1RequestHandler::handleGetStatus);
service.delete("/kernel", v1RequestHandler::handleDeleteKernel);
}
);
service.exception(IllegalArgumentException.class, (exception, request, response) -> {
response.status(400);
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
response.body(exception.getMessage());
});
service.exception(IllegalStateException.class, (exception, request, response) -> {
response.status(500);
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
response.body(exception.getMessage());
});
initialized = true;
}
@Override
public void terminate() {
if (!isInitialized()) {
return;
}
service.stop();
initialized = false;
}
@Override
public boolean isInitialized() {
return initialized;
}
}

View File

@@ -0,0 +1,43 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.adminwebapi;
import org.opentcs.configuration.ConfigurationEntry;
import org.opentcs.configuration.ConfigurationPrefix;
/**
* Configuration entries for the administration web API.
*/
@ConfigurationPrefix(AdminWebApiConfiguration.PREFIX)
public interface AdminWebApiConfiguration {
/**
* The prefix for all configuration entries here.
*/
String PREFIX = "adminwebapi";
@ConfigurationEntry(
type = "Boolean",
description = "Whether to enable the admin interface.",
changesApplied = ConfigurationEntry.ChangesApplied.ON_APPLICATION_START,
orderKey = "0"
)
boolean enable();
@ConfigurationEntry(
type = "IP address",
description = "Address to which to bind the HTTP server, e.g. 0.0.0.0. (Default: 127.0.0.1.)",
changesApplied = ConfigurationEntry.ChangesApplied.ON_APPLICATION_START,
orderKey = "1"
)
String bindAddress();
@ConfigurationEntry(
type = "Integer",
description = "Port to which to bind the HTTP server.",
changesApplied = ConfigurationEntry.ChangesApplied.ON_APPLICATION_START,
orderKey = "2"
)
int bindPort();
}

View File

@@ -0,0 +1,43 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.adminwebapi.v1;
/**
* Describes the kernel process's current status.
*/
public class Status {
private String heapSize = String.valueOf(Runtime.getRuntime().totalMemory());
private String maxHeapSize = String.valueOf(Runtime.getRuntime().maxMemory());
private String freeInHeap = String.valueOf(Runtime.getRuntime().freeMemory());
public Status() {
}
public String getHeapSize() {
return heapSize;
}
public void setHeapSize(String heapSize) {
this.heapSize = heapSize;
}
public String getMaxHeapSize() {
return maxHeapSize;
}
public void setMaxHeapSize(String maxHeapSize) {
this.maxHeapSize = maxHeapSize;
}
public String getFreeInHeap() {
return freeInHeap;
}
public void setFreeInHeap(String freeInHeap) {
this.freeInHeap = freeInHeap;
}
}

View File

@@ -0,0 +1,130 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.adminwebapi.v1;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import jakarta.inject.Inject;
import java.io.IOException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.opentcs.access.Kernel;
import org.opentcs.access.LocalKernel;
import org.opentcs.components.Lifecycle;
import org.opentcs.customizations.kernel.KernelExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import spark.Request;
import spark.Response;
/**
* Handles requests and produces responses for version 1 of the admin web API.
*/
public class V1RequestHandler
implements
Lifecycle {
/**
* This class's logger.
*/
private static final Logger LOG = LoggerFactory.getLogger(V1RequestHandler.class);
/**
* Maps between objects and their JSON representations.
*/
private final ObjectMapper objectMapper
= new ObjectMapper()
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
/**
* The local kernel.
*/
private final LocalKernel kernel;
/**
* Used to schedule kernel shutdowns.
*/
private final ScheduledExecutorService kernelExecutor;
/**
* Whether this instance is initialized.
*/
private boolean initialized;
/**
* Creates a new instance.
*
* @param kernel The local kernel.
* @param kernelExecutor Use to schedule kernel shutdowns.
*/
@Inject
public V1RequestHandler(
LocalKernel kernel,
@KernelExecutor
ScheduledExecutorService kernelExecutor
) {
this.kernel = requireNonNull(kernel, "kernel");
this.kernelExecutor = requireNonNull(kernelExecutor, "kernelExecutor");
}
@Override
public void initialize() {
if (isInitialized()) {
return;
}
initialized = true;
}
@Override
public boolean isInitialized() {
return initialized;
}
@Override
public void terminate() {
if (!isInitialized()) {
return;
}
initialized = false;
}
public Object handleGetVersion(Request request, Response response) {
return toJson(new Version());
}
public Object handleGetStatus(Request request, Response response) {
return toJson(new Status());
}
public Object handleDeleteKernel(Request request, Response response) {
LOG.info("Initiating kernel shutdown as requested from {}...", request.ip());
kernelExecutor.schedule(() -> kernel.setState(Kernel.State.SHUTDOWN), 1, TimeUnit.SECONDS);
return "";
}
private <T> T fromJson(String jsonString, Class<T> clazz)
throws IllegalArgumentException {
try {
return objectMapper.readValue(jsonString, clazz);
}
catch (IOException exc) {
throw new IllegalArgumentException("Could not parse JSON input", exc);
}
}
private String toJson(Object object)
throws IllegalStateException {
try {
return objectMapper
.writerWithDefaultPrettyPrinter()
.writeValueAsString(object);
}
catch (JsonProcessingException exc) {
throw new IllegalStateException("Could not produce JSON output", exc);
}
}
}

View File

@@ -0,0 +1,45 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.adminwebapi.v1;
import org.opentcs.util.Environment;
/**
* Describes the version of the running kernel.
*/
public class Version {
private String baselineVersion = Environment.getBaselineVersion();
private String customizationName = Environment.getCustomizationName();
private String customizationVersion = Environment.getCustomizationVersion();
public Version() {
}
public String getBaselineVersion() {
return baselineVersion;
}
public void setBaselineVersion(String baselineVersion) {
this.baselineVersion = baselineVersion;
}
public String getCustomizationName() {
return customizationName;
}
public void setCustomizationName(String customizationName) {
this.customizationName = customizationName;
}
public String getCustomizationVersion() {
return customizationVersion;
}
public void setCustomizationVersion(String customizationVersion) {
this.customizationVersion = customizationVersion;
}
}

View File

@@ -0,0 +1,64 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi;
import static java.util.Objects.requireNonNull;
import com.google.common.base.Strings;
import jakarta.inject.Inject;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import spark.Request;
/**
* Authenticates incoming requests.
*/
public class Authenticator {
/**
* This class's logger.
*/
private static final Logger LOG = LoggerFactory.getLogger(Authenticator.class);
/**
* Defines the required access rules.
*/
private final ServiceWebApiConfiguration configuration;
/**
* Creates a new instance.
*
* @param configuration Defines the required access rules.
*/
@Inject
public Authenticator(ServiceWebApiConfiguration configuration) {
this.configuration = requireNonNull(configuration, "configuration");
}
/**
* Checks whether authentication is required and the given request is authenticated.
*
* @param request The request to be checked.
* @return <code>true</code> if, and only if, authentication is required and the given request is
* authenticated.
*/
public boolean isAuthenticated(Request request) {
requireNonNull(request, "request");
String requestAccessKey = request.headers(HttpConstants.HEADER_NAME_ACCESS_KEY);
LOG.debug(
"Provided access key in header is '{}', required value is '{}'",
requestAccessKey,
configuration.accessKey()
);
// Any empty access key indicates authentication is not required.
if (Strings.isNullOrEmpty(configuration.accessKey())) {
LOG.debug("No access key, authentication not required.");
return true;
}
return Objects.equals(requestAccessKey, configuration.accessKey());
}
}

View File

@@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi;
/**
* Defines some HTTP-related constants.
*/
public class HttpConstants {
/**
* Name of the header that is expected to contain the API access keys.
*/
public static final String HEADER_NAME_ACCESS_KEY = "X-Api-Access-Key";
/**
* Content type for plain text.
*/
public static final String CONTENT_TYPE_TEXT_PLAIN_UTF8 = "text/plain; charset=utf-8";
/**
* Content type for JSON structures.
*/
public static final String CONTENT_TYPE_APPLICATION_JSON_UTF8 = "application/json; charset=utf-8";
/**
* Prevents instantiation.
*/
private HttpConstants() {
}
}

View File

@@ -0,0 +1,92 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.io.IOException;
/**
* Binds JSON strings to objects and vice versa.
*/
public class JsonBinder {
/**
* Maps between objects and their JSON representations.
*/
private final ObjectMapper objectMapper
= new ObjectMapper()
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
/**
* Creates a new instance.
*/
public JsonBinder() {
}
/**
* Maps the given JSON string to an object.
*
* @param <T> The type of object to map to.
* @param jsonString The JSON string.
* @param clazz The type of object to map to.
* @return The object created from the JSON string.
* @throws IllegalArgumentException In case there was a problem mapping the given object from
* JSON.
* (An IllegalArgumentException is mapped to HTTP status code 400, indicating a client error.)
*/
public <T> T fromJson(String jsonString, Class<T> clazz)
throws IllegalArgumentException {
try {
return objectMapper.readValue(jsonString, clazz);
}
catch (IOException exc) {
throw new IllegalArgumentException("Could not parse JSON input", exc);
}
}
/**
* Maps the given object to a JSON string.
*
* @param object The object to be mapped.
* @return The JSON string representation of the object.
* @throws IllegalStateException In case there was a problem mapping the given object to JSON.
* (An IllegalStateException is mapped to HTTP status code 500, indicating an internal error.)
*/
public String toJson(Object object)
throws IllegalStateException {
try {
return objectMapper
.writerWithDefaultPrettyPrinter()
.writeValueAsString(object);
}
catch (JsonProcessingException exc) {
throw new IllegalStateException("Could not produce JSON output", exc);
}
}
/**
* Maps the given throwable to a JSON string.
*
* @param t The throwable to be mapped.
* @return A JSON string for the given throwable, consisting of a single-element array containing
* the throwable's message.
* @throws IllegalStateException In case there was a problem mapping the given object to JSON.
* (An IllegalStateException is mapped to HTTP status code 500, indicating an internal error.)
*/
public String toJson(Throwable t)
throws IllegalStateException {
try {
return objectMapper
.writerWithDefaultPrettyPrinter()
.writeValueAsString(objectMapper.createArrayNode().add(t.getMessage()));
}
catch (JsonProcessingException exc) {
throw new IllegalStateException("Could not produce JSON output", exc);
}
}
}

View File

@@ -0,0 +1,83 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.opentcs.access.KernelRuntimeException;
import org.opentcs.customizations.kernel.KernelExecutor;
/**
* Calls callables/runnables via the kernel executor and waits for the outcome.
*/
public class KernelExecutorWrapper {
private final ExecutorService kernelExecutor;
/**
* Creates a new instance.
*
* @param kernelExecutor The kernel executor.
*/
@Inject
public KernelExecutorWrapper(
@KernelExecutor
ExecutorService kernelExecutor
) {
this.kernelExecutor = requireNonNull(kernelExecutor, "kernelExecutor");
}
/**
* Calls the given callable via the kernel executor and waits for the outcome.
*
* @param <T> The callable's return type.
* @param callable The callable.
* @return The result of the call.
* @throws IllegalStateException In case the call via the kernel executor was unexpectedly
* interrupted.
* @throws RuntimeException In case an exception was thrown from the callable. If the exception
* thrown is a {@code RuntimeException}, it is forwarded directly; if it is not a
* {@code RuntimeException}, it is wrapped in a {@link KernelRuntimeException}.
*/
public <T> T callAndWait(Callable<T> callable)
throws IllegalStateException,
RuntimeException {
requireNonNull(callable, "callable");
try {
return kernelExecutor.submit(callable).get();
}
catch (InterruptedException exc) {
throw new IllegalStateException("Unexpectedly interrupted");
}
catch (ExecutionException exc) {
if (exc.getCause() instanceof RuntimeException) {
throw (RuntimeException) exc.getCause();
}
throw new KernelRuntimeException(exc.getCause());
}
}
/**
* Calls the given runnable via the kernel executor and waits for the outcome.
*
* @param runnable The runnable.
* @throws IllegalStateException In case the call via the kernel executor was unexpectedly
* interrupted.
* @throws RuntimeException In case an exception was thrown from the runnable. If the exception
* thrown is a {@code RuntimeException}, it is forwarded directly; if it is not a
* {@code RuntimeException}, it is wrapped in a {@link KernelRuntimeException}.
*/
public void callAndWait(Runnable runnable)
throws IllegalStateException,
RuntimeException {
requireNonNull(runnable, "runnable");
callAndWait(Executors.callable(runnable));
}
}

View File

@@ -0,0 +1,21 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi;
import org.opentcs.components.Lifecycle;
import spark.Service;
/**
* A request handler.
*/
public interface RequestHandler
extends
Lifecycle {
/**
* Registers the handler's routes with the given service.
*
* @param service The service to register the routes with.
*/
void addRoutes(Service service);
}

View File

@@ -0,0 +1,187 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi;
import static java.util.Objects.requireNonNull;
import com.google.common.util.concurrent.Uninterruptibles;
import jakarta.inject.Inject;
import java.util.concurrent.TimeUnit;
import org.opentcs.access.KernelRuntimeException;
import org.opentcs.access.SslParameterSet;
import org.opentcs.components.kernel.KernelExtension;
import org.opentcs.data.ObjectExistsException;
import org.opentcs.data.ObjectUnknownException;
import org.opentcs.kernel.extensions.servicewebapi.v1.V1RequestHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import spark.Service;
/**
* Provides an HTTP interface for basic administration needs.
*/
public class ServiceWebApi
implements
KernelExtension {
/**
* This class's logger.
*/
private static final Logger LOG = LoggerFactory.getLogger(ServiceWebApi.class);
/**
* The interface configuration.
*/
private final ServiceWebApiConfiguration configuration;
/**
* Authenticates incoming requests.
*/
private final Authenticator authenticator;
/**
* Handles requests for API version 1.
*/
private final V1RequestHandler v1RequestHandler;
/**
* Binds JSON data to objects and vice versa.
*/
private final JsonBinder jsonBinder;
/**
* The connection encryption configuration.
*/
private final SslParameterSet sslParamSet;
/**
* The actual HTTP service.
*/
private Service service;
/**
* Whether this kernel extension is initialized.
*/
private boolean initialized;
/**
* Creates a new instance.
*
* @param configuration The interface configuration.
* @param sslParamSet The SSL parameter set.
* @param authenticator Authenticates incoming requests.
* @param jsonBinder Binds JSON data to objects and vice versa.
* @param v1RequestHandler Handles requests for API version 1.
*/
@Inject
public ServiceWebApi(
ServiceWebApiConfiguration configuration,
SslParameterSet sslParamSet,
Authenticator authenticator,
JsonBinder jsonBinder,
V1RequestHandler v1RequestHandler
) {
this.configuration = requireNonNull(configuration, "configuration");
this.sslParamSet = requireNonNull(sslParamSet, "sslParamSet");
this.authenticator = requireNonNull(authenticator, "authenticator");
this.jsonBinder = requireNonNull(jsonBinder, "jsonBinder");
this.v1RequestHandler = requireNonNull(v1RequestHandler, "v1RequestHandler");
}
@Override
public void initialize() {
if (isInitialized()) {
return;
}
v1RequestHandler.initialize();
service = Service.ignite()
.ipAddress(configuration.bindAddress())
.port(configuration.bindPort());
if (configuration.useSsl()) {
service.secure(
sslParamSet.getKeystoreFile().getAbsolutePath(),
sslParamSet.getKeystorePassword(),
null,
null
);
}
else {
LOG.warn("Encryption disabled, connections will not be secured!");
}
service.before((request, response) -> {
if (!authenticator.isAuthenticated(request)) {
// Delay the response a bit to slow down brute force attacks.
Uninterruptibles.sleepUninterruptibly(2, TimeUnit.SECONDS);
service.halt(403, "Not authenticated.");
}
// Add a CORS header to allow cross-origin requests from all hosts.
// This also makes using the "try it out" buttons in the Swagger UI documentation possible.
response.header("Access-Control-Allow-Origin", "*");
});
// Reflect that we allow cross-origin requests for any headers and methods.
service.options(
"/*",
(request, response) -> {
String accessControlRequestHeaders = request.headers("Access-Control-Request-Headers");
if (accessControlRequestHeaders != null) {
response.header("Access-Control-Allow-Headers", accessControlRequestHeaders);
}
String accessControlRequestMethod = request.headers("Access-Control-Request-Method");
if (accessControlRequestMethod != null) {
response.header("Access-Control-Allow-Methods", accessControlRequestMethod);
}
return "OK";
}
);
// Register routes for API versions here.
service.path("/v1", () -> v1RequestHandler.addRoutes(service));
service.exception(IllegalArgumentException.class, (exception, request, response) -> {
response.status(400);
response.type(HttpConstants.CONTENT_TYPE_APPLICATION_JSON_UTF8);
response.body(jsonBinder.toJson(exception));
});
service.exception(ObjectUnknownException.class, (exception, request, response) -> {
response.status(404);
response.type(HttpConstants.CONTENT_TYPE_APPLICATION_JSON_UTF8);
response.body(jsonBinder.toJson(exception));
});
service.exception(ObjectExistsException.class, (exception, request, response) -> {
response.status(409);
response.type(HttpConstants.CONTENT_TYPE_APPLICATION_JSON_UTF8);
response.body(jsonBinder.toJson(exception));
});
service.exception(KernelRuntimeException.class, (exception, request, response) -> {
response.status(500);
response.type(HttpConstants.CONTENT_TYPE_APPLICATION_JSON_UTF8);
response.body(jsonBinder.toJson(exception));
});
service.exception(IllegalStateException.class, (exception, request, response) -> {
response.status(500);
response.type(HttpConstants.CONTENT_TYPE_APPLICATION_JSON_UTF8);
response.body(jsonBinder.toJson(exception));
});
initialized = true;
}
@Override
public void terminate() {
if (!isInitialized()) {
return;
}
v1RequestHandler.terminate();
service.stop();
initialized = false;
}
@Override
public boolean isInitialized() {
return initialized;
}
}

View File

@@ -0,0 +1,66 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi;
import org.opentcs.configuration.ConfigurationEntry;
import org.opentcs.configuration.ConfigurationPrefix;
/**
* Configuration entries for the service web API.
*/
@ConfigurationPrefix(ServiceWebApiConfiguration.PREFIX)
public interface ServiceWebApiConfiguration {
/**
* The prefix for all configuration entries here.
*/
String PREFIX = "servicewebapi";
@ConfigurationEntry(
type = "Boolean",
description = "Whether to enable the interface.",
changesApplied = ConfigurationEntry.ChangesApplied.ON_APPLICATION_START,
orderKey = "0"
)
boolean enable();
@ConfigurationEntry(
type = "IP address",
description = "Address to which to bind the HTTP server, e.g. 0.0.0.0 or 127.0.0.1.",
changesApplied = ConfigurationEntry.ChangesApplied.ON_APPLICATION_START,
orderKey = "1"
)
String bindAddress();
@ConfigurationEntry(
type = "Integer",
description = "Port to which to bind the HTTP server.",
changesApplied = ConfigurationEntry.ChangesApplied.ON_APPLICATION_START,
orderKey = "2"
)
int bindPort();
@ConfigurationEntry(
type = "String",
description = "Key allowing access to the API.",
changesApplied = ConfigurationEntry.ChangesApplied.INSTANTLY,
orderKey = "3"
)
String accessKey();
@ConfigurationEntry(
type = "Integer",
description = "Maximum number of status events to be kept.",
changesApplied = ConfigurationEntry.ChangesApplied.INSTANTLY,
orderKey = "4"
)
int statusEventsCapacity();
@ConfigurationEntry(
type = "Boolean",
description = "Whether to use SSL to encrypt connections.",
changesApplied = ConfigurationEntry.ChangesApplied.ON_APPLICATION_START,
orderKey = "5"
)
boolean useSsl();
}

View File

@@ -0,0 +1,112 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1;
import jakarta.annotation.Nullable;
import java.util.Objects;
import java.util.function.Predicate;
import org.opentcs.data.TCSObjectReference;
import org.opentcs.data.model.Vehicle;
import org.opentcs.data.order.OrderSequence;
import org.opentcs.data.order.TransportOrder;
import org.opentcs.data.peripherals.PeripheralJob;
/**
* Provides some commonly used object filters.
*/
public class Filters {
/**
* Prevents instantiation.
*/
private Filters() {
}
/**
* Returns a predicate that is true only for transport orders whose intended vehicle is the given
* one.
* In case the given vehicle reference is null, all transport orders are accepted.
*
* @param vehicleRef The vehicle reference.
* @return A predicate that is true only for transport orders whose intended vehicle is the given
* one.
*/
public static Predicate<TransportOrder> transportOrderWithIntendedVehicle(
@Nullable
TCSObjectReference<Vehicle> vehicleRef
) {
return vehicleRef == null
? order -> true
: order -> Objects.equals(vehicleRef, order.getIntendedVehicle());
}
/**
* Returns a predicate that is true only for order sequences whose intended vehicle is the given
* one.
* In case the given vehicle reference is null, all order sequences are accepted.
*
* @param vehicleRef The vehicle reference.
* @return A predicate that is true only for order sequences whose intended vehicle is the given
* one.
*/
public static Predicate<OrderSequence> orderSequenceWithIntendedVehicle(
@Nullable
TCSObjectReference<Vehicle> vehicleRef
) {
return vehicleRef == null
? sequence -> true
: sequence -> Objects.equals(vehicleRef, sequence.getIntendedVehicle());
}
/**
* Returns a predicate that is true only for peripheral jobs whose related vehicle is the given
* one.
* In case the given vehicle reference is null, all peripheral jobs are accepted.
*
* @param vehicleRef The vehicle reference.
* @return A predicate that is true only for peripheral jobs whose related vehicle is the given
* one.
*/
public static Predicate<PeripheralJob> peripheralJobWithRelatedVehicle(
@Nullable
TCSObjectReference<Vehicle> vehicleRef
) {
return vehicleRef == null
? job -> true
: job -> Objects.equals(vehicleRef, job.getRelatedVehicle());
}
/**
* Returns a predicate that is true only for peripheral jobs whose related transport order is the
* given one.
* In case the given vehicle reference is null, all peripheral jobs are accepted.
*
* @param orderRef The transport order reference.
* @return A predicate that is true only for peripheral jobs whose related transport order is the
* given one.
*/
public static Predicate<PeripheralJob> peripheralJobWithRelatedTransportOrder(
@Nullable
TCSObjectReference<TransportOrder> orderRef
) {
return orderRef == null
? job -> true
: job -> Objects.equals(orderRef, job.getRelatedTransportOrder());
}
/**
* Returns a predicate that is true only for vehicles whose processing state is the given one.
* In case the given procState is null, all vehicles are accepted.
*
* @param procState The processing state.
* @return A predicate that is true only for vehicles whose processing state is the given one.
*/
public static Predicate<Vehicle> vehicleWithProcState(
@Nullable
Vehicle.ProcState procState
) {
return procState == null
? vehicle -> true
: vehicle -> Objects.equals(procState, vehicle.getProcState());
}
}

View File

@@ -0,0 +1,62 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1;
import static java.util.Objects.requireNonNull;
import jakarta.annotation.Nonnull;
import jakarta.inject.Inject;
import org.opentcs.components.kernel.services.PlantModelService;
import org.opentcs.data.ObjectUnknownException;
import org.opentcs.data.model.Location;
import org.opentcs.kernel.extensions.servicewebapi.KernelExecutorWrapper;
/**
* Handles requests related to locations.
*/
public class LocationHandler {
private final PlantModelService plantModelService;
private final KernelExecutorWrapper executorWrapper;
/**
* Creates a new instance.
*
* @param plantModelService Used to retrieve and update location instances.
* @param executorWrapper Executes calls via the kernel executor and waits for the outcome.
*/
@Inject
public LocationHandler(
PlantModelService plantModelService,
KernelExecutorWrapper executorWrapper
) {
this.plantModelService = requireNonNull(plantModelService, "plantModelService");
this.executorWrapper = requireNonNull(executorWrapper, "executorWrapper");
}
/**
* Updates the locked state of the location with the given name.
*
* @param locationName The name of the location to update.
* @param lockedValue The location's new locked state (a boolean as a string).
* @throws ObjectUnknownException If a location with the given name could not be found.
*/
public void updateLocationLock(
@Nonnull
String locationName,
String lockedValue
)
throws ObjectUnknownException {
executorWrapper.callAndWait(() -> {
Location location = plantModelService.fetchObject(Location.class, locationName);
if (location == null) {
throw new ObjectUnknownException("Unknown location: " + locationName);
}
plantModelService.updateLocationLock(
location.getReference(),
Boolean.parseBoolean(lockedValue)
);
});
}
}

View File

@@ -0,0 +1,63 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1;
import static java.util.Objects.requireNonNull;
import jakarta.annotation.Nonnull;
import jakarta.inject.Inject;
import org.opentcs.components.kernel.services.PlantModelService;
import org.opentcs.components.kernel.services.TCSObjectService;
import org.opentcs.data.ObjectUnknownException;
import org.opentcs.data.model.Path;
import org.opentcs.kernel.extensions.servicewebapi.KernelExecutorWrapper;
/**
* Handles requests related to paths.
*/
public class PathHandler {
private final TCSObjectService objectService;
private final KernelExecutorWrapper executorWrapper;
private final PlantModelService plantModelService;
/**
* Creates a new instance.
*
* @param objectService Used to retrieve path instances.
* @param plantModelService Used to update path locks.
* @param executorWrapper Executes calls via the kernel executor and waits for the outcome.
*/
@Inject
public PathHandler(
TCSObjectService objectService,
KernelExecutorWrapper executorWrapper,
PlantModelService plantModelService
) {
this.objectService = requireNonNull(objectService, "objectService");
this.executorWrapper = requireNonNull(executorWrapper, "executorWrapper");
this.plantModelService = requireNonNull(plantModelService, "plantModelService");
}
/**
* Updates the locked state of the path with the given name.
*
* @param pathName The name of the path to update.
* @param lockedValue The path's new locked state (a boolean as a string).
* @throws ObjectUnknownException If a path with the given name could not be found.
*/
public void updatePathLock(
@Nonnull
String pathName,
String lockedValue
)
throws ObjectUnknownException {
executorWrapper.callAndWait(() -> {
Path path = objectService.fetchObject(Path.class, pathName);
if (path == null) {
throw new ObjectUnknownException("Unknown path: " + pathName);
}
plantModelService.updatePathLock(path.getReference(), Boolean.parseBoolean(lockedValue));
});
}
}

View File

@@ -0,0 +1,101 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import org.opentcs.components.kernel.services.PeripheralService;
import org.opentcs.data.ObjectUnknownException;
import org.opentcs.data.model.Location;
import org.opentcs.drivers.peripherals.PeripheralCommAdapterDescription;
import org.opentcs.drivers.peripherals.management.PeripheralAttachmentInformation;
import org.opentcs.kernel.extensions.servicewebapi.KernelExecutorWrapper;
/**
* Handles requests related to peripherals.
*/
public class PeripheralHandler {
private final PeripheralService peripheralService;
private final KernelExecutorWrapper executorWrapper;
/**
* Creates a new instance.
*
* @param peripheralService The service used to manage peripherals.
* @param executorWrapper Executes calls via the kernel executor and waits for the outcome.
*/
@Inject
public PeripheralHandler(
PeripheralService peripheralService,
KernelExecutorWrapper executorWrapper
) {
this.peripheralService = requireNonNull(peripheralService, "peripheralService");
this.executorWrapper = requireNonNull(executorWrapper, "executorWrapper");
}
public void putPeripheralCommAdapter(String name, String value)
throws ObjectUnknownException {
requireNonNull(name, "name");
requireNonNull(value, "value");
executorWrapper.callAndWait(() -> {
Location location = peripheralService.fetchObject(Location.class, name);
if (location == null) {
throw new ObjectUnknownException("Unknown location: " + name);
}
PeripheralCommAdapterDescription newAdapter
= peripheralService.fetchAttachmentInformation(location.getReference())
.getAvailableCommAdapters()
.stream()
.filter(description -> description.getClass().getName().equals(value))
.findAny()
.orElseThrow(
() -> new IllegalArgumentException(
"Unknown peripheral driver class name: " + value
)
);
peripheralService.attachCommAdapter(location.getReference(), newAdapter);
});
}
public void putPeripheralCommAdapterEnabled(String name, String value)
throws ObjectUnknownException,
IllegalArgumentException {
requireNonNull(name, "name");
requireNonNull(value, "value");
executorWrapper.callAndWait(() -> {
Location location = peripheralService.fetchObject(Location.class, name);
if (location == null) {
throw new ObjectUnknownException("Unknown location: " + name);
}
if (Boolean.parseBoolean(value)) {
peripheralService.enableCommAdapter(location.getReference());
}
else {
peripheralService.disableCommAdapter(location.getReference());
}
});
}
public PeripheralAttachmentInformation getPeripheralCommAdapterAttachmentInformation(String name)
throws ObjectUnknownException {
requireNonNull(name, "name");
return executorWrapper.callAndWait(() -> {
Location location = peripheralService.fetchObject(Location.class, name);
if (location == null) {
throw new ObjectUnknownException("Unknown location: " + name);
}
return peripheralService.fetchAttachmentInformation(location.getReference());
});
}
}

View File

@@ -0,0 +1,73 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import org.opentcs.components.kernel.services.PeripheralDispatcherService;
import org.opentcs.components.kernel.services.PeripheralJobService;
import org.opentcs.data.ObjectUnknownException;
import org.opentcs.data.model.Location;
import org.opentcs.data.peripherals.PeripheralJob;
import org.opentcs.kernel.extensions.servicewebapi.KernelExecutorWrapper;
/**
* Handles requests related to peripheral job dispatching.
*/
public class PeripheralJobDispatcherHandler {
private final PeripheralJobService jobService;
private final PeripheralDispatcherService jobDispatcherService;
private final KernelExecutorWrapper executorWrapper;
/**
* Creates a new instance.
*
* @param jobService Used to create peripheral jobs.
* @param jobDispatcherService Used to dispatch peripheral jobs.
* @param executorWrapper Executes calls via the kernel executor and waits for the outcome.
*/
@Inject
public PeripheralJobDispatcherHandler(
PeripheralJobService jobService,
PeripheralDispatcherService jobDispatcherService,
KernelExecutorWrapper executorWrapper
) {
this.jobService = requireNonNull(jobService, "jobService");
this.jobDispatcherService = requireNonNull(jobDispatcherService, "jobDispatcherService");
this.executorWrapper = requireNonNull(executorWrapper, "executorWrapper");
}
public void triggerJobDispatcher() {
executorWrapper.callAndWait(() -> jobDispatcherService.dispatch());
}
public void withdrawPeripheralJobByLocation(String name)
throws ObjectUnknownException {
requireNonNull(name, "name");
executorWrapper.callAndWait(() -> {
Location location = jobService.fetchObject(Location.class, name);
if (location == null) {
throw new ObjectUnknownException("Unknown location: " + name);
}
jobDispatcherService.withdrawByLocation(location.getReference());
});
}
public void withdrawPeripheralJob(String name)
throws ObjectUnknownException {
requireNonNull(name, "name");
executorWrapper.callAndWait(() -> {
PeripheralJob job = jobService.fetchObject(PeripheralJob.class, name);
if (job == null) {
throw new ObjectUnknownException("Unknown peripheral job: " + name);
}
jobDispatcherService.withdrawByPeripheralJob(job.getReference());
});
}
}

View File

@@ -0,0 +1,190 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1;
import static java.util.Objects.requireNonNull;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import jakarta.inject.Inject;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.opentcs.access.to.peripherals.PeripheralJobCreationTO;
import org.opentcs.access.to.peripherals.PeripheralOperationCreationTO;
import org.opentcs.components.kernel.services.PeripheralDispatcherService;
import org.opentcs.components.kernel.services.PeripheralJobService;
import org.opentcs.data.ObjectUnknownException;
import org.opentcs.data.TCSObjectReference;
import org.opentcs.data.model.Location;
import org.opentcs.data.model.Vehicle;
import org.opentcs.data.order.TransportOrder;
import org.opentcs.data.peripherals.PeripheralJob;
import org.opentcs.kernel.extensions.servicewebapi.KernelExecutorWrapper;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.GetPeripheralJobResponseTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostPeripheralJobRequestTO;
/**
* Handles requests related to peripheral jobs.
*/
public class PeripheralJobHandler {
private final PeripheralJobService jobService;
private final PeripheralDispatcherService jobDispatcherService;
private final KernelExecutorWrapper executorWrapper;
/**
* Creates a new instance.
*
* @param jobService Used to create peripheral jobs.
* @param jobDispatcherService Used to dispatch peripheral jobs.
* @param executorWrapper Executes calls via the kernel executor and waits for the outcome.
*/
@Inject
public PeripheralJobHandler(
PeripheralJobService jobService,
PeripheralDispatcherService jobDispatcherService,
KernelExecutorWrapper executorWrapper
) {
this.jobService = requireNonNull(jobService, "jobService");
this.jobDispatcherService = requireNonNull(jobDispatcherService, "jobDispatcherService");
this.executorWrapper = requireNonNull(executorWrapper, "executorWrapper");
}
public PeripheralJob createPeripheralJob(String name, PostPeripheralJobRequestTO job) {
requireNonNull(name, "name");
requireNonNull(job, "job");
return executorWrapper.callAndWait(() -> {
// Check if the vehicle, location and transport order exist.
if (job.getRelatedVehicle() != null
&& jobService.fetchObject(Vehicle.class, job.getRelatedVehicle()) == null) {
throw new ObjectUnknownException("Unknown vehicle: " + job.getRelatedVehicle());
}
if (job.getRelatedTransportOrder() != null
&& jobService.fetchObject(
TransportOrder.class,
job.getRelatedTransportOrder()
) == null) {
throw new ObjectUnknownException(
"Unknown transport order: " + job.getRelatedTransportOrder()
);
}
if (job.getPeripheralOperation().getLocationName() != null
&& jobService.fetchObject(
Location.class,
job.getPeripheralOperation().getLocationName()
) == null) {
throw new ObjectUnknownException(
"Unknown location: " + job.getPeripheralOperation().getLocationName()
);
}
// Peripheral jobs created via the web API are expected to be executed immediately and
// require no completion. Therefore, explicitly ignore the corresponding provided values.
PeripheralOperationCreationTO operationCreationTO = new PeripheralOperationCreationTO(
job.getPeripheralOperation().getOperation(),
job.getPeripheralOperation().getLocationName()
);
PeripheralJobCreationTO jobCreationTO = new PeripheralJobCreationTO(
name,
job.getReservationToken(),
operationCreationTO
)
.withIncompleteName(job.isIncompleteName());
if (job.getProperties() != null) {
jobCreationTO = jobCreationTO.withProperties(
job.getProperties().stream()
.collect(
Collectors.toMap(
property -> property.getKey(),
property -> property.getValue()
)
)
);
}
if (job.getRelatedTransportOrder() != null) {
jobCreationTO = jobCreationTO.withRelatedTransportOrderName(job.getRelatedTransportOrder());
}
if (job.getRelatedVehicle() != null) {
jobCreationTO = jobCreationTO.withRelatedVehicleName(job.getRelatedVehicle());
}
return jobService.createPeripheralJob(jobCreationTO);
});
}
/**
* Returns all peripheral jobs, optionally filtered using the given parameters.
*
* @param relatedVehicle Which vehicle to filter peripheral jobs for. Not filtered if the value is
* null.
* @param relatedTransportOrder Which transport order to filter peripheral jobs for. Not filtered
* if the value is null.
* @return List of peripheral job states.
*/
public List<GetPeripheralJobResponseTO> getPeripheralJobs(
@Nullable
String relatedVehicle,
@Nullable
String relatedTransportOrder
) {
return executorWrapper.callAndWait(() -> {
// If a related vehicle is set, make sure it exists.
TCSObjectReference<Vehicle> relatedVehicleRef
= Optional.ofNullable(relatedVehicle)
.map(name -> jobService.fetchObject(Vehicle.class, name))
.map(Vehicle::getReference)
.orElse(null);
if (relatedVehicle != null && relatedVehicleRef == null) {
throw new ObjectUnknownException("Unknown vehicle: " + relatedVehicle);
}
// If a related transport order is set, make sure it exists.
TCSObjectReference<TransportOrder> relatedOrderRef
= Optional.ofNullable(relatedTransportOrder)
.map(name -> jobService.fetchObject(TransportOrder.class, name))
.map(TransportOrder::getReference)
.orElse(null);
if (relatedTransportOrder != null && relatedOrderRef == null) {
throw new ObjectUnknownException("Unknown oransport order: " + relatedVehicle);
}
return jobService.fetchObjects(
PeripheralJob.class,
Filters.peripheralJobWithRelatedVehicle(relatedVehicleRef)
.and(Filters.peripheralJobWithRelatedTransportOrder(relatedOrderRef))
)
.stream()
.map(GetPeripheralJobResponseTO::fromPeripheralJob)
.sorted(Comparator.comparing(GetPeripheralJobResponseTO::getName))
.collect(Collectors.toList());
});
}
/**
* Find a peripheral job by name.
*
* @param name The name of the peripheral job.
* @return The peripheral job state.
*/
public GetPeripheralJobResponseTO getPeripheralJobByName(
@Nonnull
String name
) {
requireNonNull(name, "name");
return executorWrapper.callAndWait(() -> {
PeripheralJob job = jobService.fetchObject(PeripheralJob.class, name);
if (job == null) {
throw new ObjectUnknownException("Unknown peripheral job: " + name);
}
return GetPeripheralJobResponseTO.fromPeripheralJob(job);
});
}
}

View File

@@ -0,0 +1,151 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.opentcs.access.to.model.PlantModelCreationTO;
import org.opentcs.components.kernel.services.PlantModelService;
import org.opentcs.components.kernel.services.RouterService;
import org.opentcs.data.ObjectUnknownException;
import org.opentcs.data.TCSObjectReference;
import org.opentcs.data.model.Path;
import org.opentcs.data.model.PlantModel;
import org.opentcs.kernel.extensions.servicewebapi.KernelExecutorWrapper;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PlantModelTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostTopologyUpdateRequestTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.converter.BlockConverter;
import org.opentcs.kernel.extensions.servicewebapi.v1.converter.LocationConverter;
import org.opentcs.kernel.extensions.servicewebapi.v1.converter.LocationTypeConverter;
import org.opentcs.kernel.extensions.servicewebapi.v1.converter.PathConverter;
import org.opentcs.kernel.extensions.servicewebapi.v1.converter.PointConverter;
import org.opentcs.kernel.extensions.servicewebapi.v1.converter.PropertyConverter;
import org.opentcs.kernel.extensions.servicewebapi.v1.converter.VehicleConverter;
import org.opentcs.kernel.extensions.servicewebapi.v1.converter.VisualLayoutConverter;
/**
* Handles requests related to plant models.
*/
public class PlantModelHandler {
/**
* Used to set or retrieve plant models.
*/
private final PlantModelService plantModelService;
/**
* Executes calls via the kernel executor and waits for the outcome.
*/
private final KernelExecutorWrapper executorWrapper;
private final PointConverter pointConverter;
private final PathConverter pathConverter;
private final LocationTypeConverter locationTypeConverter;
private final LocationConverter locationConverter;
private final BlockConverter blockConverter;
private final VehicleConverter vehicleConverter;
private final VisualLayoutConverter visualLayoutConverter;
private final PropertyConverter propertyConverter;
private final RouterService routerService;
/**
* Creates a new instance.
*
* @param plantModelService Used to set or retrieve plant models.
* @param executorWrapper Executes calls via the kernel executor and waits for the outcome.
* @param pointConverter Converts point instances.
* @param pathConverter Converts path instances.
* @param locationTypeConverter Converts location type instances.
* @param locationConverter Converts location instances.
* @param blockConverter Converts block instances.
* @param vehicleConverter Converts vehicle instances.
* @param visualLayoutConverter Converts visual layout instances.
* @param propertyConverter Converts property instances.
* @param routerService Provides methods concerning the router.
*/
@Inject
public PlantModelHandler(
PlantModelService plantModelService,
KernelExecutorWrapper executorWrapper,
PointConverter pointConverter,
PathConverter pathConverter,
LocationTypeConverter locationTypeConverter,
LocationConverter locationConverter,
BlockConverter blockConverter,
VehicleConverter vehicleConverter,
VisualLayoutConverter visualLayoutConverter,
PropertyConverter propertyConverter,
RouterService routerService
) {
this.plantModelService = requireNonNull(plantModelService, "plantModelService");
this.executorWrapper = requireNonNull(executorWrapper, "executorWrapper");
this.pointConverter = requireNonNull(pointConverter, "pointConverter");
this.pathConverter = requireNonNull(pathConverter, "pathConverter");
this.locationTypeConverter = requireNonNull(locationTypeConverter, "locationTypeConverter");
this.locationConverter = requireNonNull(locationConverter, "locationConverter");
this.blockConverter = requireNonNull(blockConverter, "blockConverter");
this.vehicleConverter = requireNonNull(vehicleConverter, "vehicleConverter");
this.visualLayoutConverter = requireNonNull(visualLayoutConverter, "visualLayoutConverter");
this.propertyConverter = requireNonNull(propertyConverter, "propertyConverter");
this.routerService = requireNonNull(routerService, "routerService");
}
public void putPlantModel(PlantModelTO putPlantModel)
throws ObjectUnknownException,
IllegalArgumentException {
requireNonNull(putPlantModel, "putPlantModel");
PlantModelCreationTO plantModelCreationTO = new PlantModelCreationTO(putPlantModel.getName())
.withPoints(pointConverter.toPointCreationTOs(putPlantModel.getPoints()))
.withPaths(pathConverter.toPathCreationTOs(putPlantModel.getPaths()))
.withLocationTypes(
locationTypeConverter.toLocationTypeCreationTOs(putPlantModel.getLocationTypes())
)
.withLocations(locationConverter.toLocationCreationTOs(putPlantModel.getLocations()))
.withBlocks(blockConverter.toBlockCreationTOs(putPlantModel.getBlocks()))
.withVehicles(vehicleConverter.toVehicleCreationTOs(putPlantModel.getVehicles()))
.withVisualLayout(
visualLayoutConverter.toVisualLayoutCreationTO(putPlantModel.getVisualLayout())
)
.withProperties(propertyConverter.toPropertyMap(putPlantModel.getProperties()));
executorWrapper.callAndWait(() -> plantModelService.createPlantModel(plantModelCreationTO));
}
public PlantModelTO getPlantModel() {
PlantModel plantModel = plantModelService.getPlantModel();
return new PlantModelTO(plantModel.getName())
.setPoints(pointConverter.toPointTOs(plantModel.getPoints()))
.setPaths(pathConverter.toPathTOs(plantModel.getPaths()))
.setLocationTypes(locationTypeConverter.toLocationTypeTOs(plantModel.getLocationTypes()))
.setLocations(locationConverter.toLocationTOs(plantModel.getLocations()))
.setBlocks(blockConverter.toBlockTOs(plantModel.getBlocks()))
.setVehicles(vehicleConverter.toVehicleTOs(plantModel.getVehicles()))
.setVisualLayout(visualLayoutConverter.toVisualLayoutTO(plantModel.getVisualLayout()))
.setProperties(propertyConverter.toPropertyTOs(plantModel.getProperties()));
}
public void requestTopologyUpdate(PostTopologyUpdateRequestTO request)
throws ObjectUnknownException {
executorWrapper.callAndWait(
() -> routerService.updateRoutingTopology(toResourceReferences(request.getPaths()))
);
}
private Set<TCSObjectReference<Path>> toResourceReferences(List<String> paths) {
Set<TCSObjectReference<Path>> pathsToUpdate = new HashSet<>();
for (String name : paths) {
Path path = plantModelService.fetchObject(Path.class, name);
if (path == null) {
throw new ObjectUnknownException("Unknown path: " + name);
}
pathsToUpdate.add(path.getReference());
}
return pathsToUpdate;
}
}

View File

@@ -0,0 +1,214 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1;
import static java.util.Objects.requireNonNull;
import static org.opentcs.util.Assertions.checkInRange;
import jakarta.inject.Inject;
import java.util.Collection;
import java.util.SortedMap;
import java.util.TreeMap;
import org.opentcs.access.Kernel;
import org.opentcs.access.KernelStateTransitionEvent;
import org.opentcs.components.Lifecycle;
import org.opentcs.customizations.ApplicationEventBus;
import org.opentcs.data.TCSObject;
import org.opentcs.data.TCSObjectEvent;
import org.opentcs.data.model.Vehicle;
import org.opentcs.data.order.TransportOrder;
import org.opentcs.data.peripherals.PeripheralJob;
import org.opentcs.kernel.extensions.servicewebapi.ServiceWebApiConfiguration;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.GetEventsResponseTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.getevents.OrderStatusMessage;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.getevents.PeripheralJobStatusMessage;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.getevents.StatusMessage;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.getevents.VehicleStatusMessage;
import org.opentcs.util.event.EventHandler;
import org.opentcs.util.event.EventSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Provides descriptions of recent events.
*/
public class StatusEventDispatcher
implements
Lifecycle,
EventHandler {
/**
* This class's logger.
*/
private static final Logger LOG = LoggerFactory.getLogger(StatusEventDispatcher.class);
/**
* The interface configuration.
*/
private final ServiceWebApiConfiguration configuration;
/**
* Where we register for application events.
*/
private final EventSource eventSource;
/**
* The events collected.
*/
private final SortedMap<Long, StatusMessage> events = new TreeMap<>();
/**
* The number of events collected so far.
*/
private long eventCount;
/**
* Whether this instance is initialized.
*/
private boolean initialized;
/**
* Whether we are collecting events.
*/
private boolean eventCollectingOn;
@Inject
public StatusEventDispatcher(
ServiceWebApiConfiguration configuration,
@ApplicationEventBus
EventSource eventSource
) {
this.configuration = requireNonNull(configuration, "configuration");
this.eventSource = requireNonNull(eventSource, "eventSource");
}
@Override
public void initialize() {
if (isInitialized()) {
return;
}
eventSource.subscribe(this);
initialized = true;
}
@Override
public boolean isInitialized() {
return initialized;
}
@Override
public void terminate() {
if (!isInitialized()) {
return;
}
eventSource.unsubscribe(this);
initialized = false;
}
@Override
public void onEvent(Object event) {
if (event instanceof KernelStateTransitionEvent) {
handleStateTransition((KernelStateTransitionEvent) event);
}
if (!eventCollectingOn) {
return;
}
if (event instanceof TCSObjectEvent) {
handleObjectEvent((TCSObjectEvent) event);
}
}
/**
* Provides a list of events within the given range, waiting at most <code>timeout</code>
* milliseconds for new events if there currently aren't any.
*
* @param minSequenceNo The minimum sequence number for accepted events.
* @param maxSequenceNo The maximum sequence number for accepted events.
* @param timeout The maximum time to wait for events (in ms) if there currently aren't any.
* @return A list of events within the given range.
*/
public GetEventsResponseTO fetchEvents(long minSequenceNo, long maxSequenceNo, long timeout)
throws IllegalArgumentException {
checkInRange(minSequenceNo, 0, Long.MAX_VALUE, "minSequenceNo");
checkInRange(maxSequenceNo, minSequenceNo, Long.MAX_VALUE, "maxSequenceNo");
checkInRange(timeout, 0, Long.MAX_VALUE, "timeout");
GetEventsResponseTO result = new GetEventsResponseTO();
synchronized (events) {
Collection<StatusMessage> messages = events.subMap(minSequenceNo, maxSequenceNo).values();
if (messages.isEmpty()) {
try {
events.wait(timeout);
}
catch (InterruptedException exc) {
LOG.warn("Unexpectedly interrupted", exc);
}
}
messages = events.subMap(minSequenceNo, maxSequenceNo).values();
result.getStatusMessages().addAll(messages);
}
return result;
}
private void handleStateTransition(KernelStateTransitionEvent event) {
boolean wasOn = eventCollectingOn;
eventCollectingOn
= event.getEnteredState() == Kernel.State.OPERATING && event.isTransitionFinished();
// When switching collecting of events on, ensure we start clean.
if (!wasOn && eventCollectingOn) {
synchronized (events) {
eventCount = 0;
events.clear();
}
}
}
private void handleObjectEvent(TCSObjectEvent event) {
TCSObject<?> object = event.getCurrentOrPreviousObjectState();
if (object instanceof TransportOrder) {
synchronized (events) {
addOrderStatusMessage((TransportOrder) object, eventCount);
eventCount++;
cleanUpEvents();
events.notifyAll();
}
}
else if (object instanceof Vehicle) {
synchronized (events) {
addVehicleStatusMessage((Vehicle) object, eventCount);
eventCount++;
cleanUpEvents();
events.notifyAll();
}
}
else if (object instanceof PeripheralJob) {
synchronized (events) {
addPeripheralStatusMessage((PeripheralJob) object, eventCount);
eventCount++;
cleanUpEvents();
events.notifyAll();
}
}
}
private void addOrderStatusMessage(TransportOrder order, long sequenceNumber) {
events.put(sequenceNumber, OrderStatusMessage.fromTransportOrder(order, sequenceNumber));
}
private void addVehicleStatusMessage(Vehicle vehicle, long sequenceNumber) {
events.put(sequenceNumber, VehicleStatusMessage.fromVehicle(vehicle, sequenceNumber));
}
private void addPeripheralStatusMessage(PeripheralJob job, long sequenceNumber) {
events.put(sequenceNumber, PeripheralJobStatusMessage.fromPeripheralJob(job, sequenceNumber));
}
private void cleanUpEvents() {
// XXX Sanitize maxEventCount
int maxEventCount = configuration.statusEventsCapacity();
while (events.size() > maxEventCount) {
events.remove(events.firstKey());
}
}
}

View File

@@ -0,0 +1,121 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import org.opentcs.components.kernel.services.DispatcherService;
import org.opentcs.components.kernel.services.VehicleService;
import org.opentcs.data.ObjectUnknownException;
import org.opentcs.data.model.Vehicle;
import org.opentcs.data.order.ReroutingType;
import org.opentcs.data.order.TransportOrder;
import org.opentcs.kernel.extensions.servicewebapi.KernelExecutorWrapper;
/**
* Handles requests related to transport order dispatching.
*/
public class TransportOrderDispatcherHandler {
private final VehicleService vehicleService;
private final DispatcherService dispatcherService;
private final KernelExecutorWrapper executorWrapper;
/**
* Creates a new instance.
*
* @param vehicleService Used to update vehicle state.
* @param dispatcherService Used to withdraw transport orders.
* @param executorWrapper Executes calls via the kernel executor and waits for the outcome.
*/
@Inject
public TransportOrderDispatcherHandler(
VehicleService vehicleService,
DispatcherService dispatcherService,
KernelExecutorWrapper executorWrapper
) {
this.vehicleService = requireNonNull(vehicleService, "vehicleService");
this.dispatcherService = requireNonNull(dispatcherService, "dispatcherService");
this.executorWrapper = requireNonNull(executorWrapper, "executorWrapper");
}
public void triggerDispatcher() {
executorWrapper.callAndWait(() -> dispatcherService.dispatch());
}
public void tryImmediateAssignment(String name)
throws ObjectUnknownException,
IllegalArgumentException {
requireNonNull(name, "name");
executorWrapper.callAndWait(() -> {
TransportOrder order = vehicleService.fetchObject(TransportOrder.class, name);
if (order == null) {
throw new ObjectUnknownException("Unknown transport order: " + name);
}
dispatcherService.assignNow(order.getReference());
});
}
public void withdrawByTransportOrder(String name, boolean immediate, boolean disableVehicle)
throws ObjectUnknownException {
requireNonNull(name, "name");
executorWrapper.callAndWait(() -> {
if (vehicleService.fetchObject(TransportOrder.class, name) == null) {
throw new ObjectUnknownException("Unknown transport order: " + name);
}
TransportOrder order = vehicleService.fetchObject(TransportOrder.class, name);
if (disableVehicle && order.getProcessingVehicle() != null) {
vehicleService.updateVehicleIntegrationLevel(
order.getProcessingVehicle(),
Vehicle.IntegrationLevel.TO_BE_RESPECTED
);
}
dispatcherService.withdrawByTransportOrder(order.getReference(), immediate);
});
}
public void withdrawByVehicle(String name, boolean immediate, boolean disableVehicle)
throws ObjectUnknownException {
requireNonNull(name, "name");
executorWrapper.callAndWait(() -> {
Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, name);
if (vehicle == null) {
throw new ObjectUnknownException("Unknown vehicle: " + name);
}
if (disableVehicle) {
vehicleService.updateVehicleIntegrationLevel(
vehicle.getReference(),
Vehicle.IntegrationLevel.TO_BE_RESPECTED
);
}
dispatcherService.withdrawByVehicle(vehicle.getReference(), immediate);
});
}
public void reroute(String vehicleName, boolean forced)
throws ObjectUnknownException {
requireNonNull(vehicleName, "vehicleName");
executorWrapper.callAndWait(() -> {
Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, vehicleName);
if (vehicle == null) {
throw new ObjectUnknownException("Unknown vehicle: " + vehicleName);
}
dispatcherService.reroute(
vehicle.getReference(),
forced ? ReroutingType.FORCED : ReroutingType.REGULAR
);
});
}
}

View File

@@ -0,0 +1,276 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1;
import static java.util.Objects.requireNonNull;
import jakarta.annotation.Nullable;
import jakarta.inject.Inject;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.opentcs.access.KernelRuntimeException;
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.services.TransportOrderService;
import org.opentcs.data.ObjectExistsException;
import org.opentcs.data.ObjectUnknownException;
import org.opentcs.data.TCSObjectReference;
import org.opentcs.data.model.Vehicle;
import org.opentcs.data.order.OrderConstants;
import org.opentcs.data.order.OrderSequence;
import org.opentcs.data.order.TransportOrder;
import org.opentcs.kernel.extensions.servicewebapi.KernelExecutorWrapper;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.GetOrderSequenceResponseTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.GetTransportOrderResponseTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostOrderSequenceRequestTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostTransportOrderRequestTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.posttransportorder.Destination;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.Property;
/**
* Handles requests related to transport orders and order sequences.
*/
public class TransportOrderHandler {
private final TransportOrderService orderService;
private final KernelExecutorWrapper executorWrapper;
/**
* Creates a new instance.
*
* @param orderService The service we use to get the transport orders.
* @param executorWrapper Executes calls via the kernel executor and waits for the outcome.
*/
@Inject
public TransportOrderHandler(
TransportOrderService orderService,
KernelExecutorWrapper executorWrapper
) {
this.orderService = requireNonNull(orderService, "orderService");
this.executorWrapper = requireNonNull(executorWrapper, "executorWrapper");
}
public TransportOrder createOrder(String name, PostTransportOrderRequestTO order)
throws ObjectUnknownException,
ObjectExistsException,
KernelRuntimeException,
IllegalStateException {
requireNonNull(name, "name");
requireNonNull(order, "order");
TransportOrderCreationTO to
= new TransportOrderCreationTO(name, destinations(order))
.withIncompleteName(order.isIncompleteName())
.withDispensable(order.isDispensable())
.withIntendedVehicleName(order.getIntendedVehicle())
.withDependencyNames(dependencyNames(order.getDependencies()))
.withDeadline(deadline(order))
.withPeripheralReservationToken(order.getPeripheralReservationToken())
.withWrappingSequence(order.getWrappingSequence())
.withType(order.getType() == null ? OrderConstants.TYPE_NONE : order.getType())
.withProperties(properties(order.getProperties()));
return executorWrapper.callAndWait(() -> {
return orderService.createTransportOrder(to);
});
}
public void updateTransportOrderIntendedVehicle(
String orderName,
@Nullable
String vehicleName
)
throws ObjectUnknownException {
requireNonNull(orderName, "orderName");
executorWrapper.callAndWait(() -> {
TransportOrder order = orderService.fetchObject(TransportOrder.class, orderName);
if (order == null) {
throw new ObjectUnknownException("Unknown transport order: " + orderName);
}
Vehicle vehicle = null;
if (vehicleName != null) {
vehicle = orderService.fetchObject(Vehicle.class, vehicleName);
if (vehicle == null) {
throw new ObjectUnknownException("Unknown vehicle: " + vehicleName);
}
}
orderService.updateTransportOrderIntendedVehicle(
order.getReference(),
vehicle != null ? vehicle.getReference() : null
);
});
}
/**
* Find all transport orders and filters depending on the given parameters.
*
* @param intendedVehicle The filter parameter for the name of the
* intended vehicle for the transport order. The filtering is disabled for this parameter if the
* value is null.
* @return A list of transport orders that match the filter.
*/
public List<GetTransportOrderResponseTO> getTransportOrders(
@Nullable
String intendedVehicle
) {
return executorWrapper.callAndWait(() -> {
TCSObjectReference<Vehicle> intendedVehicleRef
= Optional.ofNullable(intendedVehicle)
.map(name -> orderService.fetchObject(Vehicle.class, name))
.map(Vehicle::getReference)
.orElse(null);
if (intendedVehicle != null && intendedVehicleRef == null) {
throw new ObjectUnknownException("Unknown vehicle: " + intendedVehicle);
}
return orderService.fetchObjects(
TransportOrder.class,
Filters.transportOrderWithIntendedVehicle(intendedVehicleRef)
)
.stream()
.map(GetTransportOrderResponseTO::fromTransportOrder)
.sorted(Comparator.comparing(GetTransportOrderResponseTO::getName))
.collect(Collectors.toList());
});
}
/**
* Finds the transport order with the given name.
*
* @param name The name of the requested transport order.
* @return A single transport order with the given name.
* @throws ObjectUnknownException If a transport order with the given name does not exist.
*/
public GetTransportOrderResponseTO getTransportOrderByName(String name)
throws ObjectUnknownException {
requireNonNull(name, "name");
return executorWrapper.callAndWait(() -> {
return Optional.ofNullable(orderService.fetchObject(TransportOrder.class, name))
.map(GetTransportOrderResponseTO::fromTransportOrder)
.orElseThrow(() -> new ObjectUnknownException("Unknown transport order: " + name));
});
}
public OrderSequence createOrderSequence(String name, PostOrderSequenceRequestTO sequence)
throws ObjectUnknownException,
ObjectExistsException,
KernelRuntimeException,
IllegalStateException {
requireNonNull(name, "name");
requireNonNull(sequence, "sequence");
OrderSequenceCreationTO to = new OrderSequenceCreationTO(name)
.withFailureFatal(sequence.isFailureFatal())
.withIncompleteName(sequence.isIncompleteName())
.withIntendedVehicleName(sequence.getIntendedVehicle())
.withProperties(properties(sequence.getProperties()))
.withType(sequence.getType());
return executorWrapper.callAndWait(() -> {
return orderService.createOrderSequence(to);
});
}
public void putOrderSequenceComplete(String name)
throws ObjectUnknownException,
IllegalStateException {
requireNonNull(name, "name");
executorWrapper.callAndWait(() -> {
OrderSequence orderSequence = orderService.fetchObject(OrderSequence.class, name);
if (orderSequence == null) {
throw new ObjectUnknownException("Unknown order sequence: " + name);
}
orderService.markOrderSequenceComplete(orderSequence.getReference());
});
}
public List<GetOrderSequenceResponseTO> getOrderSequences(
@Nullable
String intendedVehicle
) {
return executorWrapper.callAndWait(() -> {
TCSObjectReference<Vehicle> intendedVehicleRef
= Optional.ofNullable(intendedVehicle)
.map(name -> orderService.fetchObject(Vehicle.class, name))
.map(Vehicle::getReference)
.orElse(null);
if (intendedVehicle != null && intendedVehicleRef == null) {
throw new ObjectUnknownException("Unknown vehicle: " + intendedVehicle);
}
return orderService.fetchObjects(
OrderSequence.class,
Filters.orderSequenceWithIntendedVehicle(intendedVehicleRef)
)
.stream()
.map(GetOrderSequenceResponseTO::fromOrderSequence)
.sorted(Comparator.comparing(GetOrderSequenceResponseTO::getName))
.collect(Collectors.toList());
});
}
public GetOrderSequenceResponseTO getOrderSequenceByName(String name)
throws ObjectUnknownException {
requireNonNull(name, "name");
return executorWrapper.callAndWait(() -> {
return Optional.ofNullable(orderService.fetchObject(OrderSequence.class, name))
.map(GetOrderSequenceResponseTO::fromOrderSequence)
.orElseThrow(() -> new ObjectUnknownException("Unknown transport order: " + name));
});
}
private List<DestinationCreationTO> destinations(PostTransportOrderRequestTO order) {
List<DestinationCreationTO> result = new ArrayList<>(order.getDestinations().size());
for (Destination dest : order.getDestinations()) {
DestinationCreationTO to = new DestinationCreationTO(
dest.getLocationName(),
dest.getOperation()
);
if (dest.getProperties() != null) {
for (Property prop : dest.getProperties()) {
to = to.withProperty(prop.getKey(), prop.getValue());
}
}
result.add(to);
}
return result;
}
private Set<String> dependencyNames(List<String> dependencies) {
return dependencies == null ? new HashSet<>() : new HashSet<>(dependencies);
}
private Instant deadline(PostTransportOrderRequestTO order) {
return order.getDeadline() == null ? Instant.MAX : order.getDeadline();
}
private Map<String, String> properties(List<Property> properties) {
Map<String, String> result = new HashMap<>();
if (properties != null) {
for (Property prop : properties) {
result.put(prop.getKey(), prop.getValue());
}
}
return result;
}
}

View File

@@ -0,0 +1,735 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import java.util.List;
import java.util.concurrent.ExecutionException;
import org.opentcs.access.KernelRuntimeException;
import org.opentcs.data.ObjectExistsException;
import org.opentcs.data.ObjectUnknownException;
import org.opentcs.kernel.extensions.servicewebapi.HttpConstants;
import org.opentcs.kernel.extensions.servicewebapi.JsonBinder;
import org.opentcs.kernel.extensions.servicewebapi.RequestHandler;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.GetOrderSequenceResponseTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.GetPeripheralAttachmentInfoResponseTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.GetPeripheralJobResponseTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.GetTransportOrderResponseTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.GetVehicleAttachmentInfoResponseTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PlantModelTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostOrderSequenceRequestTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostPeripheralJobRequestTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostTopologyUpdateRequestTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostTransportOrderRequestTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostVehicleRoutesRequestTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostVehicleRoutesResponseTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PutVehicleAllowedOrderTypesTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PutVehicleEnergyLevelThresholdSetTO;
import spark.QueryParamsMap;
import spark.Request;
import spark.Response;
import spark.Service;
/**
* Handles requests and produces responses for version 1 of the web API.
*/
public class V1RequestHandler
implements
RequestHandler {
private final JsonBinder jsonBinder;
private final StatusEventDispatcher statusEventDispatcher;
private final TransportOrderDispatcherHandler orderDispatcherHandler;
private final TransportOrderHandler transportOrderHandler;
private final PeripheralJobHandler peripheralJobHandler;
private final PeripheralJobDispatcherHandler jobDispatcherHandler;
private final PlantModelHandler plantModelHandler;
private final VehicleHandler vehicleHandler;
private final PathHandler pathHandler;
private final LocationHandler locationHandler;
private final PeripheralHandler peripheralHandler;
private boolean initialized;
@Inject
public V1RequestHandler(
JsonBinder jsonBinder,
StatusEventDispatcher statusEventDispatcher,
TransportOrderDispatcherHandler orderDispatcherHandler,
TransportOrderHandler transportOrderHandler,
PeripheralJobHandler peripheralJobHandler,
PeripheralJobDispatcherHandler jobDispatcherHandler,
PlantModelHandler plantModelHandler,
VehicleHandler vehicleHandler,
PathHandler pathHandler,
LocationHandler locationHandler,
PeripheralHandler peripheralHandler
) {
this.jsonBinder = requireNonNull(jsonBinder, "jsonBinder");
this.statusEventDispatcher = requireNonNull(statusEventDispatcher, "statusEventDispatcher");
this.orderDispatcherHandler = requireNonNull(orderDispatcherHandler, "orderDispatcherHandler");
this.transportOrderHandler = requireNonNull(transportOrderHandler, "transportOrderHandler");
this.peripheralJobHandler = requireNonNull(peripheralJobHandler, "peripheralJobHandler");
this.jobDispatcherHandler = requireNonNull(jobDispatcherHandler, "jobDispatcherHandler");
this.plantModelHandler = requireNonNull(plantModelHandler, "plantModelHandler");
this.vehicleHandler = requireNonNull(vehicleHandler, "vehicleHandler");
this.pathHandler = requireNonNull(pathHandler, "pathHandler");
this.locationHandler = requireNonNull(locationHandler, "locationHandler");
this.peripheralHandler = requireNonNull(peripheralHandler, "peripheralHandler");
}
@Override
public void initialize() {
if (isInitialized()) {
return;
}
statusEventDispatcher.initialize();
initialized = true;
}
@Override
public boolean isInitialized() {
return initialized;
}
@Override
public void terminate() {
if (!isInitialized()) {
return;
}
statusEventDispatcher.terminate();
initialized = false;
}
@Override
public void addRoutes(Service service) {
requireNonNull(service, "service");
service.get(
"/events",
this::handleGetEvents
);
service.post(
"/vehicles/dispatcher/trigger",
this::handlePostDispatcherTrigger
);
service.post(
"/vehicles/:NAME/routeComputationQuery",
this::handleGetVehicleRoutes
);
service.put(
"/vehicles/:NAME/commAdapter/attachment",
this::handlePutVehicleCommAdapterAttachment
);
service.get(
"/vehicles/:NAME/commAdapter/attachmentInformation",
this::handleGetVehicleCommAdapterAttachmentInfo
);
service.put(
"/vehicles/:NAME/commAdapter/enabled",
this::handlePutVehicleCommAdapterEnabled
);
service.put(
"/vehicles/:NAME/paused",
this::handlePutVehiclePaused
);
service.put(
"/vehicles/:NAME/integrationLevel",
this::handlePutVehicleIntegrationLevel
);
service.post(
"/vehicles/:NAME/withdrawal",
this::handlePostWithdrawalByVehicle
);
service.post(
"/vehicles/:NAME/rerouteRequest",
this::handlePostVehicleRerouteRequest
);
service.put(
"/vehicles/:NAME/allowedOrderTypes",
this::handlePutVehicleAllowedOrderTypes
);
service.put(
"/vehicles/:NAME/energyLevelThresholdSet",
this::handlePutVehicleEnergyLevelThresholdSet
);
service.put(
"/vehicles/:NAME/envelopeKey",
this::handlePutVehicleEnvelopeKey
);
service.get(
"/vehicles/:NAME",
this::handleGetVehicleByName
);
service.get(
"/vehicles",
this::handleGetVehicles
);
service.post(
"/transportOrders/dispatcher/trigger",
this::handlePostDispatcherTrigger
);
service.post(
"/transportOrders/:NAME/immediateAssignment",
this::handlePostImmediateAssignment
);
service.post(
"/transportOrders/:NAME/withdrawal",
this::handlePostWithdrawalByOrder
);
service.post(
"/transportOrders/:NAME",
this::handlePostTransportOrder
);
service.put(
"/transportOrders/:NAME/intendedVehicle",
this::handlePutTransportOrderIntendedVehicle
);
service.get(
"/transportOrders/:NAME",
this::handleGetTransportOrderByName
);
service.get(
"/transportOrders",
this::handleGetTransportOrders
);
service.post(
"/orderSequences/:NAME",
this::handlePostOrderSequence
);
service.get(
"/orderSequences",
this::handleGetOrderSequences
);
service.get(
"/orderSequences/:NAME",
this::handleGetOrderSequenceByName
);
service.put(
"/orderSequences/:NAME/complete",
this::handlePutOrderSequenceComplete
);
service.put(
"/plantModel",
this::handlePutPlantModel
);
service.get(
"/plantModel",
this::handleGetPlantModel
);
service.post(
"/plantModel/topologyUpdateRequest",
this::handlePostUpdateTopology
);
service.put(
"/paths/:NAME/locked",
this::handlePutPathLocked
);
service.put(
"/locations/:NAME/locked",
this::handlePutLocationLocked
);
service.post(
"/dispatcher/trigger",
this::handlePostDispatcherTrigger
);
service.post(
"/peripherals/dispatcher/trigger",
this::handlePostPeripheralJobsDispatchTrigger
);
service.post(
"/peripherals/:NAME/withdrawal",
this::handlePostPeripheralWithdrawal
);
service.put(
"/peripherals/:NAME/commAdapter/enabled",
this::handlePutPeripheralCommAdapterEnabled
);
service.get(
"/peripherals/:NAME/commAdapter/attachmentInformation",
this::handleGetPeripheralCommAdapterAttachmentInfo
);
service.put(
"/peripherals/:NAME/commAdapter/attachment",
this::handlePutPeripheralCommAdapterAttachment
);
service.get(
"/peripheralJobs",
this::handleGetPeripheralJobs
);
service.get(
"/peripheralJobs/:NAME",
this::handleGetPeripheralJobsByName
);
service.post(
"/peripheralJobs/:NAME",
this::handlePostPeripheralJobsByName
);
service.post(
"/peripheralJobs/:NAME/withdrawal",
this::handlePostPeripheralJobWithdrawal
);
service.post(
"/peripheralJobs/dispatcher/trigger",
this::handlePostPeripheralJobsDispatchTrigger
);
}
private Object handlePostDispatcherTrigger(Request request, Response response)
throws KernelRuntimeException {
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
orderDispatcherHandler.triggerDispatcher();
return "";
}
private Object handleGetEvents(Request request, Response response)
throws IllegalArgumentException,
IllegalStateException {
response.type(HttpConstants.CONTENT_TYPE_APPLICATION_JSON_UTF8);
return jsonBinder.toJson(
statusEventDispatcher.fetchEvents(
minSequenceNo(request),
maxSequenceNo(request),
timeout(request)
)
);
}
private Object handlePutVehicleCommAdapterEnabled(Request request, Response response)
throws ObjectUnknownException,
IllegalArgumentException {
vehicleHandler.putVehicleCommAdapterEnabled(
request.params(":NAME"),
valueIfKeyPresent(request.queryMap(), "newValue")
);
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
return "";
}
private Object handleGetVehicleCommAdapterAttachmentInfo(Request request, Response response)
throws ObjectUnknownException,
IllegalArgumentException {
response.type(HttpConstants.CONTENT_TYPE_APPLICATION_JSON_UTF8);
return jsonBinder.toJson(
GetVehicleAttachmentInfoResponseTO.fromAttachmentInformation(
vehicleHandler.getVehicleCommAdapterAttachmentInformation(
request.params(":NAME")
)
)
);
}
private Object handleGetVehicleRoutes(Request request, Response response)
throws ObjectUnknownException,
IllegalArgumentException {
response.type(HttpConstants.CONTENT_TYPE_APPLICATION_JSON_UTF8);
return jsonBinder.toJson(
PostVehicleRoutesResponseTO.fromMap(
vehicleHandler.getVehicleRoutes(
request.params(":NAME"),
jsonBinder.fromJson(request.body(), PostVehicleRoutesRequestTO.class)
)
)
);
}
private Object handlePutVehicleCommAdapterAttachment(Request request, Response response)
throws ObjectUnknownException,
IllegalArgumentException {
vehicleHandler.putVehicleCommAdapter(
request.params(":NAME"),
valueIfKeyPresent(request.queryMap(), "newValue")
);
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
return "";
}
private Object handlePostTransportOrder(Request request, Response response)
throws ObjectUnknownException,
ObjectExistsException,
IllegalArgumentException,
IllegalStateException {
response.type(HttpConstants.CONTENT_TYPE_APPLICATION_JSON_UTF8);
return jsonBinder.toJson(
GetTransportOrderResponseTO.fromTransportOrder(
transportOrderHandler.createOrder(
request.params(":NAME"),
jsonBinder.fromJson(request.body(), PostTransportOrderRequestTO.class)
)
)
);
}
private Object handlePutTransportOrderIntendedVehicle(Request request, Response response)
throws ObjectUnknownException {
transportOrderHandler.updateTransportOrderIntendedVehicle(
request.params(":NAME"),
request.queryParamOrDefault("vehicle", null)
);
response.type(HttpConstants.CONTENT_TYPE_APPLICATION_JSON_UTF8);
return "";
}
private Object handlePostOrderSequence(Request request, Response response)
throws ObjectUnknownException,
ObjectExistsException,
IllegalArgumentException,
IllegalStateException {
response.type(HttpConstants.CONTENT_TYPE_APPLICATION_JSON_UTF8);
return jsonBinder.toJson(
GetOrderSequenceResponseTO.fromOrderSequence(
transportOrderHandler.createOrderSequence(
request.params(":NAME"),
jsonBinder.fromJson(request.body(), PostOrderSequenceRequestTO.class)
)
)
);
}
private Object handleGetOrderSequences(Request request, Response response) {
response.type(HttpConstants.CONTENT_TYPE_APPLICATION_JSON_UTF8);
return jsonBinder.toJson(
transportOrderHandler.getOrderSequences(
valueIfKeyPresent(request.queryMap(), "intendedVehicle")
)
);
}
private Object handleGetOrderSequenceByName(Request request, Response response) {
response.type(HttpConstants.CONTENT_TYPE_APPLICATION_JSON_UTF8);
return jsonBinder.toJson(
transportOrderHandler.getOrderSequenceByName(request.params(":NAME"))
);
}
private Object handlePutOrderSequenceComplete(Request request, Response response)
throws ObjectUnknownException,
IllegalArgumentException,
InterruptedException,
ExecutionException {
transportOrderHandler.putOrderSequenceComplete(request.params(":NAME"));
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
return "";
}
private Object handlePostImmediateAssignment(Request request, Response response)
throws ObjectUnknownException {
orderDispatcherHandler.tryImmediateAssignment(request.params(":NAME"));
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
return "";
}
private Object handlePostWithdrawalByOrder(Request request, Response response)
throws ObjectUnknownException {
orderDispatcherHandler.withdrawByTransportOrder(
request.params(":NAME"),
immediate(request),
disableVehicle(request)
);
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
return "";
}
private Object handlePostWithdrawalByVehicle(Request request, Response response)
throws ObjectUnknownException {
orderDispatcherHandler.withdrawByVehicle(
request.params(":NAME"),
immediate(request),
disableVehicle(request)
);
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
return "";
}
private Object handlePostPeripheralJobWithdrawal(Request request, Response response)
throws KernelRuntimeException {
jobDispatcherHandler.withdrawPeripheralJob(request.params(":NAME"));
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
return "";
}
private Object handlePostVehicleRerouteRequest(Request request, Response response)
throws ObjectUnknownException {
orderDispatcherHandler.reroute(request.params(":NAME"), forced(request));
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
return "";
}
private Object handleGetTransportOrders(Request request, Response response) {
response.type(HttpConstants.CONTENT_TYPE_APPLICATION_JSON_UTF8);
return jsonBinder.toJson(
transportOrderHandler.getTransportOrders(
valueIfKeyPresent(request.queryMap(), "intendedVehicle")
)
);
}
private Object handlePutPlantModel(Request request, Response response)
throws ObjectUnknownException,
IllegalArgumentException {
plantModelHandler.putPlantModel(jsonBinder.fromJson(request.body(), PlantModelTO.class));
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
return "";
}
private Object handleGetPlantModel(Request request, Response response) {
response.type(HttpConstants.CONTENT_TYPE_APPLICATION_JSON_UTF8);
return jsonBinder.toJson(plantModelHandler.getPlantModel());
}
private Object handlePostUpdateTopology(Request request, Response response)
throws ObjectUnknownException,
KernelRuntimeException {
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
if (request.body().isBlank()) {
plantModelHandler.requestTopologyUpdate(new PostTopologyUpdateRequestTO(List.of()));
}
else {
plantModelHandler
.requestTopologyUpdate(
jsonBinder.fromJson(request.body(), PostTopologyUpdateRequestTO.class)
);
}
return "";
}
private Object handlePutPathLocked(Request request, Response response) {
pathHandler.updatePathLock(
request.params(":NAME"),
valueIfKeyPresent(request.queryMap(), "newValue")
);
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
return "";
}
private Object handlePutLocationLocked(Request request, Response response) {
locationHandler.updateLocationLock(
request.params(":NAME"),
valueIfKeyPresent(request.queryMap(), "newValue")
);
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
return "";
}
private Object handleGetTransportOrderByName(Request request, Response response) {
response.type(HttpConstants.CONTENT_TYPE_APPLICATION_JSON_UTF8);
return jsonBinder.toJson(
transportOrderHandler.getTransportOrderByName(request.params(":NAME"))
);
}
private Object handleGetVehicles(Request request, Response response)
throws IllegalArgumentException {
response.type(HttpConstants.CONTENT_TYPE_APPLICATION_JSON_UTF8);
return jsonBinder.toJson(
vehicleHandler.getVehiclesState(
valueIfKeyPresent(
request.queryMap(),
"procState"
)
)
);
}
private Object handleGetVehicleByName(Request request, Response response)
throws ObjectUnknownException {
response.type(HttpConstants.CONTENT_TYPE_APPLICATION_JSON_UTF8);
return jsonBinder.toJson(
vehicleHandler.getVehicleStateByName(request.params(":NAME"))
);
}
private Object handlePutVehicleIntegrationLevel(Request request, Response response)
throws ObjectUnknownException,
IllegalArgumentException {
vehicleHandler.putVehicleIntegrationLevel(
request.params(":NAME"),
valueIfKeyPresent(request.queryMap(), "newValue")
);
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
return "";
}
private Object handlePutVehiclePaused(Request request, Response response)
throws ObjectUnknownException,
IllegalArgumentException {
vehicleHandler.putVehiclePaused(
request.params(":NAME"),
valueIfKeyPresent(request.queryMap(), "newValue")
);
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
return "";
}
private Object handlePutVehicleAllowedOrderTypes(Request request, Response response)
throws ObjectUnknownException,
IllegalArgumentException {
vehicleHandler.putVehicleAllowedOrderTypes(
request.params(":NAME"),
jsonBinder.fromJson(request.body(), PutVehicleAllowedOrderTypesTO.class)
);
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
return "";
}
private Object handlePutVehicleEnergyLevelThresholdSet(Request request, Response response)
throws ObjectUnknownException,
IllegalArgumentException {
vehicleHandler.putVehicleEnergyLevelThresholdSet(
request.params(":NAME"),
jsonBinder.fromJson(request.body(), PutVehicleEnergyLevelThresholdSetTO.class)
);
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
return "";
}
private Object handlePutVehicleEnvelopeKey(Request request, Response response)
throws ObjectUnknownException,
IllegalArgumentException {
vehicleHandler.putVehicleEnvelopeKey(
request.params(":NAME"),
valueIfKeyPresent(request.queryMap(), "newValue")
);
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
return "";
}
private Object handlePostPeripheralWithdrawal(Request request, Response response)
throws KernelRuntimeException {
jobDispatcherHandler.withdrawPeripheralJobByLocation(request.params(":NAME"));
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
return "";
}
private Object handlePutPeripheralCommAdapterEnabled(Request request, Response response)
throws ObjectUnknownException,
IllegalArgumentException {
peripheralHandler.putPeripheralCommAdapterEnabled(
request.params(":NAME"),
valueIfKeyPresent(request.queryMap(), "newValue")
);
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
return "";
}
private Object handleGetPeripheralCommAdapterAttachmentInfo(Request request, Response response)
throws ObjectUnknownException,
IllegalArgumentException {
response.type(HttpConstants.CONTENT_TYPE_APPLICATION_JSON_UTF8);
return jsonBinder.toJson(
GetPeripheralAttachmentInfoResponseTO.fromAttachmentInformation(
peripheralHandler.getPeripheralCommAdapterAttachmentInformation(
request.params(":NAME")
)
)
);
}
private Object handleGetPeripheralJobs(Request request, Response response) {
response.type(HttpConstants.CONTENT_TYPE_APPLICATION_JSON_UTF8);
return jsonBinder.toJson(
peripheralJobHandler.getPeripheralJobs(
valueIfKeyPresent(request.queryMap(), "relatedVehicle"),
valueIfKeyPresent(request.queryMap(), "relatedTransportOrder")
)
);
}
private Object handlePutPeripheralCommAdapterAttachment(Request request, Response response)
throws ObjectUnknownException,
IllegalArgumentException {
peripheralHandler.putPeripheralCommAdapter(
request.params(":NAME"),
valueIfKeyPresent(request.queryMap(), "newValue")
);
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
return "";
}
private Object handleGetPeripheralJobsByName(Request request, Response response) {
response.type(HttpConstants.CONTENT_TYPE_APPLICATION_JSON_UTF8);
return jsonBinder.toJson(
peripheralJobHandler.getPeripheralJobByName(request.params(":NAME"))
);
}
private Object handlePostPeripheralJobsByName(Request request, Response response) {
response.type(HttpConstants.CONTENT_TYPE_APPLICATION_JSON_UTF8);
return jsonBinder.toJson(
GetPeripheralJobResponseTO.fromPeripheralJob(
peripheralJobHandler.createPeripheralJob(
request.params(":NAME"),
jsonBinder.fromJson(request.body(), PostPeripheralJobRequestTO.class)
)
)
);
}
private Object handlePostPeripheralJobsDispatchTrigger(Request request, Response response) {
response.type(HttpConstants.CONTENT_TYPE_TEXT_PLAIN_UTF8);
jobDispatcherHandler.triggerJobDispatcher();
return "";
}
private String valueIfKeyPresent(QueryParamsMap queryParams, String key) {
if (queryParams.hasKey(key)) {
return queryParams.value(key);
}
else {
return null;
}
}
private long minSequenceNo(Request request)
throws IllegalArgumentException {
String param = request.queryParamOrDefault("minSequenceNo", "0");
try {
return Long.parseLong(param);
}
catch (NumberFormatException exc) {
throw new IllegalArgumentException("Malformed minSequenceNo: " + param);
}
}
private long maxSequenceNo(Request request)
throws IllegalArgumentException {
String param = request.queryParamOrDefault("maxSequenceNo", String.valueOf(Long.MAX_VALUE));
try {
return Long.parseLong(param);
}
catch (NumberFormatException exc) {
throw new IllegalArgumentException("Malformed minSequenceNo: " + param);
}
}
private long timeout(Request request)
throws IllegalArgumentException {
String param = request.queryParamOrDefault("timeout", "1000");
try {
// Allow a maximum timeout of 10 seconds so server threads are only bound for a limited time.
return Math.min(10000, Long.parseLong(param));
}
catch (NumberFormatException exc) {
throw new IllegalArgumentException("Malformed timeout: " + param);
}
}
private boolean immediate(Request request) {
return Boolean.parseBoolean(request.queryParamOrDefault("immediate", "false"));
}
private boolean disableVehicle(Request request) {
return Boolean.parseBoolean(request.queryParamOrDefault("disableVehicle", "false"));
}
private boolean forced(Request request) {
return Boolean.parseBoolean(request.queryParamOrDefault("forced", "false"));
}
}

View File

@@ -0,0 +1,334 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1;
import static java.util.Objects.requireNonNull;
import jakarta.annotation.Nullable;
import jakarta.inject.Inject;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.opentcs.components.kernel.services.RouterService;
import org.opentcs.components.kernel.services.VehicleService;
import org.opentcs.data.ObjectUnknownException;
import org.opentcs.data.TCSObjectReference;
import org.opentcs.data.model.Location;
import org.opentcs.data.model.Path;
import org.opentcs.data.model.Point;
import org.opentcs.data.model.TCSResourceReference;
import org.opentcs.data.model.Vehicle;
import org.opentcs.data.model.Vehicle.EnergyLevelThresholdSet;
import org.opentcs.data.order.Route;
import org.opentcs.drivers.vehicle.VehicleCommAdapterDescription;
import org.opentcs.drivers.vehicle.management.VehicleAttachmentInformation;
import org.opentcs.kernel.extensions.servicewebapi.KernelExecutorWrapper;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.GetVehicleResponseTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostVehicleRoutesRequestTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PutVehicleAllowedOrderTypesTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PutVehicleEnergyLevelThresholdSetTO;
/**
* Handles requests related to vehicles.
*/
public class VehicleHandler {
private final VehicleService vehicleService;
private final RouterService routerService;
private final KernelExecutorWrapper executorWrapper;
/**
* Creates a new instance.
*
* @param vehicleService Used to update vehicle instances.
* @param routerService Used to get information about potential routes.
* @param executorWrapper Executes calls via the kernel executor and waits for the outcome.
*/
@Inject
public VehicleHandler(
VehicleService vehicleService,
RouterService routerService,
KernelExecutorWrapper executorWrapper
) {
this.vehicleService = requireNonNull(vehicleService, "vehicleService");
this.routerService = requireNonNull(routerService, "routerService");
this.executorWrapper = requireNonNull(executorWrapper, "executorWrapper");
}
/**
* Find all vehicles orders and filters depending on the given parameters.
*
* @param procStateName The filter parameter for the processing state of the vehicle.
* The filtering is disabled for this parameter if the value is null.
* @return A list of vehicles, that match the filter.
* @throws IllegalArgumentException If procStateName could not be parsed.
*/
public List<GetVehicleResponseTO> getVehiclesState(
@Nullable
String procStateName
)
throws IllegalArgumentException {
return executorWrapper.callAndWait(() -> {
Vehicle.ProcState pState = procStateName == null
? null
: Vehicle.ProcState.valueOf(procStateName);
return vehicleService.fetchObjects(Vehicle.class, Filters.vehicleWithProcState(pState))
.stream()
.map(GetVehicleResponseTO::fromVehicle)
.sorted(Comparator.comparing(GetVehicleResponseTO::getName))
.collect(Collectors.toList());
});
}
/**
* Finds the vehicle with the given name.
*
* @param name The name of the requested vehicle.
* @return A single vehicle that has the given name.
* @throws ObjectUnknownException If a vehicle with the given name does not exist.
*/
public GetVehicleResponseTO getVehicleStateByName(String name)
throws ObjectUnknownException {
requireNonNull(name, "name");
return executorWrapper.callAndWait(() -> {
return Optional.ofNullable(vehicleService.fetchObject(Vehicle.class, name))
.map(GetVehicleResponseTO::fromVehicle)
.orElseThrow(() -> new ObjectUnknownException("Unknown vehicle: " + name));
});
}
public void putVehicleIntegrationLevel(String name, String value)
throws ObjectUnknownException,
IllegalArgumentException {
requireNonNull(name, "name");
requireNonNull(value, "value");
executorWrapper.callAndWait(() -> {
Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, name);
if (vehicle == null) {
throw new ObjectUnknownException("Unknown vehicle: " + name);
}
vehicleService.updateVehicleIntegrationLevel(
vehicle.getReference(),
Vehicle.IntegrationLevel.valueOf(value)
);
});
}
public void putVehiclePaused(String name, String value)
throws ObjectUnknownException,
IllegalArgumentException {
requireNonNull(name, "name");
requireNonNull(value, "value");
executorWrapper.callAndWait(() -> {
Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, name);
if (vehicle == null) {
throw new ObjectUnknownException("Unknown vehicle: " + name);
}
vehicleService.updateVehiclePaused(vehicle.getReference(), Boolean.parseBoolean(value));
});
}
public void putVehicleEnvelopeKey(String name, String value)
throws ObjectUnknownException,
IllegalArgumentException {
requireNonNull(name, "name");
executorWrapper.callAndWait(() -> {
Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, name);
if (vehicle == null) {
throw new ObjectUnknownException("Unknown vehicle: " + name);
}
vehicleService.updateVehicleEnvelopeKey(vehicle.getReference(), value);
});
}
public void putVehicleCommAdapterEnabled(String name, String value)
throws ObjectUnknownException,
IllegalArgumentException {
requireNonNull(name, "name");
requireNonNull(value, "value");
executorWrapper.callAndWait(() -> {
Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, name);
if (vehicle == null) {
throw new ObjectUnknownException("Unknown vehicle: " + name);
}
if (Boolean.parseBoolean(value)) {
vehicleService.enableCommAdapter(vehicle.getReference());
}
else {
vehicleService.disableCommAdapter(vehicle.getReference());
}
});
}
public VehicleAttachmentInformation getVehicleCommAdapterAttachmentInformation(String name)
throws ObjectUnknownException {
requireNonNull(name, "name");
return executorWrapper.callAndWait(() -> {
Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, name);
if (vehicle == null) {
throw new ObjectUnknownException("Unknown vehicle: " + name);
}
return vehicleService.fetchAttachmentInformation(vehicle.getReference());
});
}
public void putVehicleCommAdapter(String name, String value)
throws ObjectUnknownException {
requireNonNull(name, "name");
requireNonNull(value, "value");
executorWrapper.callAndWait(() -> {
Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, name);
if (vehicle == null) {
throw new ObjectUnknownException("Unknown vehicle: " + name);
}
VehicleCommAdapterDescription newAdapter
= vehicleService.fetchAttachmentInformation(vehicle.getReference())
.getAvailableCommAdapters()
.stream()
.filter(description -> description.getClass().getName().equals(value))
.findAny()
.orElseThrow(
() -> new IllegalArgumentException("Unknown vehicle driver class name: " + value)
);
vehicleService.attachCommAdapter(vehicle.getReference(), newAdapter);
});
}
public void putVehicleAllowedOrderTypes(
String name,
PutVehicleAllowedOrderTypesTO allowedOrderTypes
)
throws ObjectUnknownException {
requireNonNull(name, "name");
requireNonNull(allowedOrderTypes, "allowedOrderTypes");
executorWrapper.callAndWait(() -> {
Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, name);
if (vehicle == null) {
throw new ObjectUnknownException("Unknown vehicle: " + name);
}
vehicleService.updateVehicleAllowedOrderTypes(
vehicle.getReference(), new HashSet<>(allowedOrderTypes.getOrderTypes())
);
});
}
public void putVehicleEnergyLevelThresholdSet(
String name,
PutVehicleEnergyLevelThresholdSetTO energyLevelThresholdSet
)
throws ObjectUnknownException {
requireNonNull(name, "name");
requireNonNull(energyLevelThresholdSet, "energyLevelThresholdSet");
executorWrapper.callAndWait(() -> {
Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, name);
if (vehicle == null) {
throw new ObjectUnknownException("Unknown vehicle: " + name);
}
vehicleService.updateVehicleEnergyLevelThresholdSet(
vehicle.getReference(),
new EnergyLevelThresholdSet(
energyLevelThresholdSet.getEnergyLevelCritical(),
energyLevelThresholdSet.getEnergyLevelGood(),
energyLevelThresholdSet.getEnergyLevelSufficientlyRecharged(),
energyLevelThresholdSet.getEnergyLevelFullyRecharged()
)
);
});
}
public Map<TCSObjectReference<Point>, Route> getVehicleRoutes(
String name,
PostVehicleRoutesRequestTO request
)
throws ObjectUnknownException {
requireNonNull(name, "name");
requireNonNull(request, "request");
return executorWrapper.callAndWait(() -> {
Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, name);
if (vehicle == null) {
throw new ObjectUnknownException("Unknown vehicle: " + name);
}
TCSObjectReference<Point> sourcePointRef;
if (request.getSourcePoint() == null) {
if (vehicle.getCurrentPosition() == null) {
throw new IllegalArgumentException("Unknown vehicle position: " + vehicle.getName());
}
sourcePointRef = vehicle.getCurrentPosition();
}
else {
Point sourcePoint = vehicleService.fetchObject(Point.class, request.getSourcePoint());
if (sourcePoint == null) {
throw new ObjectUnknownException("Unknown source point: " + request.getSourcePoint());
}
sourcePointRef = sourcePoint.getReference();
}
Set<TCSObjectReference<Point>> destinationPointRefs = request.getDestinationPoints()
.stream()
.map(destPointName -> {
Point destPoint = vehicleService.fetchObject(Point.class, destPointName);
if (destPoint == null) {
throw new ObjectUnknownException("Unknown destination point: " + destPointName);
}
return destPoint.getReference();
})
.collect(Collectors.toSet());
Set<TCSResourceReference<?>> resourcesToAvoid = new HashSet<>();
if (request.getResourcesToAvoid() != null) {
for (String resourceName : request.getResourcesToAvoid()) {
Point point = vehicleService.fetchObject(Point.class, resourceName);
if (point != null) {
resourcesToAvoid.add(point.getReference());
continue;
}
Path path = vehicleService.fetchObject(Path.class, resourceName);
if (path != null) {
resourcesToAvoid.add(path.getReference());
continue;
}
Location location = vehicleService.fetchObject(Location.class, resourceName);
if (location != null) {
resourcesToAvoid.add(location.getReference());
continue;
}
throw new ObjectUnknownException("Unknown resource: " + resourceName);
}
}
return routerService.computeRoutes(
vehicle.getReference(),
sourcePointRef,
destinationPointRefs,
resourcesToAvoid
);
});
}
}

View File

@@ -0,0 +1,44 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding;
import static java.util.Objects.requireNonNull;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.getevents.StatusMessage;
/**
* A set of status messages sent via the status channel.
*/
public class GetEventsResponseTO {
private Instant timeStamp = Instant.now();
private List<StatusMessage> statusMessages = new ArrayList<>();
/**
* Creates a new instance.
*/
public GetEventsResponseTO() {
}
public List<StatusMessage> getStatusMessages() {
return statusMessages;
}
public GetEventsResponseTO setStatusMessages(List<StatusMessage> statusMessages) {
this.statusMessages = requireNonNull(statusMessages, "statusMessages");
return this;
}
public Instant getTimeStamp() {
return timeStamp;
}
public GetEventsResponseTO setTimeStamp(Instant timeStamp) {
this.timeStamp = requireNonNull(timeStamp, "timeStamp");
return this;
}
}

View File

@@ -0,0 +1,200 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding;
import static java.util.Objects.requireNonNull;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.opentcs.data.TCSObjectReference;
import org.opentcs.data.order.OrderConstants;
import org.opentcs.data.order.OrderSequence;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.Property;
/**
* The current state of an order sequence.
*/
public class GetOrderSequenceResponseTO {
@Nonnull
private String name;
@Nonnull
private String type = OrderConstants.TYPE_NONE;
@Nonnull
private List<String> orders = List.of();
private int finishedIndex;
private boolean complete;
private boolean finished;
private boolean failureFatal;
@Nullable
private String intendedVehicle;
@Nullable
private String processingVehicle;
@Nonnull
private List<Property> properties = List.of();
public GetOrderSequenceResponseTO(
@Nonnull
String name
) {
this.name = requireNonNull(name, "name");
}
@Nonnull
public String getName() {
return name;
}
public GetOrderSequenceResponseTO setName(
@Nonnull
String name
) {
this.name = requireNonNull(name, "name");
return this;
}
@Nonnull
public String getType() {
return type;
}
public GetOrderSequenceResponseTO setType(
@Nonnull
String type
) {
this.type = requireNonNull(type, "type");
return this;
}
@Nonnull
public List<String> getOrders() {
return orders;
}
public GetOrderSequenceResponseTO setOrders(
@Nonnull
List<String> orders
) {
this.orders = requireNonNull(orders, "orders");
return this;
}
public int getFinishedIndex() {
return finishedIndex;
}
public GetOrderSequenceResponseTO setFinishedIndex(int finishedIndex) {
this.finishedIndex = finishedIndex;
return this;
}
public boolean isComplete() {
return complete;
}
public GetOrderSequenceResponseTO setComplete(boolean complete) {
this.complete = complete;
return this;
}
public boolean isFinished() {
return finished;
}
public GetOrderSequenceResponseTO setFinished(boolean finished) {
this.finished = finished;
return this;
}
public boolean isFailureFatal() {
return failureFatal;
}
public GetOrderSequenceResponseTO setFailureFatal(boolean failureFatal) {
this.failureFatal = failureFatal;
return this;
}
@Nullable
public String getIntendedVehicle() {
return intendedVehicle;
}
public GetOrderSequenceResponseTO setIntendedVehicle(
@Nullable
String intendedVehicle
) {
this.intendedVehicle = intendedVehicle;
return this;
}
@Nullable
public String getProcessingVehicle() {
return processingVehicle;
}
public GetOrderSequenceResponseTO setProcessingVehicle(
@Nullable
String processingVehicle
) {
this.processingVehicle = processingVehicle;
return this;
}
@Nonnull
public List<Property> getProperties() {
return properties;
}
public GetOrderSequenceResponseTO setProperties(
@Nonnull
List<Property> properties
) {
this.properties = requireNonNull(properties, "properties");
return this;
}
public static GetOrderSequenceResponseTO fromOrderSequence(OrderSequence orderSequence) {
return new GetOrderSequenceResponseTO(orderSequence.getName())
.setComplete(orderSequence.isComplete())
.setFailureFatal(orderSequence.isFailureFatal())
.setFinished(orderSequence.isFinished())
.setFinishedIndex(orderSequence.getFinishedIndex())
.setType(orderSequence.getType())
.setOrders(
orderSequence.getOrders()
.stream()
.map(TCSObjectReference::getName)
.collect(Collectors.toList())
)
.setProcessingVehicle(nameOfNullableReference(orderSequence.getProcessingVehicle()))
.setIntendedVehicle(nameOfNullableReference(orderSequence.getIntendedVehicle()))
.setProperties(convertProperties(orderSequence.getProperties()));
}
private static String nameOfNullableReference(
@Nullable
TCSObjectReference<?> reference
) {
return reference == null ? null : reference.getName();
}
private static List<Property> convertProperties(Map<String, String> properties) {
return properties.entrySet().stream()
.map(property -> new Property(property.getKey(), property.getValue()))
.collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,97 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding;
import static java.util.Objects.requireNonNull;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import java.util.List;
import java.util.stream.Collectors;
import org.opentcs.drivers.peripherals.management.PeripheralAttachmentInformation;
/**
*/
public class GetPeripheralAttachmentInfoResponseTO {
@Nonnull
private String locationName;
@Nonnull
private List<String> availableCommAdapters;
@Nonnull
private String attachedCommAdapter;
public GetPeripheralAttachmentInfoResponseTO(
@Nonnull
String locationName,
@Nonnull
String attachedCommAdapter,
@Nonnull
List<String> availableCommAdapters
) {
this.locationName = requireNonNull(locationName, "locationName");
this.attachedCommAdapter = requireNonNull(attachedCommAdapter, "attachedCommAdapter");
this.availableCommAdapters = requireNonNull(availableCommAdapters, "availableCommAdapters");
}
@Nonnull
public String getLocationName() {
return locationName;
}
public GetPeripheralAttachmentInfoResponseTO setLocationName(
@Nonnull
String locationName
) {
this.locationName = requireNonNull(locationName, "locationName");
return this;
}
@Nonnull
public List<String> getAvailableCommAdapters() {
return availableCommAdapters;
}
public GetPeripheralAttachmentInfoResponseTO setAvailableCommAdapters(
@Nonnull
List<String> availableCommAdapters
) {
this.availableCommAdapters = requireNonNull(availableCommAdapters, "availableCommAdapters");
return this;
}
@Nonnull
public String getAttachedCommAdapter() {
return attachedCommAdapter;
}
public GetPeripheralAttachmentInfoResponseTO setAttachedCommAdapter(
@Nonnull
String attachedCommAdapter
) {
this.attachedCommAdapter = requireNonNull(attachedCommAdapter, "attachedCommAdapter");
return this;
}
public static GetPeripheralAttachmentInfoResponseTO fromAttachmentInformation(
@Nullable
PeripheralAttachmentInformation peripheralAttachmentInfo
) {
if (peripheralAttachmentInfo == null) {
return null;
}
List<String> availableAdapters = peripheralAttachmentInfo.getAvailableCommAdapters()
.stream()
.map(description -> description.getClass().getName())
.collect(Collectors.toList());
return new GetPeripheralAttachmentInfoResponseTO(
peripheralAttachmentInfo.getLocationReference().getName(),
peripheralAttachmentInfo.getAttachedCommAdapter().getClass().getName(),
availableAdapters
);
}
}

View File

@@ -0,0 +1,142 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding;
import java.time.Instant;
import java.util.List;
import java.util.stream.Collectors;
import org.opentcs.data.peripherals.PeripheralJob;
import org.opentcs.data.peripherals.PeripheralJob.State;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.PeripheralOperationDescription;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.Property;
/**
* The current state of a peripheral job.
*/
public class GetPeripheralJobResponseTO {
private String name;
private String reservationToken;
private String relatedVehicle;
private String relatedTransportOrder;
private PeripheralOperationDescription peripheralOperation;
private State state;
private Instant creationTime;
private Instant finishedTime;
private List<Property> properties;
public GetPeripheralJobResponseTO() {
}
public String getName() {
return name;
}
public GetPeripheralJobResponseTO setName(String name) {
this.name = name;
return this;
}
public String getReservationToken() {
return reservationToken;
}
public GetPeripheralJobResponseTO setReservationToken(String reservationToken) {
this.reservationToken = reservationToken;
return this;
}
public String getRelatedVehicle() {
return relatedVehicle;
}
public GetPeripheralJobResponseTO setRelatedVehicle(String relatedVehicle) {
this.relatedVehicle = relatedVehicle;
return this;
}
public String getRelatedTransportOrder() {
return relatedTransportOrder;
}
public GetPeripheralJobResponseTO setRelatedTransportOrder(String relatedTransportOrder) {
this.relatedTransportOrder = relatedTransportOrder;
return this;
}
public PeripheralOperationDescription getPeripheralOperation() {
return peripheralOperation;
}
public GetPeripheralJobResponseTO setPeripheralOperation(
PeripheralOperationDescription peripheralOperation
) {
this.peripheralOperation = peripheralOperation;
return this;
}
public State getState() {
return state;
}
public GetPeripheralJobResponseTO setState(State state) {
this.state = state;
return this;
}
public Instant getCreationTime() {
return creationTime;
}
public GetPeripheralJobResponseTO setCreationTime(Instant creationTime) {
this.creationTime = creationTime;
return this;
}
public Instant getFinishedTime() {
return finishedTime;
}
public GetPeripheralJobResponseTO setFinishedTime(Instant finishedTime) {
this.finishedTime = finishedTime;
return this;
}
public List<Property> getProperties() {
return properties;
}
public GetPeripheralJobResponseTO setProperties(List<Property> properties) {
this.properties = properties;
return this;
}
public static GetPeripheralJobResponseTO fromPeripheralJob(PeripheralJob job) {
GetPeripheralJobResponseTO state = new GetPeripheralJobResponseTO();
state.name = job.getName();
state.reservationToken = job.getReservationToken();
if (job.getRelatedVehicle() != null) {
state.relatedVehicle = job.getRelatedVehicle().getName();
}
if (job.getRelatedTransportOrder() != null) {
state.relatedTransportOrder = job.getRelatedTransportOrder().getName();
}
state.peripheralOperation
= PeripheralOperationDescription.fromPeripheralOperation(job.getPeripheralOperation());
state.state = job.getState();
state.creationTime = job.getCreationTime();
state.finishedTime = job.getFinishedTime();
state.properties = job.getProperties().entrySet().stream()
.map(entry -> new Property(entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
return state;
}
}

View File

@@ -0,0 +1,165 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding;
import static java.util.Objects.requireNonNull;
import jakarta.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.opentcs.data.TCSObjectReference;
import org.opentcs.data.order.TransportOrder;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.DestinationState;
/**
*/
public class GetTransportOrderResponseTO {
private boolean dispensable;
private String name = "";
private String peripheralReservationToken;
private String wrappingSequence;
private String type = "";
private TransportOrder.State state = TransportOrder.State.RAW;
private String intendedVehicle;
private String processingVehicle;
private List<DestinationState> destinations = new ArrayList<>();
public GetTransportOrderResponseTO() {
}
public boolean isDispensable() {
return dispensable;
}
public GetTransportOrderResponseTO setDispensable(boolean dispensable) {
this.dispensable = dispensable;
return this;
}
public String getName() {
return name;
}
public GetTransportOrderResponseTO setName(String name) {
this.name = requireNonNull(name, "name");
return this;
}
public String getPeripheralReservationToken() {
return peripheralReservationToken;
}
public GetTransportOrderResponseTO setPeripheralReservationToken(
String peripheralReservationToken
) {
this.peripheralReservationToken = peripheralReservationToken;
return this;
}
public String getWrappingSequence() {
return wrappingSequence;
}
public GetTransportOrderResponseTO setWrappingSequence(String wrappingSequence) {
this.wrappingSequence = wrappingSequence;
return this;
}
public String getType() {
return type;
}
public GetTransportOrderResponseTO setType(String type) {
this.type = type;
return this;
}
public TransportOrder.State getState() {
return state;
}
public GetTransportOrderResponseTO setState(TransportOrder.State state) {
this.state = requireNonNull(state, "state");
return this;
}
public String getIntendedVehicle() {
return intendedVehicle;
}
public GetTransportOrderResponseTO setIntendedVehicle(String intendedVehicle) {
this.intendedVehicle = intendedVehicle;
return this;
}
public String getProcessingVehicle() {
return processingVehicle;
}
public GetTransportOrderResponseTO setProcessingVehicle(String processingVehicle) {
this.processingVehicle = processingVehicle;
return this;
}
public List<DestinationState> getDestinations() {
return destinations;
}
public GetTransportOrderResponseTO setDestinations(List<DestinationState> destinations) {
this.destinations = requireNonNull(destinations, "destinations");
return this;
}
/**
* Creates a new instance from a <code>TransportOrder</code>.
*
* @param transportOrder The transport order to create an instance from.
* @return A new instance containing the data from the given transport order.
*/
public static GetTransportOrderResponseTO fromTransportOrder(TransportOrder transportOrder) {
if (transportOrder == null) {
return null;
}
GetTransportOrderResponseTO transportOrderState = new GetTransportOrderResponseTO();
transportOrderState.setDispensable(transportOrder.isDispensable());
transportOrderState.setName(transportOrder.getName());
transportOrderState.setPeripheralReservationToken(
transportOrder.getPeripheralReservationToken()
);
transportOrderState.setWrappingSequence(
nameOfNullableReference(transportOrder.getWrappingSequence())
);
transportOrderState.setType(transportOrder.getType());
transportOrderState.setDestinations(
transportOrder.getAllDriveOrders()
.stream()
.map(driveOrder -> DestinationState.fromDriveOrder(driveOrder))
.collect(Collectors.toList())
);
transportOrderState.setIntendedVehicle(
nameOfNullableReference(transportOrder.getIntendedVehicle())
);
transportOrderState.setProcessingVehicle(
nameOfNullableReference(transportOrder.getProcessingVehicle())
);
transportOrderState.setState(transportOrder.getState());
return transportOrderState;
}
private static String nameOfNullableReference(
@Nullable
TCSObjectReference<?> reference
) {
return reference == null ? null : reference.getName();
}
}

View File

@@ -0,0 +1,95 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding;
import static java.util.Objects.requireNonNull;
import java.util.List;
import java.util.stream.Collectors;
import org.opentcs.drivers.vehicle.management.VehicleAttachmentInformation;
/**
* Arranges the data from a vehicle's <code>AttachmentInformation</code> for transferring.
*/
public class GetVehicleAttachmentInfoResponseTO {
/**
* The vehicle this attachment information belongs to.
*/
private String vehicleName;
/**
* The list of comm adapters available to be attached to the referenced vehicle.
*/
private List<String> availableCommAdapters;
/**
* The comm adapter attached to the referenced vehicle.
*/
private String attachedCommAdapter;
public GetVehicleAttachmentInfoResponseTO() {
}
public GetVehicleAttachmentInfoResponseTO setVehicleName(String vehicleName) {
this.vehicleName = requireNonNull(vehicleName, "vehicleName");
return this;
}
public String getVehicleName() {
return vehicleName;
}
public GetVehicleAttachmentInfoResponseTO setAvailableCommAdapters(
List<String> availableCommAdapters
) {
this.availableCommAdapters = requireNonNull(availableCommAdapters, "availableCommAdapters");
return this;
}
public List<String> getAvailableCommAdapters() {
return availableCommAdapters;
}
public GetVehicleAttachmentInfoResponseTO setAttachedCommAdapter(String attachedCommAdapter) {
this.attachedCommAdapter = requireNonNull(attachedCommAdapter, "attachedCommAdapter");
return this;
}
public String getAttachedCommAdapter() {
return attachedCommAdapter;
}
/**
* Creates a new instance from <code>AttachmentInformation</code>.
*
* @param attachmentInformation The <code>AttachmentInformation</code> to create an
* instance from.
* @return A new instance containing the data from the given <code>AttachmentInformation</code>.
*/
public static GetVehicleAttachmentInfoResponseTO fromAttachmentInformation(
VehicleAttachmentInformation attachmentInformation
) {
if (attachmentInformation == null) {
return null;
}
GetVehicleAttachmentInfoResponseTO attachmentInformationTO
= new GetVehicleAttachmentInfoResponseTO();
attachmentInformationTO.setVehicleName(
attachmentInformation.getVehicleReference()
.getName()
);
attachmentInformationTO.setAvailableCommAdapters(
attachmentInformation.getAvailableCommAdapters()
.stream()
.map(description -> description.getClass().getName())
.collect(Collectors.toList())
);
attachmentInformationTO.setAttachedCommAdapter(
attachmentInformation.getAttachedCommAdapter()
.getClass()
.getName()
);
return attachmentInformationTO;
}
}

View File

@@ -0,0 +1,391 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding;
import static java.util.Objects.requireNonNull;
import jakarta.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.opentcs.data.TCSObjectReference;
import org.opentcs.data.model.TCSResourceReference;
import org.opentcs.data.model.Vehicle;
import org.opentcs.data.model.Vehicle.IntegrationLevel;
import org.opentcs.data.model.Vehicle.ProcState;
import org.opentcs.data.model.Vehicle.State;
import org.opentcs.util.annotations.ScheduledApiChange;
/**
*/
public class GetVehicleResponseTO {
private String name;
private Map<String, String> properties = new HashMap<>();
private int length;
private int energyLevelGood;
private int energyLevelCritical;
private int energyLevelSufficientlyRecharged;
private int energyLevelFullyRecharged;
private int energyLevel;
private IntegrationLevel integrationLevel = IntegrationLevel.TO_BE_RESPECTED;
private boolean paused;
private ProcState procState = ProcState.IDLE;
private String transportOrder;
private String currentPosition;
private PrecisePosition precisePosition;
private double orientationAngle;
private State state = State.UNKNOWN;
private List<List<String>> allocatedResources = new ArrayList<>();
private List<List<String>> claimedResources = new ArrayList<>();
private List<String> allowedOrderTypes = new ArrayList<>();
private String envelopeKey;
public GetVehicleResponseTO() {
}
public String getName() {
return name;
}
public GetVehicleResponseTO setName(String name) {
this.name = requireNonNull(name, "name");
return this;
}
public int getLength() {
return length;
}
public GetVehicleResponseTO setLength(int length) {
this.length = length;
return this;
}
public int getEnergyLevelGood() {
return energyLevelGood;
}
public GetVehicleResponseTO setEnergyLevelGood(int energyLevelGood) {
this.energyLevelGood = energyLevelGood;
return this;
}
public int getEnergyLevelCritical() {
return energyLevelCritical;
}
public GetVehicleResponseTO setEnergyLevelCritical(int energyLevelCritical) {
this.energyLevelCritical = energyLevelCritical;
return this;
}
public int getEnergyLevelSufficientlyRecharged() {
return energyLevelSufficientlyRecharged;
}
public GetVehicleResponseTO setEnergyLevelSufficientlyRecharged(
int energyLevelSufficientlyRecharged
) {
this.energyLevelSufficientlyRecharged = energyLevelSufficientlyRecharged;
return this;
}
public int getEnergyLevelFullyRecharged() {
return energyLevelFullyRecharged;
}
public GetVehicleResponseTO setEnergyLevelFullyRecharged(int energyLevelFullyRecharged) {
this.energyLevelFullyRecharged = energyLevelFullyRecharged;
return this;
}
public int getEnergyLevel() {
return energyLevel;
}
public GetVehicleResponseTO setEnergyLevel(int energyLevel) {
this.energyLevel = energyLevel;
return this;
}
public IntegrationLevel getIntegrationLevel() {
return integrationLevel;
}
public GetVehicleResponseTO setIntegrationLevel(IntegrationLevel integrationLevel) {
this.integrationLevel = requireNonNull(integrationLevel, "integrationLevel");
return this;
}
public boolean isPaused() {
return paused;
}
public GetVehicleResponseTO setPaused(boolean paused) {
this.paused = paused;
return this;
}
public ProcState getProcState() {
return procState;
}
public GetVehicleResponseTO setProcState(Vehicle.ProcState procState) {
this.procState = requireNonNull(procState, "procState");
return this;
}
public String getTransportOrder() {
return transportOrder;
}
public GetVehicleResponseTO setTransportOrder(String transportOrder) {
this.transportOrder = transportOrder;
return this;
}
public String getCurrentPosition() {
return currentPosition;
}
public GetVehicleResponseTO setCurrentPosition(String currentPosition) {
this.currentPosition = currentPosition;
return this;
}
public PrecisePosition getPrecisePosition() {
return precisePosition;
}
public GetVehicleResponseTO setPrecisePosition(PrecisePosition precisePosition) {
this.precisePosition = precisePosition;
return this;
}
public double getOrientationAngle() {
return orientationAngle;
}
public GetVehicleResponseTO setOrientationAngle(double orientationAngle) {
this.orientationAngle = orientationAngle;
return this;
}
public State getState() {
return state;
}
public GetVehicleResponseTO setState(State state) {
this.state = requireNonNull(state, "state");
return this;
}
public List<List<String>> getAllocatedResources() {
return allocatedResources;
}
public GetVehicleResponseTO setAllocatedResources(List<List<String>> allocatedResources) {
this.allocatedResources = requireNonNull(allocatedResources, "allocatedResources");
return this;
}
public List<List<String>> getClaimedResources() {
return claimedResources;
}
public GetVehicleResponseTO setClaimedResources(List<List<String>> claimedResources) {
this.claimedResources = requireNonNull(claimedResources, "claimedResources");
return this;
}
public Map<String, String> getProperties() {
return properties;
}
public GetVehicleResponseTO setProperties(Map<String, String> properties) {
this.properties = requireNonNull(properties, "properties");
return this;
}
public List<String> getAllowedOrderTypes() {
return allowedOrderTypes;
}
public GetVehicleResponseTO setAllowedOrderTypes(List<String> allowedOrderTypes) {
this.allowedOrderTypes = requireNonNull(allowedOrderTypes, "allowedOrderTypes");
return this;
}
@ScheduledApiChange(when = "7.0", details = "Envelope key will become non-null.")
@Nullable
public String getEnvelopeKey() {
return envelopeKey;
}
@ScheduledApiChange(when = "7.0", details = "Envelope key will become non-null.")
public GetVehicleResponseTO setEnvelopeKey(
@Nullable
String envelopeKey
) {
this.envelopeKey = envelopeKey;
return this;
}
/**
* Creates a <Code>VehicleState</Code> instance from a <Code>Vehicle</Code> instance.
*
* @param vehicle The vehicle whose properties will be used to create a <Code>VehicleState</Code>
* instance.
* @return A new <Code>VehicleState</Code> instance filled with data from the given vehicle.
*/
public static GetVehicleResponseTO fromVehicle(Vehicle vehicle) {
if (vehicle == null) {
return null;
}
GetVehicleResponseTO vehicleState = new GetVehicleResponseTO();
vehicleState.setName(vehicle.getName());
vehicleState.setProperties(vehicle.getProperties());
vehicleState.setLength((int) vehicle.getBoundingBox().getLength());
vehicleState.setEnergyLevelCritical(
vehicle.getEnergyLevelThresholdSet().getEnergyLevelCritical()
);
vehicleState.setEnergyLevelGood(vehicle.getEnergyLevelThresholdSet().getEnergyLevelGood());
vehicleState.setEnergyLevelSufficientlyRecharged(
vehicle.getEnergyLevelThresholdSet().getEnergyLevelSufficientlyRecharged()
);
vehicleState.setEnergyLevelFullyRecharged(
vehicle.getEnergyLevelThresholdSet().getEnergyLevelFullyRecharged()
);
vehicleState.setEnergyLevel(vehicle.getEnergyLevel());
vehicleState.setIntegrationLevel(vehicle.getIntegrationLevel());
vehicleState.setPaused(vehicle.isPaused());
vehicleState.setProcState(vehicle.getProcState());
vehicleState.setTransportOrder(nameOfNullableReference(vehicle.getTransportOrder()));
vehicleState.setCurrentPosition(nameOfNullableReference(vehicle.getCurrentPosition()));
if (vehicle.getPose().getPosition() != null) {
vehicleState.setPrecisePosition(
new PrecisePosition(
vehicle.getPose().getPosition().getX(),
vehicle.getPose().getPosition().getY(),
vehicle.getPose().getPosition().getZ()
)
);
}
else {
vehicleState.setPrecisePosition(null);
}
vehicleState.setOrientationAngle(vehicle.getPose().getOrientationAngle());
vehicleState.setState(vehicle.getState());
vehicleState.setAllocatedResources(toListOfListOfNames(vehicle.getAllocatedResources()));
vehicleState.setClaimedResources(toListOfListOfNames(vehicle.getClaimedResources()));
vehicleState.setEnvelopeKey(vehicle.getEnvelopeKey());
vehicleState.setAllowedOrderTypes(
vehicle.getAllowedOrderTypes()
.stream()
.sorted()
.collect(Collectors.toCollection(ArrayList::new))
);
return vehicleState;
}
private static String nameOfNullableReference(
@Nullable
TCSObjectReference<?> reference
) {
return reference == null ? null : reference.getName();
}
private static List<List<String>> toListOfListOfNames(
List<Set<TCSResourceReference<?>>> resources
) {
List<List<String>> result = new ArrayList<>(resources.size());
for (Set<TCSResourceReference<?>> resSet : resources) {
result.add(
resSet.stream()
.map(resRef -> resRef.getName())
.collect(Collectors.toList())
);
}
return result;
}
/**
* A precise position of a vehicle.
*/
public static class PrecisePosition {
private long x;
private long y;
private long z;
/**
* Creates a new instance.
*/
public PrecisePosition() {
}
/**
* Creates a new instance.
*
* @param x x value
* @param y y value
* @param z z value
*/
public PrecisePosition(long x, long y, long z) {
this.x = x;
this.y = y;
this.z = z;
}
public long getX() {
return x;
}
public void setX(long x) {
this.x = x;
}
public long getY() {
return y;
}
public void setY(long y) {
this.y = y;
}
public long getZ() {
return z;
}
public void setZ(long z) {
this.z = z;
}
}
}

View File

@@ -0,0 +1,160 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.Nonnull;
import java.util.List;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.BlockTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.LocationTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.LocationTypeTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.PathTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.PointTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.VehicleTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.VisualLayoutTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.PropertyTO;
/**
*/
public class PlantModelTO {
private String name;
private List<PointTO> points = List.of();
private List<PathTO> paths = List.of();
private List<LocationTypeTO> locationTypes = List.of();
private List<LocationTO> locations = List.of();
private List<BlockTO> blocks = List.of();
private List<VehicleTO> vehicles = List.of();
private VisualLayoutTO visualLayout = new VisualLayoutTO("unnamed");
private List<PropertyTO> properties = List.of();
@JsonCreator
public PlantModelTO(
@Nonnull
@JsonProperty(value = "name", required = true)
String name
) {
this.name = requireNonNull(name, "name");
}
@Nonnull
public String getName() {
return name;
}
public PlantModelTO setName(
@Nonnull
String name
) {
this.name = requireNonNull(name, "name");
return this;
}
@Nonnull
public List<PropertyTO> getProperties() {
return properties;
}
public PlantModelTO setProperties(
@Nonnull
List<PropertyTO> properties
) {
this.properties = requireNonNull(properties, "properties");
return this;
}
@Nonnull
public List<PointTO> getPoints() {
return points;
}
public PlantModelTO setPoints(
@Nonnull
List<PointTO> points
) {
this.points = requireNonNull(points, "points");
return this;
}
@Nonnull
public List<PathTO> getPaths() {
return paths;
}
public PlantModelTO setPaths(
@Nonnull
List<PathTO> paths
) {
this.paths = requireNonNull(paths, "paths");
return this;
}
@Nonnull
public List<LocationTO> getLocations() {
return locations;
}
public PlantModelTO setLocations(
@Nonnull
List<LocationTO> locations
) {
this.locations = requireNonNull(locations, "locations");
return this;
}
@Nonnull
public List<LocationTypeTO> getLocationTypes() {
return locationTypes;
}
public PlantModelTO setLocationTypes(
@Nonnull
List<LocationTypeTO> locationTypes
) {
this.locationTypes = requireNonNull(locationTypes, "locationTypes");
return this;
}
@Nonnull
public List<BlockTO> getBlocks() {
return blocks;
}
public PlantModelTO setBlocks(
@Nonnull
List<BlockTO> blocks
) {
this.blocks = requireNonNull(blocks, "blocks");
return this;
}
@Nonnull
public List<VehicleTO> getVehicles() {
return vehicles;
}
public PlantModelTO setVehicles(
@Nonnull
List<VehicleTO> vehicles
) {
this.vehicles = requireNonNull(vehicles, "vehicles");
return this;
}
@Nonnull
public VisualLayoutTO getVisualLayout() {
return visualLayout;
}
public PlantModelTO setVisualLayout(
@Nonnull
VisualLayoutTO visualLayout
) {
this.visualLayout = requireNonNull(visualLayout, "visualLayout");
return this;
}
}

View File

@@ -0,0 +1,92 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding;
import static java.util.Objects.requireNonNull;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import java.util.List;
import org.opentcs.data.order.OrderConstants;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.Property;
/**
* An order sequence to be created by the kernel.
*/
public class PostOrderSequenceRequestTO {
private boolean incompleteName;
@Nonnull
private String type = OrderConstants.TYPE_NONE;
@Nullable
private String intendedVehicle;
private boolean failureFatal;
@Nonnull
private List<Property> properties = List.of();
public PostOrderSequenceRequestTO() {
}
public boolean isIncompleteName() {
return incompleteName;
}
public PostOrderSequenceRequestTO setIncompleteName(boolean incompleteName) {
this.incompleteName = incompleteName;
return this;
}
@Nonnull
public String getType() {
return type;
}
public PostOrderSequenceRequestTO setType(
@Nonnull
String type
) {
this.type = requireNonNull(type, "type");
return this;
}
@Nullable
public String getIntendedVehicle() {
return intendedVehicle;
}
public PostOrderSequenceRequestTO setIntendedVehicle(
@Nullable
String intendedVehicle
) {
this.intendedVehicle = intendedVehicle;
return this;
}
public boolean isFailureFatal() {
return failureFatal;
}
public PostOrderSequenceRequestTO setFailureFatal(boolean failureFatal) {
this.failureFatal = failureFatal;
return this;
}
@Nonnull
public List<Property> getProperties() {
return properties;
}
public PostOrderSequenceRequestTO setProperties(
@Nonnull
List<Property> properties
) {
this.properties = requireNonNull(properties, "properties");
return this;
}
}

View File

@@ -0,0 +1,85 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding;
import java.util.List;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.PeripheralOperationDescription;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.Property;
/**
* A peripheral job to be processed by the kernel.
*/
public class PostPeripheralJobRequestTO {
private boolean incompleteName;
private String reservationToken;
private String relatedVehicle;
private String relatedTransportOrder;
private PeripheralOperationDescription peripheralOperation;
private List<Property> properties;
public PostPeripheralJobRequestTO() {
}
public boolean isIncompleteName() {
return incompleteName;
}
public PostPeripheralJobRequestTO setIncompleteName(boolean incompleteName) {
this.incompleteName = incompleteName;
return this;
}
public String getReservationToken() {
return reservationToken;
}
public PostPeripheralJobRequestTO setReservationToken(String reservationToken) {
this.reservationToken = reservationToken;
return this;
}
public String getRelatedVehicle() {
return relatedVehicle;
}
public PostPeripheralJobRequestTO setRelatedVehicle(String relatedVehicle) {
this.relatedVehicle = relatedVehicle;
return this;
}
public String getRelatedTransportOrder() {
return relatedTransportOrder;
}
public PostPeripheralJobRequestTO setRelatedTransportOrder(String relatedTransportOrder) {
this.relatedTransportOrder = relatedTransportOrder;
return this;
}
public PeripheralOperationDescription getPeripheralOperation() {
return peripheralOperation;
}
public PostPeripheralJobRequestTO setPeripheralOperation(
PeripheralOperationDescription peripheralOperation
) {
this.peripheralOperation = peripheralOperation;
return this;
}
public List<Property> getProperties() {
return properties;
}
public PostPeripheralJobRequestTO setProperties(List<Property> properties) {
this.properties = properties;
return this;
}
}

View File

@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.Nonnull;
import java.util.List;
/**
* A list of paths that are to be updated when the routing topology gets updated.
*/
public class PostTopologyUpdateRequestTO {
@Nonnull
private List<String> paths;
@JsonCreator
public PostTopologyUpdateRequestTO(
@Nonnull
@JsonProperty(value = "paths", required = true)
List<String> paths
) {
this.paths = requireNonNull(paths, "paths");
}
@Nonnull
public List<String> getPaths() {
return paths;
}
public PostTopologyUpdateRequestTO setPaths(
@Nonnull
List<String> paths
) {
this.paths = requireNonNull(paths, "paths");
return this;
}
}

View File

@@ -0,0 +1,210 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import java.time.Instant;
import java.util.List;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.posttransportorder.Destination;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.Property;
/**
* A transport order to be processed by the kernel.
*/
public class PostTransportOrderRequestTO {
private boolean incompleteName;
private boolean dispensable;
private Instant deadline;
private String intendedVehicle;
private String peripheralReservationToken;
private String wrappingSequence;
private String type;
private List<Destination> destinations;
private List<Property> properties;
private List<String> dependencies;
// CHECKSTYLE:OFF (because of very long parameter declarations)
@JsonCreator
public PostTransportOrderRequestTO(
@JsonProperty(required = false, value = "incompleteName")
boolean incompleteName,
@JsonProperty(required = false, value = "dispensable")
boolean dispensable,
@Nullable
@JsonProperty(required = false, value = "deadline")
Instant deadline,
@Nullable
@JsonProperty(required = false, value = "intendedVehicle")
String intendedVehicle,
@Nullable
@JsonProperty(required = false, value = "peripheralReservationToken")
String peripheralReservationToken,
@Nullable
@JsonProperty(required = false, value = "wrappingSequence")
String wrappingSequence,
@Nullable
@JsonProperty(required = false, value = "type")
String type,
@Nonnull
@JsonProperty(required = true, value = "destinations")
List<Destination> destinations,
@Nullable
@JsonProperty(required = false, value = "properties")
List<Property> properties,
@Nullable
@JsonProperty(required = false, value = "dependencies")
List<String> dependencies
) {
this.incompleteName = incompleteName;
this.dispensable = dispensable;
this.deadline = deadline;
this.intendedVehicle = intendedVehicle;
this.peripheralReservationToken = peripheralReservationToken;
this.wrappingSequence = wrappingSequence;
this.type = type;
this.destinations = requireNonNull(destinations, "destinations");
this.properties = properties;
this.dependencies = dependencies;
}
// CHECKSTYLE:ON
public PostTransportOrderRequestTO() {
}
public boolean isIncompleteName() {
return incompleteName;
}
public PostTransportOrderRequestTO setIncompleteName(boolean incompleteName) {
this.incompleteName = incompleteName;
return this;
}
public boolean isDispensable() {
return dispensable;
}
public PostTransportOrderRequestTO setDispensable(boolean dispensable) {
this.dispensable = dispensable;
return this;
}
@Nullable
public Instant getDeadline() {
return deadline;
}
public PostTransportOrderRequestTO setDeadline(
@Nullable
Instant deadline
) {
this.deadline = deadline;
return this;
}
@Nullable
public String getIntendedVehicle() {
return intendedVehicle;
}
public PostTransportOrderRequestTO setIntendedVehicle(
@Nullable
String intendedVehicle
) {
this.intendedVehicle = intendedVehicle;
return this;
}
@Nullable
public String getPeripheralReservationToken() {
return peripheralReservationToken;
}
public PostTransportOrderRequestTO setPeripheralReservationToken(
@Nullable
String peripheralReservationToken
) {
this.peripheralReservationToken = peripheralReservationToken;
return this;
}
@Nullable
public String getWrappingSequence() {
return wrappingSequence;
}
public PostTransportOrderRequestTO setWrappingSequence(
@Nullable
String wrappingSequence
) {
this.wrappingSequence = wrappingSequence;
return this;
}
@Nullable
public String getType() {
return type;
}
public PostTransportOrderRequestTO setType(
@Nullable
String type
) {
this.type = type;
return this;
}
@Nonnull
public List<Destination> getDestinations() {
return destinations;
}
public PostTransportOrderRequestTO setDestinations(
@Nonnull
List<Destination> destinations
) {
this.destinations = requireNonNull(destinations, "destinations");
return this;
}
@Nullable
public List<Property> getProperties() {
return properties;
}
public PostTransportOrderRequestTO setProperties(
@Nullable
List<Property> properties
) {
this.properties = properties;
return this;
}
@Nullable
public List<String> getDependencies() {
return dependencies;
}
public PostTransportOrderRequestTO setDependencies(
@Nullable
List<String> dependencies
) {
this.dependencies = dependencies;
return this;
}
}

View File

@@ -0,0 +1,70 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import java.util.List;
/**
*
*/
public class PostVehicleRoutesRequestTO {
private String sourcePoint;
private List<String> destinationPoints;
private List<String> resourcesToAvoid;
@JsonCreator
@SuppressWarnings("checkstyle:LineLength")
public PostVehicleRoutesRequestTO(
@Nonnull
@JsonProperty(value = "destinationPoints", required = true)
List<String> destinationPoints
) {
this.destinationPoints = requireNonNull(destinationPoints, "destinationPoints");
}
@Nullable
public String getSourcePoint() {
return sourcePoint;
}
public PostVehicleRoutesRequestTO setSourcePoint(
@Nullable
String sourcePoint
) {
this.sourcePoint = sourcePoint;
return this;
}
@Nonnull
public List<String> getDestinationPoints() {
return destinationPoints;
}
public PostVehicleRoutesRequestTO setDestinationPoints(
@Nonnull
List<String> destinationPoints
) {
this.destinationPoints = requireNonNull(destinationPoints, "destinationPoints");
return this;
}
@Nullable
public List<String> getResourcesToAvoid() {
return resourcesToAvoid;
}
public PostVehicleRoutesRequestTO setResourcesToAvoid(
@Nullable
List<String> resourcesToAvoid
) {
this.resourcesToAvoid = resourcesToAvoid;
return this;
}
}

View File

@@ -0,0 +1,79 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding;
import static java.util.Objects.requireNonNull;
import jakarta.annotation.Nonnull;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.opentcs.data.TCSObjectReference;
import org.opentcs.data.model.Point;
import org.opentcs.data.order.Route;
import org.opentcs.data.order.Route.Step;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.getvehicleroutes.RouteTO;
/**
*
*/
public class PostVehicleRoutesResponseTO {
private List<RouteTO> routes = List.of();
public PostVehicleRoutesResponseTO() {
}
@Nonnull
public List<RouteTO> getRoutes() {
return routes;
}
public PostVehicleRoutesResponseTO setRoutes(
@Nonnull
List<RouteTO> routes
) {
this.routes = requireNonNull(routes, "routes");
return this;
}
public static PostVehicleRoutesResponseTO fromMap(
Map<TCSObjectReference<Point>, Route> routeMap
) {
return new PostVehicleRoutesResponseTO()
.setRoutes(
routeMap.entrySet().stream()
.map(PostVehicleRoutesResponseTO::toRouteTO)
.collect(Collectors.toList())
);
}
private static RouteTO toRouteTO(Map.Entry<TCSObjectReference<Point>, Route> entry) {
if (entry.getValue() == null) {
return new RouteTO()
.setDestinationPoint(entry.getKey().getName())
.setCosts(-1)
.setSteps(null);
}
return new RouteTO()
.setDestinationPoint(entry.getKey().getName())
.setCosts(entry.getValue().getCosts())
.setSteps(toSteps(entry.getValue().getSteps()));
}
private static List<RouteTO.Step> toSteps(List<Step> steps) {
return steps.stream()
.map(
step -> new RouteTO.Step()
.setDestinationPoint(step.getDestinationPoint().getName())
.setSourcePoint(
(step.getSourcePoint() != null) ? step.getSourcePoint().getName() : null
)
.setPath((step.getPath() != null) ? step.getPath().getName() : null)
.setVehicleOrientation(step.getVehicleOrientation().name())
)
.collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.Nonnull;
import java.util.List;
/**
* An update for a vehicle's list of allowed order types.
*/
public class PutVehicleAllowedOrderTypesTO {
@Nonnull
private List<String> orderTypes;
@JsonCreator
public PutVehicleAllowedOrderTypesTO(
@Nonnull
@JsonProperty(value = "orderTypes", required = true)
List<String> orderTypes
) {
this.orderTypes = requireNonNull(orderTypes, "orderTypes");
}
@Nonnull
public List<String> getOrderTypes() {
return orderTypes;
}
public PutVehicleAllowedOrderTypesTO setOrderTypes(
@Nonnull
List<String> orderTypes
) {
this.orderTypes = requireNonNull(orderTypes, "orderTypes");
return this;
}
}

View File

@@ -0,0 +1,74 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* An update for a vehicle's energy level threshold set.
*/
public class PutVehicleEnergyLevelThresholdSetTO {
private int energyLevelCritical;
private int energyLevelGood;
private int energyLevelSufficientlyRecharged;
private int energyLevelFullyRecharged;
@JsonCreator
public PutVehicleEnergyLevelThresholdSetTO(
@JsonProperty(value = "energyLevelCritical", required = true)
int energyLevelCritical,
@JsonProperty(value = "energyLevelGood", required = true)
int energyLevelGood,
@JsonProperty(value = "energyLevelSufficientlyRecharged", required = true)
int energyLevelSufficientlyRecharged,
@JsonProperty(value = "energyLevelFullyRecharged", required = true)
int energyLevelFullyRecharged
) {
this.energyLevelCritical = energyLevelCritical;
this.energyLevelGood = energyLevelGood;
this.energyLevelSufficientlyRecharged = energyLevelSufficientlyRecharged;
this.energyLevelFullyRecharged = energyLevelFullyRecharged;
}
public int getEnergyLevelCritical() {
return energyLevelCritical;
}
public PutVehicleEnergyLevelThresholdSetTO setEnergyLevelCritical(int energyLevelCritical) {
this.energyLevelCritical = energyLevelCritical;
return this;
}
public int getEnergyLevelGood() {
return energyLevelGood;
}
public PutVehicleEnergyLevelThresholdSetTO setEnergyLevelGood(int energyLevelGood) {
this.energyLevelGood = energyLevelGood;
return this;
}
public int getEnergyLevelSufficientlyRecharged() {
return energyLevelSufficientlyRecharged;
}
public PutVehicleEnergyLevelThresholdSetTO setEnergyLevelSufficientlyRecharged(
int energyLevelSufficientlyRecharged
) {
this.energyLevelSufficientlyRecharged = energyLevelSufficientlyRecharged;
return this;
}
public int getEnergyLevelFullyRecharged() {
return energyLevelFullyRecharged;
}
public PutVehicleEnergyLevelThresholdSetTO setEnergyLevelFullyRecharged(
int energyLevelFullyRecharged
) {
this.energyLevelFullyRecharged = energyLevelFullyRecharged;
return this;
}
}

View File

@@ -0,0 +1,190 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.getevents;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.opentcs.data.order.DriveOrder;
import org.opentcs.data.order.TransportOrder;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.DestinationState;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.Property;
/**
* A status message containing details about a transport order.
*/
public class OrderStatusMessage
extends
StatusMessage {
private String orderName;
private String processingVehicleName;
private OrderState orderState;
private List<DestinationState> destinations = new ArrayList<>();
private List<Property> properties = new ArrayList<>();
/**
* Creates a new instance.
*/
public OrderStatusMessage() {
}
@Override
public OrderStatusMessage setSequenceNumber(long sequenceNumber) {
return (OrderStatusMessage) super.setSequenceNumber(sequenceNumber);
}
@Override
public OrderStatusMessage setCreationTimeStamp(Instant creationTimeStamp) {
return (OrderStatusMessage) super.setCreationTimeStamp(creationTimeStamp);
}
public String getOrderName() {
return orderName;
}
public OrderStatusMessage setOrderName(String orderName) {
this.orderName = orderName;
return this;
}
public String getProcessingVehicleName() {
return processingVehicleName;
}
public OrderStatusMessage setProcessingVehicleName(String processingVehicleName) {
this.processingVehicleName = processingVehicleName;
return this;
}
public OrderState getOrderState() {
return orderState;
}
public OrderStatusMessage setOrderState(OrderState orderState) {
this.orderState = orderState;
return this;
}
public List<DestinationState> getDestinations() {
return destinations;
}
public OrderStatusMessage setDestinations(List<DestinationState> destinations) {
this.destinations = destinations;
return this;
}
public List<Property> getProperties() {
return properties;
}
public OrderStatusMessage setProperties(List<Property> properties) {
this.properties = properties;
return this;
}
public static OrderStatusMessage fromTransportOrder(
TransportOrder order,
long sequenceNumber
) {
return fromTransportOrder(order, sequenceNumber, Instant.now());
}
public static OrderStatusMessage fromTransportOrder(
TransportOrder order,
long sequenceNumber,
Instant creationTimeStamp
) {
OrderStatusMessage orderMessage = new OrderStatusMessage();
orderMessage.setSequenceNumber(sequenceNumber);
orderMessage.setCreationTimeStamp(creationTimeStamp);
orderMessage.setOrderName(order.getName());
orderMessage.setProcessingVehicleName(
order.getProcessingVehicle() == null ? null : order.getProcessingVehicle().getName()
);
orderMessage.setOrderState(OrderState.fromTransportOrderState(order.getState()));
for (DriveOrder curDriveOrder : order.getAllDriveOrders()) {
orderMessage.getDestinations().add(DestinationState.fromDriveOrder(curDriveOrder));
}
for (Map.Entry<String, String> mapEntry : order.getProperties().entrySet()) {
orderMessage.getProperties().add(new Property(mapEntry.getKey(), mapEntry.getValue()));
}
return orderMessage;
}
/**
* The various states a transport order may be in.
*/
public enum OrderState {
/**
* A transport order's initial state.
*/
RAW,
/**
* Indicates a transport order's parameters have been set up completely and the kernel should
* dispatch it when possible.
*/
ACTIVE,
/**
* Marks a transport order as ready to be dispatched to a vehicle.
*/
DISPATCHABLE,
/**
* Marks a transport order as being processed by a vehicle.
*/
BEING_PROCESSED,
/**
* Indicates the transport order is withdrawn from a processing vehicle but not yet in its
* final state, as the vehicle has not yet finished/cleaned up.
*/
WITHDRAWN,
/**
* Marks a transport order as successfully completed.
*/
FINISHED,
/**
* General failure state that marks a transport order as failed.
*/
FAILED,
/**
* Failure state that marks a transport order as unroutable.
*/
UNROUTABLE;
/**
* Maps a transpor order's {@link TransportOrder#state state} to the corresponding
* {@link OrderState}.
*
* @param state The transport order's state.
* @return The corresponding OrderState.
*/
public static OrderState fromTransportOrderState(TransportOrder.State state) {
switch (state) {
case RAW:
return RAW;
case ACTIVE:
return ACTIVE;
case DISPATCHABLE:
return DISPATCHABLE;
case BEING_PROCESSED:
return BEING_PROCESSED;
case WITHDRAWN:
return WITHDRAWN;
case FINISHED:
return FINISHED;
case FAILED:
return FAILED;
case UNROUTABLE:
return UNROUTABLE;
default:
throw new IllegalArgumentException("Unknown transport order state.");
}
}
}
}

View File

@@ -0,0 +1,175 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.getevents;
import java.time.Instant;
import java.util.List;
import java.util.stream.Collectors;
import org.opentcs.data.peripherals.PeripheralJob;
import org.opentcs.data.peripherals.PeripheralJob.State;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.PeripheralOperationDescription;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.Property;
/**
* A status message containing information about a peripheral job.
*/
public class PeripheralJobStatusMessage
extends
StatusMessage {
private String name;
private String reservationToken;
private String relatedVehicle;
private String relatedTransportOrder;
private PeripheralOperationDescription peripheralOperation;
private State state;
private Instant creationTime;
private Instant finishedTime;
private List<Property> properties;
/**
* Creates a new instance.
*/
public PeripheralJobStatusMessage() {
}
@Override
public PeripheralJobStatusMessage setSequenceNumber(long sequenceNumber) {
return (PeripheralJobStatusMessage) super.setSequenceNumber(sequenceNumber);
}
@Override
public PeripheralJobStatusMessage setCreationTimeStamp(Instant creationTimeStamp) {
return (PeripheralJobStatusMessage) super.setCreationTimeStamp(creationTimeStamp);
}
public String getName() {
return name;
}
public PeripheralJobStatusMessage setName(String name) {
this.name = name;
return this;
}
public String getReservationToken() {
return reservationToken;
}
public PeripheralJobStatusMessage setReservationToken(String reservationToken) {
this.reservationToken = reservationToken;
return this;
}
public String getRelatedVehicle() {
return relatedVehicle;
}
public PeripheralJobStatusMessage setRelatedVehicle(String relatedVehicle) {
this.relatedVehicle = relatedVehicle;
return this;
}
public String getRelatedTransportOrder() {
return relatedTransportOrder;
}
public PeripheralJobStatusMessage setRelatedTransportOrder(String relatedTransportOrder) {
this.relatedTransportOrder = relatedTransportOrder;
return this;
}
public PeripheralOperationDescription getPeripheralOperation() {
return peripheralOperation;
}
public PeripheralJobStatusMessage setPeripheralOperation(
PeripheralOperationDescription peripheralOperation
) {
this.peripheralOperation = peripheralOperation;
return this;
}
public State getState() {
return state;
}
public PeripheralJobStatusMessage setState(State state) {
this.state = state;
return this;
}
public Instant getCreationTime() {
return creationTime;
}
public PeripheralJobStatusMessage setCreationTime(Instant creationTime) {
this.creationTime = creationTime;
return this;
}
public Instant getFinishedTime() {
return finishedTime;
}
public PeripheralJobStatusMessage setFinishedTime(Instant finishedTime) {
this.finishedTime = finishedTime;
return this;
}
public List<Property> getProperties() {
return properties;
}
public PeripheralJobStatusMessage setProperties(List<Property> properties) {
this.properties = properties;
return this;
}
public static PeripheralJobStatusMessage fromPeripheralJob(
PeripheralJob job,
long sequenceNumber
) {
return fromPeripheralJob(job, sequenceNumber, Instant.now());
}
public static PeripheralJobStatusMessage fromPeripheralJob(
PeripheralJob job,
long sequenceNumber,
Instant creationTimestamp
) {
PeripheralJobStatusMessage message = new PeripheralJobStatusMessage();
message.setSequenceNumber(sequenceNumber);
message.setCreationTimeStamp(creationTimestamp);
message.setName(job.getName());
message.setReservationToken(job.getReservationToken());
if (job.getRelatedVehicle() != null) {
message.setRelatedVehicle(job.getRelatedVehicle().getName());
}
if (job.getRelatedTransportOrder() != null) {
message.setRelatedTransportOrder(job.getRelatedTransportOrder().getName());
}
message.setPeripheralOperation(
PeripheralOperationDescription.fromPeripheralOperation(job.getPeripheralOperation())
);
message.setState(job.getState());
message.setCreationTime(job.getCreationTime());
message.setFinishedTime(job.getFinishedTime());
message.setProperties(
job.getProperties().entrySet().stream()
.map(entry -> new Property(entry.getKey(), entry.getValue()))
.collect(Collectors.toList())
);
return message;
}
}

View File

@@ -0,0 +1,54 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.getevents;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import java.time.Instant;
/**
* A generic status message.
*/
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type"
)
@JsonSubTypes(
{
@JsonSubTypes.Type(value = OrderStatusMessage.class, name = "TransportOrder"),
@JsonSubTypes.Type(value = VehicleStatusMessage.class, name = "Vehicle"),
@JsonSubTypes.Type(value = PeripheralJobStatusMessage.class, name = "PeripheralJob")
}
)
public abstract class StatusMessage {
private long sequenceNumber;
private Instant creationTimeStamp = Instant.now();
/**
* Creates a new instance.
*/
public StatusMessage() {
}
public long getSequenceNumber() {
return sequenceNumber;
}
public StatusMessage setSequenceNumber(long sequenceNumber) {
this.sequenceNumber = sequenceNumber;
return this;
}
public Instant getCreationTimeStamp() {
return creationTimeStamp;
}
public StatusMessage setCreationTimeStamp(Instant creationTimeStamp) {
this.creationTimeStamp = creationTimeStamp;
return this;
}
}

View File

@@ -0,0 +1,261 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.getevents;
import static java.util.Objects.requireNonNull;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.opentcs.data.model.TCSResourceReference;
import org.opentcs.data.model.Vehicle;
/**
* A status message containing information about a vehicle.
*/
public class VehicleStatusMessage
extends
StatusMessage {
private String vehicleName = "";
private String transportOrderName = "";
private String position;
private PrecisePosition precisePosition;
private double orientationAngle;
private boolean paused;
private Vehicle.State state;
private Vehicle.ProcState procState;
private List<List<String>> allocatedResources = new ArrayList<>();
private List<List<String>> claimedResources = new ArrayList<>();
/**
* Creates a new instance.
*/
public VehicleStatusMessage() {
}
@Override
public VehicleStatusMessage setSequenceNumber(long sequenceNumber) {
return (VehicleStatusMessage) super.setSequenceNumber(sequenceNumber);
}
@Override
public VehicleStatusMessage setCreationTimeStamp(Instant creationTimeStamp) {
return (VehicleStatusMessage) super.setCreationTimeStamp(creationTimeStamp);
}
public String getVehicleName() {
return vehicleName;
}
public VehicleStatusMessage setVehicleName(String vehicleName) {
this.vehicleName = vehicleName;
return this;
}
public String getTransportOrderName() {
return transportOrderName;
}
public VehicleStatusMessage setTransportOrderName(String transportOrderName) {
this.transportOrderName = transportOrderName;
return this;
}
public String getPosition() {
return position;
}
public VehicleStatusMessage setPosition(String position) {
this.position = position;
return this;
}
public PrecisePosition getPrecisePosition() {
return precisePosition;
}
public VehicleStatusMessage setPrecisePosition(PrecisePosition precisePosition) {
this.precisePosition = precisePosition;
return this;
}
public double getOrientationAngle() {
return orientationAngle;
}
public VehicleStatusMessage setOrientationAngle(double orientationAngle) {
this.orientationAngle = orientationAngle;
return this;
}
public boolean isPaused() {
return paused;
}
public VehicleStatusMessage setPaused(boolean paused) {
this.paused = paused;
return this;
}
public Vehicle.State getState() {
return state;
}
public VehicleStatusMessage setState(Vehicle.State state) {
this.state = state;
return this;
}
public Vehicle.ProcState getProcState() {
return procState;
}
public VehicleStatusMessage setProcState(Vehicle.ProcState procState) {
this.procState = procState;
return this;
}
public List<List<String>> getAllocatedResources() {
return allocatedResources;
}
public VehicleStatusMessage setAllocatedResources(List<List<String>> allocatedResources) {
this.allocatedResources = requireNonNull(allocatedResources, "allocatedResources");
return this;
}
public List<List<String>> getClaimedResources() {
return claimedResources;
}
public VehicleStatusMessage setClaimedResources(List<List<String>> claimedResources) {
this.claimedResources = requireNonNull(claimedResources, "claimedResources");
return this;
}
public static VehicleStatusMessage fromVehicle(
Vehicle vehicle,
long sequenceNumber
) {
return fromVehicle(vehicle, sequenceNumber, Instant.now());
}
public static VehicleStatusMessage fromVehicle(
Vehicle vehicle,
long sequenceNumber,
Instant creationTimeStamp
) {
VehicleStatusMessage vehicleMessage = new VehicleStatusMessage();
vehicleMessage.setSequenceNumber(sequenceNumber);
vehicleMessage.setCreationTimeStamp(creationTimeStamp);
vehicleMessage.setVehicleName(vehicle.getName());
vehicleMessage.setTransportOrderName(
vehicle.getTransportOrder() == null ? null : vehicle.getTransportOrder().getName()
);
vehicleMessage.setPosition(
vehicle.getCurrentPosition() == null ? null : vehicle.getCurrentPosition().getName()
);
vehicleMessage.setPaused(vehicle.isPaused());
vehicleMessage.setState(vehicle.getState());
vehicleMessage.setProcState(vehicle.getProcState());
if (vehicle.getPose().getPosition() != null) {
vehicleMessage.setPrecisePosition(
new PrecisePosition(
vehicle.getPose().getPosition().getX(),
vehicle.getPose().getPosition().getY(),
vehicle.getPose().getPosition().getZ()
)
);
}
else {
vehicleMessage.setPrecisePosition(null);
}
vehicleMessage.setOrientationAngle(vehicle.getPose().getOrientationAngle());
vehicleMessage.setAllocatedResources(toListOfListOfNames(vehicle.getAllocatedResources()));
vehicleMessage.setClaimedResources(toListOfListOfNames(vehicle.getClaimedResources()));
return vehicleMessage;
}
private static List<List<String>> toListOfListOfNames(
List<Set<TCSResourceReference<?>>> resources
) {
List<List<String>> result = new ArrayList<>(resources.size());
for (Set<TCSResourceReference<?>> resSet : resources) {
result.add(
resSet.stream()
.map(resRef -> resRef.getName())
.collect(Collectors.toList())
);
}
return result;
}
/**
* A precise position of a vehicle.
*/
public static class PrecisePosition {
private long x;
private long y;
private long z;
/**
* Creates a new instance.
*/
public PrecisePosition() {
}
/**
* Creates a new instance.
*
* @param x x value
* @param y y value
* @param z z value
*/
public PrecisePosition(long x, long y, long z) {
this.x = x;
this.y = y;
this.z = z;
}
public long getX() {
return x;
}
public void setX(long x) {
this.x = x;
}
public long getY() {
return y;
}
public void setY(long y) {
this.y = y;
}
public long getZ() {
return z;
}
public void setZ(long z) {
this.z = z;
}
}
}

View File

@@ -0,0 +1,111 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.getvehicleroutes;
import static java.util.Objects.requireNonNull;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import java.util.List;
import org.opentcs.data.model.Vehicle;
/**
* The web API representation of a route.
*/
public class RouteTO {
private String destinationPoint = "";
private long costs = -1;
private List<Step> steps;
public RouteTO() {
}
@Nonnull
public String getDestinationPoint() {
return destinationPoint;
}
public RouteTO setDestinationPoint(
@Nonnull
String destinationPoint
) {
this.destinationPoint = requireNonNull(destinationPoint, "destinationPoint");
return this;
}
public long getCosts() {
return costs;
}
public RouteTO setCosts(long costs) {
this.costs = costs;
return this;
}
public List<Step> getSteps() {
return steps;
}
public RouteTO setSteps(List<Step> steps) {
this.steps = steps;
return this;
}
public static class Step {
private String path;
private String sourcePoint;
private String destinationPoint = "";
private String vehicleOrientation = Vehicle.Orientation.UNDEFINED.name();
public Step() {
}
@Nullable
public String getPath() {
return path;
}
public Step setPath(String path) {
this.path = path;
return this;
}
@Nullable
public String getSourcePoint() {
return sourcePoint;
}
public Step setSourcePoint(String sourcePoint) {
this.sourcePoint = sourcePoint;
return this;
}
@Nonnull
public String getDestinationPoint() {
return destinationPoint;
}
public Step setDestinationPoint(
@Nonnull
String destinationPoint
) {
this.destinationPoint = requireNonNull(destinationPoint, "destinationPoint");
return this;
}
@Nonnull
public String getVehicleOrientation() {
return vehicleOrientation;
}
public Step setVehicleOrientation(
@Nonnull
String vehicleOrientation
) {
this.vehicleOrientation = requireNonNull(vehicleOrientation, "vehicleOrientation");
return this;
}
}
}

View File

@@ -0,0 +1,121 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.Nonnull;
import java.util.List;
import java.util.Set;
import org.opentcs.data.model.Block;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.PropertyTO;
/**
*/
public class BlockTO {
private String name;
private String type = Block.Type.SINGLE_VEHICLE_ONLY.name();
private Layout layout = new Layout();
private Set<String> memberNames = Set.of();
private List<PropertyTO> properties = List.of();
@JsonCreator
public BlockTO(
@Nonnull
@JsonProperty(value = "name", required = true)
String name
) {
this.name = requireNonNull(name, "name");
}
@Nonnull
public String getName() {
return name;
}
public BlockTO setName(
@Nonnull
String name
) {
this.name = requireNonNull(name, "name");
return this;
}
@Nonnull
public List<PropertyTO> getProperties() {
return properties;
}
public BlockTO setProperties(
@Nonnull
List<PropertyTO> properties
) {
this.properties = requireNonNull(properties, "properties");
return this;
}
@Nonnull
public String getType() {
return type;
}
public BlockTO setType(
@Nonnull
String type
) {
this.type = requireNonNull(type, "type");
return this;
}
@Nonnull
public Layout getLayout() {
return layout;
}
public BlockTO setLayout(
@Nonnull
Layout layout
) {
this.layout = requireNonNull(layout, "layout");
return this;
}
@Nonnull
public Set<String> getMemberNames() {
return memberNames;
}
public BlockTO setMemberNames(
@Nonnull
Set<String> memberNames
) {
this.memberNames = requireNonNull(memberNames, "memberNames");
return this;
}
public static class Layout {
private String color = "#FF0000";
public Layout() {
}
@Nonnull
public String getColor() {
return color;
}
public Layout setColor(
@Nonnull
String color
) {
this.color = requireNonNull(color, "color");
return this;
}
}
}

View File

@@ -0,0 +1,65 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.Nonnull;
/**
*/
public class LayerGroupTO {
private int id;
private String name;
private boolean visible;
@JsonCreator
public LayerGroupTO(
@JsonProperty(value = "id", required = true)
int id,
@Nonnull
@JsonProperty(value = "name", required = true)
String name,
@JsonProperty(value = "visible", required = true)
boolean visible
) {
this.id = id;
this.name = requireNonNull(name, "name");
this.visible = visible;
}
public int getId() {
return id;
}
public LayerGroupTO setId(int id) {
this.id = id;
return this;
}
@Nonnull
public String getName() {
return name;
}
public LayerGroupTO setName(
@Nonnull
String name
) {
this.name = requireNonNull(name, "name");
return this;
}
public boolean isVisible() {
return visible;
}
public LayerGroupTO setVisible(boolean visible) {
this.visible = visible;
return this;
}
}

View File

@@ -0,0 +1,91 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.Nonnull;
/**
*/
public class LayerTO {
private int id;
private int ordinal;
private boolean visible;
private String name;
private int groupId;
@JsonCreator
public LayerTO(
@JsonProperty(value = "id", required = true)
int id,
@JsonProperty(value = "ordinal", required = true)
int ordinal,
@JsonProperty(value = "visible", required = true)
boolean visible,
@Nonnull
@JsonProperty(value = "name", required = true)
String name,
@JsonProperty(value = "groupId", required = true)
int groupId
) {
this.id = id;
this.ordinal = ordinal;
this.visible = visible;
this.name = requireNonNull(name, "name");
this.groupId = groupId;
}
public int getId() {
return id;
}
public LayerTO setId(int id) {
this.id = id;
return this;
}
public int getOrdinal() {
return ordinal;
}
public LayerTO setOrdinal(int ordinal) {
this.ordinal = ordinal;
return this;
}
public boolean isVisible() {
return visible;
}
public LayerTO setVisible(boolean visible) {
this.visible = visible;
return this;
}
@Nonnull
public String getName() {
return name;
}
public LayerTO setName(
@Nonnull
String name
) {
this.name = requireNonNull(name, "name");
return this;
}
public int getGroupId() {
return groupId;
}
public LayerTO setGroupId(int groupId) {
this.groupId = groupId;
return this;
}
}

View File

@@ -0,0 +1,197 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.Nonnull;
import java.util.List;
import org.opentcs.data.model.visualization.LocationRepresentation;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.CoupleTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.LinkTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.PropertyTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.TripleTO;
/**
*/
public class LocationTO {
private String name;
private String typeName;
private TripleTO position;
private List<LinkTO> links = List.of();
private boolean locked;
private Layout layout = new Layout();
private List<PropertyTO> properties = List.of();
@JsonCreator
public LocationTO(
@Nonnull
@JsonProperty(value = "name", required = true)
String name,
@Nonnull
@JsonProperty(value = "typeName", required = true)
String typeName,
@Nonnull
@JsonProperty(value = "position", required = true)
TripleTO position
) {
this.name = requireNonNull(name, "name");
this.typeName = requireNonNull(typeName, "typeName");
this.position = requireNonNull(position, "position");
}
@Nonnull
public String getName() {
return name;
}
public LocationTO setName(
@Nonnull
String name
) {
this.name = requireNonNull(name, "name");
return this;
}
@Nonnull
public List<PropertyTO> getProperties() {
return properties;
}
public LocationTO setProperties(
@Nonnull
List<PropertyTO> properties
) {
this.properties = requireNonNull(properties, "properties");
return this;
}
@Nonnull
public String getTypeName() {
return typeName;
}
public LocationTO setTypeName(
@Nonnull
String typeName
) {
this.typeName = requireNonNull(typeName, "typeName");
return this;
}
@Nonnull
public TripleTO getPosition() {
return position;
}
public LocationTO setPosition(
@Nonnull
TripleTO position
) {
this.position = requireNonNull(position, "position");
return this;
}
@Nonnull
public List<LinkTO> getLinks() {
return links;
}
public LocationTO setLinks(
@Nonnull
List<LinkTO> links
) {
this.links = requireNonNull(links, "links");
return this;
}
public boolean isLocked() {
return locked;
}
public LocationTO setLocked(boolean locked) {
this.locked = locked;
return this;
}
@Nonnull
public Layout getLayout() {
return layout;
}
@Nonnull
public LocationTO setLayout(
@Nonnull
Layout layout
) {
this.layout = requireNonNull(layout, "layout");
return this;
}
public static class Layout {
private CoupleTO position = new CoupleTO(0, 0);
private CoupleTO labelOffset = new CoupleTO(0, 0);
private String locationRepresentation = LocationRepresentation.DEFAULT.name();
private int layerId;
public Layout() {
}
@Nonnull
public CoupleTO getPosition() {
return position;
}
public Layout setPosition(
@Nonnull
CoupleTO position
) {
this.position = requireNonNull(position, "position");
return this;
}
@Nonnull
public CoupleTO getLabelOffset() {
return labelOffset;
}
public Layout setLabelOffset(
@Nonnull
CoupleTO labelOffset
) {
this.labelOffset = requireNonNull(labelOffset, "labelOffset");
return this;
}
@Nonnull
public String getLocationRepresentation() {
return locationRepresentation;
}
public Layout setLocationRepresentation(
@Nonnull
String locationRepresentation
) {
this.locationRepresentation = requireNonNull(
locationRepresentation, "locationRepresentation"
);
return this;
}
public int getLayerId() {
return layerId;
}
public Layout setLayerId(int layerId) {
this.layerId = layerId;
return this;
}
}
}

View File

@@ -0,0 +1,127 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.Nonnull;
import java.util.List;
import org.opentcs.data.model.visualization.LocationRepresentation;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.PropertyTO;
/**
*/
public class LocationTypeTO {
private String name;
private List<String> allowedOperations = List.of();
private List<String> allowedPeripheralOperations = List.of();
private Layout layout = new Layout();
private List<PropertyTO> properties = List.of();
@JsonCreator
public LocationTypeTO(
@Nonnull
@JsonProperty(value = "name", required = true)
String name
) {
this.name = requireNonNull(name, "name");
}
@Nonnull
public String getName() {
return name;
}
public LocationTypeTO setName(
@Nonnull
String name
) {
this.name = requireNonNull(name, "name");
return this;
}
@Nonnull
public List<PropertyTO> getProperties() {
return properties;
}
public LocationTypeTO setProperties(
@Nonnull
List<PropertyTO> properties
) {
this.properties = requireNonNull(properties, "properties");
return this;
}
@Nonnull
public List<String> getAllowedOperations() {
return allowedOperations;
}
public LocationTypeTO setAllowedOperations(
@Nonnull
List<String> allowedOperations
) {
this.allowedOperations = requireNonNull(allowedOperations, "allowedOperations");
return this;
}
@Nonnull
public List<String> getAllowedPeripheralOperations() {
return allowedPeripheralOperations;
}
public LocationTypeTO setAllowedPeripheralOperations(
@Nonnull
List<String> allowedPeripheralOperations
) {
this.allowedPeripheralOperations = requireNonNull(
allowedPeripheralOperations,
"allowedPeripheralOperations"
);
return this;
}
@Nonnull
public Layout getLayout() {
return layout;
}
public LocationTypeTO setLayout(
@Nonnull
Layout layout
) {
this.layout = requireNonNull(layout, "layout");
return this;
}
public static class Layout {
private String locationRepresentation = LocationRepresentation.NONE.name();
public Layout() {
}
@Nonnull
public String getLocationRepresentation() {
return locationRepresentation;
}
public Layout setLocationRepresentation(
@Nonnull
String locationRepresentation
) {
this.locationRepresentation = requireNonNull(
locationRepresentation,
"locationRepresentation"
);
return this;
}
}
}

View File

@@ -0,0 +1,223 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.Nonnull;
import java.util.List;
import org.opentcs.data.model.Path;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.CoupleTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.EnvelopeTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.PropertyTO;
/**
*/
public class PathTO {
private String name;
private String srcPointName;
private String destPointName;
private long length = 1;
private int maxVelocity;
private int maxReverseVelocity;
private List<PeripheralOperationTO> peripheralOperations = List.of();
private boolean locked;
private Layout layout = new Layout();
private List<EnvelopeTO> vehicleEnvelopes = List.of();
private List<PropertyTO> properties = List.of();
@JsonCreator
public PathTO(
@Nonnull
@JsonProperty(value = "name", required = true)
String name,
@Nonnull
@JsonProperty(value = "srcPointName", required = true)
String srcPointName,
@Nonnull
@JsonProperty(value = "destPointName", required = true)
String destPointName
) {
this.name = requireNonNull(name, "name");
this.srcPointName = requireNonNull(srcPointName, "srcPointName");
this.destPointName = requireNonNull(destPointName, "destPointName");
}
@Nonnull
public String getName() {
return name;
}
public PathTO setName(
@Nonnull
String name
) {
this.name = requireNonNull(name, "name");
return this;
}
@Nonnull
public List<PropertyTO> getProperties() {
return properties;
}
public PathTO setProperties(
@Nonnull
List<PropertyTO> properties
) {
this.properties = requireNonNull(properties, "properties");
return this;
}
@Nonnull
public String getSrcPointName() {
return srcPointName;
}
public PathTO setSrcPointName(
@Nonnull
String srcPointName
) {
this.srcPointName = requireNonNull(srcPointName, "srcPointName");
return this;
}
@Nonnull
public String getDestPointName() {
return destPointName;
}
public PathTO setDestPointName(
@Nonnull
String destPointName
) {
this.destPointName = requireNonNull(destPointName, "destPointName");
return this;
}
public long getLength() {
return length;
}
public PathTO setLength(long length) {
this.length = length;
return this;
}
public int getMaxVelocity() {
return maxVelocity;
}
public PathTO setMaxVelocity(int maxVelocity) {
this.maxVelocity = maxVelocity;
return this;
}
public int getMaxReverseVelocity() {
return maxReverseVelocity;
}
public PathTO setMaxReverseVelocity(int maxReverseVelocity) {
this.maxReverseVelocity = maxReverseVelocity;
return this;
}
@Nonnull
public List<PeripheralOperationTO> getPeripheralOperations() {
return peripheralOperations;
}
public PathTO setPeripheralOperations(
@Nonnull
List<PeripheralOperationTO> peripheralOperations
) {
this.peripheralOperations = requireNonNull(peripheralOperations, "peripheralOperations");
return this;
}
public boolean isLocked() {
return locked;
}
public PathTO setLocked(boolean locked) {
this.locked = locked;
return this;
}
@Nonnull
public Layout getLayout() {
return layout;
}
public PathTO setLayout(
@Nonnull
Layout layout
) {
this.layout = requireNonNull(layout, "layout");
return this;
}
@Nonnull
public List<EnvelopeTO> getVehicleEnvelopes() {
return vehicleEnvelopes;
}
public PathTO setVehicleEnvelopes(
@Nonnull
List<EnvelopeTO> vehicleEnvelopes
) {
this.vehicleEnvelopes = requireNonNull(vehicleEnvelopes, "vehicleEnvelopes");
return this;
}
public static class Layout {
private String connectionType = Path.Layout.ConnectionType.DIRECT.name();
private List<CoupleTO> controlPoints = List.of();
private int layerId;
public Layout() {
}
@Nonnull
public String getConnectionType() {
return connectionType;
}
public Layout setConnectionType(
@Nonnull
String connectionType
) {
this.connectionType = requireNonNull(connectionType, "connectionType");
return this;
}
@Nonnull
public List<CoupleTO> getControlPoints() {
return controlPoints;
}
public Layout setControlPoints(
@Nonnull
List<CoupleTO> controlPoints
) {
this.controlPoints = requireNonNull(controlPoints, "controlPoints");
return this;
}
public int getLayerId() {
return layerId;
}
public Layout setLayerId(int layerId) {
this.layerId = layerId;
return this;
}
}
}

View File

@@ -0,0 +1,82 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.Nonnull;
import org.opentcs.data.peripherals.PeripheralOperation;
/**
*/
public class PeripheralOperationTO {
private String operation;
private String locationName;
private String executionTrigger = PeripheralOperation.ExecutionTrigger.AFTER_ALLOCATION.name();
private boolean completionRequired;
@JsonCreator
public PeripheralOperationTO(
@Nonnull
@JsonProperty(value = "operation", required = true)
String operation,
@Nonnull
@JsonProperty(value = "locationName", required = true)
String locationName
) {
this.operation = requireNonNull(operation, "operation");
this.locationName = requireNonNull(locationName, "locationName");
}
@Nonnull
public String getOperation() {
return operation;
}
public PeripheralOperationTO setOperation(
@Nonnull
String operation
) {
this.operation = requireNonNull(operation, "operation");
return this;
}
@Nonnull
public String getLocationName() {
return locationName;
}
public PeripheralOperationTO setLocationName(
@Nonnull
String locationName
) {
this.locationName = requireNonNull(locationName, "locationName");
return this;
}
@Nonnull
public String getExecutionTrigger() {
return executionTrigger;
}
public PeripheralOperationTO setExecutionTrigger(
@Nonnull
String executionTrigger
) {
this.executionTrigger = requireNonNull(executionTrigger, "executionTrigger");
return this;
}
public boolean isCompletionRequired() {
return completionRequired;
}
public PeripheralOperationTO setCompletionRequired(boolean completionRequired) {
this.completionRequired = completionRequired;
return this;
}
}

View File

@@ -0,0 +1,172 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.Nonnull;
import java.util.List;
import org.opentcs.data.model.Point;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.CoupleTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.EnvelopeTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.PropertyTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.TripleTO;
/**
*/
public class PointTO {
private String name;
private TripleTO position = new TripleTO(0, 0, 0);
private double vehicleOrientationAngle = Double.NaN;
private String type = Point.Type.HALT_POSITION.name();
private Layout layout = new Layout();
private List<EnvelopeTO> vehicleEnvelopes = List.of();
private List<PropertyTO> properties = List.of();
@JsonCreator
public PointTO(
@Nonnull
@JsonProperty(value = "name", required = true)
String name
) {
this.name = requireNonNull(name, "name");
}
@Nonnull
public String getName() {
return name;
}
public PointTO setName(
@Nonnull
String name
) {
this.name = requireNonNull(name, "name");
return this;
}
@Nonnull
public List<PropertyTO> getProperties() {
return properties;
}
public PointTO setProperties(
@Nonnull
List<PropertyTO> properties
) {
this.properties = requireNonNull(properties, "properties");
return this;
}
@Nonnull
public TripleTO getPosition() {
return position;
}
public PointTO setPosition(
@Nonnull
TripleTO position
) {
this.position = requireNonNull(position, "position");
return this;
}
public double getVehicleOrientationAngle() {
return vehicleOrientationAngle;
}
public PointTO setVehicleOrientationAngle(double vehicleOrientationAngle) {
this.vehicleOrientationAngle = vehicleOrientationAngle;
return this;
}
@Nonnull
public String getType() {
return type;
}
public PointTO setType(
@Nonnull
String type
) {
this.type = requireNonNull(type, "type");
return this;
}
@Nonnull
public Layout getLayout() {
return layout;
}
public PointTO setLayout(
@Nonnull
Layout pointLayout
) {
this.layout = requireNonNull(pointLayout, "pointLayout");
return this;
}
@Nonnull
public List<EnvelopeTO> getVehicleEnvelopes() {
return vehicleEnvelopes;
}
public PointTO setVehicleEnvelopes(
@Nonnull
List<EnvelopeTO> vehicleEnvelopes
) {
this.vehicleEnvelopes = requireNonNull(vehicleEnvelopes, "vehicleEnvelopes");
return this;
}
public static class Layout {
private CoupleTO position = new CoupleTO(0, 0);
private CoupleTO labelOffset = new CoupleTO(0, 0);
private int layerId;
public Layout() {
}
@Nonnull
public CoupleTO getPosition() {
return position;
}
public Layout setPosition(
@Nonnull
CoupleTO position
) {
this.position = requireNonNull(position, "position");
return this;
}
@Nonnull
public CoupleTO getLabelOffset() {
return labelOffset;
}
public Layout setLabelOffset(
@Nonnull
CoupleTO labelOffset
) {
this.labelOffset = requireNonNull(labelOffset, "labelOffset");
return this;
}
public int getLayerId() {
return layerId;
}
public Layout setLayerId(int layerId) {
this.layerId = layerId;
return this;
}
}
}

View File

@@ -0,0 +1,162 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.Nonnull;
import java.util.List;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.PropertyTO;
/**
*/
public class VehicleTO {
private String name;
private int length = 1000;
private int energyLevelCritical = 30;
private int energyLevelGood = 90;
private int energyLevelFullyRecharged = 90;
private int energyLevelSufficientlyRecharged = 30;
private int maxVelocity = 1000;
private int maxReverseVelocity = 1000;
private Layout layout = new Layout();
private List<PropertyTO> properties = List.of();
@JsonCreator
public VehicleTO(
@Nonnull
@JsonProperty(value = "name", required = true)
String name
) {
this.name = requireNonNull(name, "name");
}
@Nonnull
public String getName() {
return name;
}
public VehicleTO setName(
@Nonnull
String name
) {
this.name = requireNonNull(name, "name");
return this;
}
@Nonnull
public List<PropertyTO> getProperties() {
return properties;
}
public VehicleTO setProperties(
@Nonnull
List<PropertyTO> properties
) {
this.properties = requireNonNull(properties, "properties");
return this;
}
public int getLength() {
return length;
}
public VehicleTO setLength(int length) {
this.length = length;
return this;
}
public int getEnergyLevelCritical() {
return energyLevelCritical;
}
public VehicleTO setEnergyLevelCritical(int energyLevelCritical) {
this.energyLevelCritical = energyLevelCritical;
return this;
}
public int getEnergyLevelGood() {
return energyLevelGood;
}
public VehicleTO setEnergyLevelGood(int energyLevelGood) {
this.energyLevelGood = energyLevelGood;
return this;
}
public int getEnergyLevelFullyRecharged() {
return energyLevelFullyRecharged;
}
public VehicleTO setEnergyLevelFullyRecharged(int energyLevelFullyRecharged) {
this.energyLevelFullyRecharged = energyLevelFullyRecharged;
return this;
}
public int getEnergyLevelSufficientlyRecharged() {
return energyLevelSufficientlyRecharged;
}
public VehicleTO setEnergyLevelSufficientlyRecharged(int energyLevelSufficientlyRecharged) {
this.energyLevelSufficientlyRecharged = energyLevelSufficientlyRecharged;
return this;
}
public int getMaxVelocity() {
return maxVelocity;
}
public VehicleTO setMaxVelocity(int maxVelocity) {
this.maxVelocity = maxVelocity;
return this;
}
public int getMaxReverseVelocity() {
return maxReverseVelocity;
}
public VehicleTO setMaxReverseVelocity(int maxReverseVelocity) {
this.maxReverseVelocity = maxReverseVelocity;
return this;
}
@Nonnull
public Layout getLayout() {
return layout;
}
public VehicleTO setLayout(
@Nonnull
Layout layout
) {
this.layout = requireNonNull(layout, "layout");
return this;
}
public static class Layout {
private String routeColor = "#00FF00";
public Layout() {
}
@Nonnull
public String getRouteColor() {
return routeColor;
}
public Layout setRouteColor(
@Nonnull
String routeColor
) {
this.routeColor = requireNonNull(routeColor, "routeColor");
return this;
}
}
}

View File

@@ -0,0 +1,103 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.Nonnull;
import java.util.List;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.PropertyTO;
/**
*/
public class VisualLayoutTO {
private String name;
private double scaleX = 50.0;
private double scaleY = 50.0;
private List<LayerTO> layers = List.of(new LayerTO(0, 0, true, "layer0", 0));
private List<LayerGroupTO> layerGroups = List.of(new LayerGroupTO(0, "layerGroup0", true));
private List<PropertyTO> properties = List.of();
@JsonCreator
public VisualLayoutTO(
@Nonnull
@JsonProperty(value = "name", required = true)
String name
) {
this.name = requireNonNull(name, "name");
}
@Nonnull
public String getName() {
return name;
}
public VisualLayoutTO setName(
@Nonnull
String name
) {
this.name = requireNonNull(name, "name");
return this;
}
@Nonnull
public List<PropertyTO> getProperties() {
return properties;
}
public VisualLayoutTO setProperties(
@Nonnull
List<PropertyTO> properties
) {
this.properties = requireNonNull(properties, "properties");
return this;
}
public double getScaleX() {
return scaleX;
}
public VisualLayoutTO setScaleX(double scaleX) {
this.scaleX = scaleX;
return this;
}
public double getScaleY() {
return scaleY;
}
public VisualLayoutTO setScaleY(double scaleY) {
this.scaleY = scaleY;
return this;
}
@Nonnull
public List<LayerTO> getLayers() {
return layers;
}
public VisualLayoutTO setLayers(
@Nonnull
List<LayerTO> layers
) {
this.layers = requireNonNull(layers, "layers");
return this;
}
@Nonnull
public List<LayerGroupTO> getLayerGroups() {
return layerGroups;
}
public VisualLayoutTO setLayerGroups(
@Nonnull
List<LayerGroupTO> layerGroups
) {
this.layerGroups = requireNonNull(layerGroups, "layerGroups");
return this;
}
}

View File

@@ -0,0 +1,83 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.posttransportorder;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import java.util.List;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.Property;
/**
* A destination of a transport.
*/
public class Destination {
private String locationName;
private String operation;
private List<Property> properties;
@JsonCreator
public Destination(
@Nonnull
@JsonProperty(required = true, value = "locationName")
String locationName,
@Nonnull
@JsonProperty(required = true, value = "operation")
String operation,
@Nullable
@JsonProperty(required = false, value = "properties")
List<Property> properties
) {
this.locationName = requireNonNull(locationName, "locationName");
this.operation = requireNonNull(operation, "operation");
this.properties = properties;
}
public Destination() {
}
@Nonnull
public String getLocationName() {
return locationName;
}
public Destination setLocationName(
@Nonnull
String locationName
) {
this.locationName = requireNonNull(locationName, "locationName");
return this;
}
@Nonnull
public String getOperation() {
return operation;
}
public Destination setOperation(
@Nonnull
String operation
) {
this.operation = requireNonNull(operation, "operation");
return this;
}
@Nullable
public List<Property> getProperties() {
return properties;
}
public Destination setProperties(
@Nullable
List<Property> properties
) {
this.properties = properties;
return this;
}
}

View File

@@ -0,0 +1,43 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
*/
public class CoupleTO {
private long x;
private long y;
@JsonCreator
public CoupleTO(
@JsonProperty(value = "x", required = true)
long x,
@JsonProperty(value = "y", required = true)
long y
) {
this.x = x;
this.y = y;
}
public long getX() {
return x;
}
public CoupleTO setX(long x) {
this.x = x;
return this;
}
public long getY() {
return y;
}
public CoupleTO setY(long y) {
this.y = y;
return this;
}
}

View File

@@ -0,0 +1,145 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared;
import static java.util.Objects.requireNonNull;
import jakarta.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.opentcs.data.order.DriveOrder;
/**
* A {@link org.opentcs.data.order.DriveOrder DriveOrder}'s destination.
*/
public class DestinationState {
private String locationName = "";
private String operation = "";
private State state = State.PRISTINE;
private List<Property> properties = new ArrayList<>();
/**
* Creates a new instance.
*/
public DestinationState() {
}
@Nonnull
public String getLocationName() {
return locationName;
}
public DestinationState setLocationName(
@Nonnull
String name
) {
this.locationName = requireNonNull(name, "name");
return this;
}
@Nonnull
public String getOperation() {
return operation;
}
public DestinationState setOperation(
@Nonnull
String operation
) {
this.operation = requireNonNull(operation, "operation");
return this;
}
@Nonnull
public State getState() {
return state;
}
public DestinationState setState(
@Nonnull
State state
) {
this.state = requireNonNull(state, "state");
return this;
}
@Nonnull
public List<Property> getProperties() {
return properties;
}
public DestinationState setProperties(
@Nonnull
List<Property> properties
) {
this.properties = requireNonNull(properties, "properties");
return this;
}
public static DestinationState fromDriveOrder(DriveOrder driveOrder) {
if (driveOrder == null) {
return null;
}
DestinationState destination = new DestinationState();
destination.setLocationName(driveOrder.getDestination().getDestination().getName());
destination.setOperation(driveOrder.getDestination().getOperation());
destination.setState(mapDriveOrderState(driveOrder.getState()));
for (Map.Entry<String, String> mapEntry : driveOrder.getDestination().getProperties()
.entrySet()) {
destination.getProperties().add(new Property(mapEntry.getKey(), mapEntry.getValue()));
}
return destination;
}
private static DestinationState.State mapDriveOrderState(DriveOrder.State driveOrderState) {
switch (driveOrderState) {
case PRISTINE:
return DestinationState.State.PRISTINE;
case TRAVELLING:
return DestinationState.State.TRAVELLING;
case OPERATING:
return DestinationState.State.OPERATING;
case FINISHED:
return DestinationState.State.FINISHED;
case FAILED:
return DestinationState.State.FAILED;
default:
throw new IllegalArgumentException("Unhandled drive order state: " + driveOrderState);
}
}
/**
* This enumeration defines the various states a DriveOrder may be in.
*/
public enum State {
/**
* A DriveOrder's initial state, indicating it being still untouched/not
* being processed.
*/
PRISTINE,
/**
* Indicates this drive order being processed at the moment.
*/
TRAVELLING,
/**
* Indicates the vehicle processing an order is currently executing an
* operation.
*/
OPERATING,
/**
* Marks a DriveOrder as successfully completed.
*/
FINISHED,
/**
* Marks a DriveOrder as failed.
*/
FAILED;
}
}

View File

@@ -0,0 +1,58 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.Nonnull;
import java.util.List;
/**
*/
public class EnvelopeTO {
private String key;
private List<CoupleTO> vertices;
@JsonCreator
public EnvelopeTO(
@Nonnull
@JsonProperty(value = "key", required = true)
String key,
@Nonnull
@JsonProperty(value = "vertices", required = true)
List<CoupleTO> vertices
) {
this.key = requireNonNull(key, "key");
this.vertices = requireNonNull(vertices, "vertices");
}
@Nonnull
public String getKey() {
return key;
}
public EnvelopeTO setKey(
@Nonnull
String key
) {
this.key = requireNonNull(key, "key");
return this;
}
@Nonnull
public List<CoupleTO> getVertices() {
return vertices;
}
public EnvelopeTO setVertices(
@Nonnull
List<CoupleTO> vertices
) {
this.vertices = requireNonNull(vertices, "vertices");
return this;
}
}

View File

@@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared;
import static java.util.Objects.requireNonNull;
import jakarta.annotation.Nonnull;
import java.util.Set;
/**
*/
public class LinkTO {
private String pointName = "";
private Set<String> allowedOperations = Set.of();
public LinkTO() {
}
@Nonnull
public String getPointName() {
return pointName;
}
public LinkTO setPointName(
@Nonnull
String pointName
) {
this.pointName = requireNonNull(pointName, "pointName");
return this;
}
@Nonnull
public Set<String> getAllowedOperations() {
return allowedOperations;
}
public LinkTO setAllowedOperations(
@Nonnull
Set<String> allowedOperations
) {
this.allowedOperations = requireNonNull(allowedOperations, "allowedOperations");
return this;
}
}

View File

@@ -0,0 +1,71 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared;
import org.opentcs.data.peripherals.PeripheralOperation;
import org.opentcs.data.peripherals.PeripheralOperation.ExecutionTrigger;
/**
* Describes a peripheral operation.
*/
public class PeripheralOperationDescription {
private String operation;
private String locationName;
private ExecutionTrigger executionTrigger;
private boolean completionRequired;
public PeripheralOperationDescription() {
}
public String getOperation() {
return operation;
}
public PeripheralOperationDescription setOperation(String operation) {
this.operation = operation;
return this;
}
public String getLocationName() {
return locationName;
}
public PeripheralOperationDescription setLocationName(String locationName) {
this.locationName = locationName;
return this;
}
public ExecutionTrigger getExecutionTrigger() {
return executionTrigger;
}
public PeripheralOperationDescription setExecutionTrigger(ExecutionTrigger executionTrigger) {
this.executionTrigger = executionTrigger;
return this;
}
public boolean isCompletionRequired() {
return completionRequired;
}
public PeripheralOperationDescription setCompletionRequired(boolean completionRequired) {
this.completionRequired = completionRequired;
return this;
}
public static PeripheralOperationDescription fromPeripheralOperation(
PeripheralOperation operation
) {
PeripheralOperationDescription state = new PeripheralOperationDescription();
state.operation = operation.getOperation();
state.locationName = operation.getLocation().getName();
state.executionTrigger = operation.getExecutionTrigger();
state.completionRequired = operation.isCompletionRequired();
return state;
}
}

View File

@@ -0,0 +1,75 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
/**
* A key-value property.
*/
public class Property {
private String key;
private String value;
public Property(
@Nonnull
@JsonProperty(required = true, value = "key")
String key,
@Nullable
@JsonProperty(required = false, value = "value")
String value
) {
this.key = requireNonNull(key, "key");
this.value = value;
}
/**
* Returns the property key.
*
* @return The property key.
*/
@Nonnull
public String getKey() {
return key;
}
/**
* Sets the property key.
*
* @param key The new key.
*/
public void setKey(
@Nonnull
String key
) {
this.key = requireNonNull(key, "key");
}
/**
* Returns the property value.
*
* @return The property value.
*/
@Nullable
public String getValue() {
return value;
}
/**
* Sets the property value.
*
* @param value The new value.
*/
public void setValue(
@Nullable
String value
) {
this.value = value;
}
}

View File

@@ -0,0 +1,57 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.Nonnull;
/**
*/
public class PropertyTO {
private String name;
private String value;
@JsonCreator
public PropertyTO(
@Nonnull
@JsonProperty(value = "name", required = true)
String name,
@Nonnull
@JsonProperty(value = "value", required = true)
String value
) {
this.name = requireNonNull(name, "name");
this.value = requireNonNull(value, "value");
}
@Nonnull
public String getName() {
return name;
}
public PropertyTO setName(
@Nonnull
String name
) {
this.name = requireNonNull(name, "name");
return this;
}
@Nonnull
public String getValue() {
return value;
}
public PropertyTO setValue(
@Nonnull
String value
) {
this.value = requireNonNull(value, "value");
return this;
}
}

View File

@@ -0,0 +1,57 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
*/
public class TripleTO {
private long x;
private long y;
private long z;
@JsonCreator
public TripleTO(
@JsonProperty(value = "x", required = true)
long x,
@JsonProperty(value = "y", required = true)
long y,
@JsonProperty(value = "z", required = true)
long z
) {
this.x = x;
this.y = y;
this.z = z;
}
public long getX() {
return x;
}
public TripleTO setX(long x) {
this.x = x;
return this;
}
public long getY() {
return y;
}
public TripleTO setY(long y) {
this.y = y;
return this;
}
public long getZ() {
return z;
}
public TripleTO setZ(long z) {
this.z = z;
return this;
}
}

View File

@@ -0,0 +1,68 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.converter;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.opentcs.access.to.model.BlockCreationTO;
import org.opentcs.data.model.Block;
import org.opentcs.data.model.TCSResourceReference;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.BlockTO;
import org.opentcs.util.Colors;
/**
* Includes the conversion methods for all Block classes.
*/
public class BlockConverter {
private final PropertyConverter pConverter;
@Inject
public BlockConverter(PropertyConverter pConverter) {
this.pConverter = requireNonNull(pConverter, "pConverter");
}
public List<BlockCreationTO> toBlockCreationTOs(List<BlockTO> blocks) {
return blocks.stream()
.map(
block -> new BlockCreationTO(block.getName())
.withProperties(pConverter.toPropertyMap(block.getProperties()))
.withMemberNames(block.getMemberNames())
.withType(Block.Type.valueOf(block.getType()))
.withLayout(
new BlockCreationTO.Layout(
Colors.decodeFromHexRGB(block.getLayout().getColor())
)
)
)
.collect(Collectors.toCollection(ArrayList::new));
}
public List<BlockTO> toBlockTOs(Set<Block> blocks) {
return blocks.stream()
.map(
block -> new BlockTO(block.getName())
.setType(block.getType().name())
.setMemberNames(convertMemberNames(block.getMembers()))
.setLayout(
new BlockTO.Layout()
.setColor(Colors.encodeToHexRGB(block.getLayout().getColor()))
)
.setProperties(pConverter.toPropertyTOs(block.getProperties()))
)
.sorted(Comparator.comparing(BlockTO::getName))
.collect(Collectors.toList());
}
private Set<String> convertMemberNames(Set<TCSResourceReference<?>> members) {
return members.stream()
.map(TCSResourceReference::getName)
.collect(Collectors.toSet());
}
}

View File

@@ -0,0 +1,50 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.converter;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.opentcs.data.model.Couple;
import org.opentcs.data.model.Envelope;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.CoupleTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.EnvelopeTO;
/**
* Includes the conversion methods for all Envelope classes.
*/
public class EnvelopeConverter {
public EnvelopeConverter() {
}
public Map<String, Envelope> toVehicleEnvelopeMap(List<EnvelopeTO> envelopeEntries) {
return envelopeEntries.stream()
.collect(
Collectors.toMap(
EnvelopeTO::getKey,
entry -> {
List<Couple> couples = entry.getVertices().stream()
.map(coupleTO -> new Couple(coupleTO.getX(), coupleTO.getY()))
.collect(Collectors.toList());
return new Envelope(couples);
}
)
);
}
public List<EnvelopeTO> toEnvelopeTOs(Map<String, Envelope> envelopeMap) {
return envelopeMap.entrySet().stream()
.map(
entry -> new EnvelopeTO(
entry.getKey(),
entry.getValue().getVertices().stream()
.map(couple -> new CoupleTO(couple.getX(), couple.getY()))
.collect(Collectors.toList())
)
)
.sorted(Comparator.comparing(EnvelopeTO::getKey))
.collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,124 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.converter;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.opentcs.access.to.model.LocationCreationTO;
import org.opentcs.data.model.Couple;
import org.opentcs.data.model.Location;
import org.opentcs.data.model.Triple;
import org.opentcs.data.model.visualization.LocationRepresentation;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.LocationTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.CoupleTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.LinkTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.TripleTO;
/**
* Includes the conversion methods for all Location classes.
*/
public class LocationConverter {
private final PropertyConverter pConverter;
@Inject
public LocationConverter(PropertyConverter pConverter) {
this.pConverter = requireNonNull(pConverter, "pConverter");
}
public List<LocationCreationTO> toLocationCreationTOs(List<LocationTO> locations) {
return locations.stream()
.map(
location -> new LocationCreationTO(
location.getName(),
location.getTypeName(),
new Triple(
location.getPosition().getX(),
location.getPosition().getY(),
location.getPosition().getZ()
)
)
.withProperties(pConverter.toPropertyMap(location.getProperties()))
.withLinks(toLinkMap(location.getLinks()))
.withLocked(location.isLocked())
.withLayout(
new LocationCreationTO.Layout(
new Couple(
location.getLayout().getPosition().getX(),
location.getLayout().getPosition().getY()
),
new Couple(
location.getLayout().getLabelOffset().getX(),
location.getLayout().getLabelOffset().getY()
),
LocationRepresentation.valueOf(
location.getLayout().getLocationRepresentation()
),
location.getLayout().getLayerId()
)
)
)
.collect(Collectors.toCollection(ArrayList::new));
}
public List<LocationTO> toLocationTOs(Set<Location> locations) {
return locations.stream()
.map(
location -> new LocationTO(
location.getName(),
location.getType().getName(),
new TripleTO(
location.getPosition().getX(),
location.getPosition().getY(),
location.getPosition().getZ()
)
)
.setLocked(location.isLocked())
.setProperties(pConverter.toPropertyTOs(location.getProperties()))
.setLinks(toLinkTOs(location.getAttachedLinks()))
.setLayout(
new LocationTO.Layout()
.setLayerId(location.getLayout().getLayerId())
.setLocationRepresentation(
location.getLayout().getLocationRepresentation().name()
)
.setLabelOffset(
new CoupleTO(
location.getLayout().getLabelOffset().getX(),
location.getLayout().getLabelOffset().getY()
)
)
.setPosition(
new CoupleTO(
location.getLayout().getPosition().getX(),
location.getLayout().getPosition().getY()
)
)
)
)
.sorted(Comparator.comparing(LocationTO::getName))
.collect(Collectors.toList());
}
private Map<String, Set<String>> toLinkMap(List<LinkTO> links) {
return links.stream()
.collect(Collectors.toMap(LinkTO::getPointName, LinkTO::getAllowedOperations));
}
private List<LinkTO> toLinkTOs(Set<Location.Link> links) {
return links.stream()
.map(
link -> new LinkTO()
.setPointName(link.getPoint().getName())
.setAllowedOperations(link.getAllowedOperations())
)
.collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,67 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.converter;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.opentcs.access.to.model.LocationTypeCreationTO;
import org.opentcs.data.model.LocationType;
import org.opentcs.data.model.visualization.LocationRepresentation;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.LocationTypeTO;
/**
* Includes the conversion methods for all LocationType classes.
*/
public class LocationTypeConverter {
private final PropertyConverter pConverter;
@Inject
public LocationTypeConverter(PropertyConverter pConverter) {
this.pConverter = requireNonNull(pConverter, "pConverter");
}
public List<LocationTypeCreationTO> toLocationTypeCreationTOs(List<LocationTypeTO> locTypes) {
return locTypes.stream()
.map(
locationType -> new LocationTypeCreationTO(locationType.getName())
.withAllowedOperations(locationType.getAllowedOperations())
.withAllowedPeripheralOperations(
locationType.getAllowedPeripheralOperations()
)
.withProperties(pConverter.toPropertyMap(locationType.getProperties()))
.withLayout(
new LocationTypeCreationTO.Layout(
LocationRepresentation.valueOf(
locationType.getLayout().getLocationRepresentation()
)
)
)
)
.collect(Collectors.toCollection(ArrayList::new));
}
public List<LocationTypeTO> toLocationTypeTOs(Set<LocationType> locationTypes) {
return locationTypes.stream()
.map(
locationType -> new LocationTypeTO(locationType.getName())
.setProperties(pConverter.toPropertyTOs(locationType.getProperties()))
.setAllowedOperations(locationType.getAllowedOperations())
.setAllowedPeripheralOperations(locationType.getAllowedPeripheralOperations())
.setLayout(
new LocationTypeTO.Layout()
.setLocationRepresentation(
locationType.getLayout().getLocationRepresentation().name()
)
)
)
.sorted(Comparator.comparing(LocationTypeTO::getName))
.collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,108 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.converter;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.opentcs.access.to.model.PathCreationTO;
import org.opentcs.data.model.Couple;
import org.opentcs.data.model.Path;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.PathTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.CoupleTO;
/**
* Includes the conversion methods for all Path classes.
*/
public class PathConverter {
private final PropertyConverter pConverter;
private final PeripheralOperationConverter pOConverter;
private final EnvelopeConverter envelopeConverter;
@Inject
public PathConverter(
PropertyConverter pConverter, PeripheralOperationConverter pOConverter,
EnvelopeConverter envelopeConverter
) {
this.pConverter = requireNonNull(pConverter, "pConverter");
this.pOConverter = requireNonNull(pOConverter, "pOConverter");
this.envelopeConverter = requireNonNull(envelopeConverter, "envelopeConverter");
}
public List<PathTO> toPathTOs(Set<Path> paths) {
return paths.stream()
.map(
path -> new PathTO(
path.getName(),
path.getSourcePoint().getName(),
path.getDestinationPoint().getName()
)
.setLength(path.getLength())
.setLocked(path.isLocked())
.setMaxReverseVelocity(path.getMaxReverseVelocity())
.setMaxVelocity(path.getMaxVelocity())
.setVehicleEnvelopes(envelopeConverter.toEnvelopeTOs(path.getVehicleEnvelopes()))
.setProperties(pConverter.toPropertyTOs(path.getProperties()))
.setPeripheralOperations(
pOConverter.toPeripheralOperationsTOs(path.getPeripheralOperations())
)
.setLayout(
new PathTO.Layout()
.setLayerId(path.getLayout().getLayerId())
.setConnectionType(path.getLayout().getConnectionType().name())
.setControlPoints(toCoupleTOs(path.getLayout().getControlPoints()))
)
)
.sorted(Comparator.comparing(PathTO::getName))
.collect(Collectors.toList());
}
public List<PathCreationTO> toPathCreationTOs(List<PathTO> paths) {
return paths.stream()
.map(
path -> new PathCreationTO(
path.getName(),
path.getSrcPointName(),
path.getDestPointName()
)
.withName(path.getName())
.withProperties(pConverter.toPropertyMap(path.getProperties()))
.withLength(path.getLength())
.withMaxVelocity(path.getMaxVelocity())
.withMaxReverseVelocity(path.getMaxReverseVelocity())
.withLocked(path.isLocked())
.withLayout(toPathCreationTOLayout(path.getLayout()))
.withVehicleEnvelopes(
envelopeConverter
.toVehicleEnvelopeMap(path.getVehicleEnvelopes())
)
.withPeripheralOperations(
pOConverter.toPeripheralOperationCreationTOs(path.getPeripheralOperations())
)
)
.collect(Collectors.toCollection(ArrayList::new));
}
private PathCreationTO.Layout toPathCreationTOLayout(PathTO.Layout layout) {
return new PathCreationTO.Layout(
Path.Layout.ConnectionType.valueOf(layout.getConnectionType()),
layout.getControlPoints()
.stream()
.map(cp -> new Couple(cp.getX(), cp.getY()))
.collect(Collectors.toList()),
layout.getLayerId()
);
}
private List<CoupleTO> toCoupleTOs(List<Couple> controlPoints) {
return controlPoints.stream()
.map(cp -> new CoupleTO(cp.getX(), cp.getY()))
.collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,54 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.converter;
import java.util.List;
import java.util.stream.Collectors;
import org.opentcs.access.to.peripherals.PeripheralOperationCreationTO;
import org.opentcs.data.peripherals.PeripheralOperation;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.PeripheralOperationTO;
/**
* Includes the conversion methods for all PeripheralOperation classes.
*/
public class PeripheralOperationConverter {
public PeripheralOperationConverter() {
}
public List<PeripheralOperationTO> toPeripheralOperationsTOs(
List<PeripheralOperation> peripheralOperations
) {
return peripheralOperations.stream()
.map(
perOp -> new PeripheralOperationTO(
perOp.getOperation(),
perOp.getLocation().getName()
)
.setCompletionRequired(perOp.isCompletionRequired())
.setExecutionTrigger(
perOp.getExecutionTrigger().name()
)
)
.collect(Collectors.toList());
}
public List<PeripheralOperationCreationTO> toPeripheralOperationCreationTOs(
List<PeripheralOperationTO> perOps
) {
return perOps.stream()
.map(
perOp -> new PeripheralOperationCreationTO(
perOp.getOperation(),
perOp.getLocationName()
)
.withCompletionRequired(perOp.isCompletionRequired())
.withExecutionTrigger(
PeripheralOperation.ExecutionTrigger.valueOf(
perOp.getExecutionTrigger()
)
)
)
.collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,108 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.converter;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.opentcs.access.to.model.PointCreationTO;
import org.opentcs.data.model.Couple;
import org.opentcs.data.model.Point;
import org.opentcs.data.model.Pose;
import org.opentcs.data.model.Triple;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.PointTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.CoupleTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.TripleTO;
/**
* Includes the conversion methods for all Point classes.
*/
public class PointConverter {
private final PropertyConverter pConverter;
private final EnvelopeConverter envelopeConverter;
@Inject
public PointConverter(PropertyConverter pConverter, EnvelopeConverter envelopeConverter) {
this.pConverter = requireNonNull(pConverter, "pConverter");
this.envelopeConverter = requireNonNull(envelopeConverter, "envelopeConverter");
}
public List<PointTO> toPointTOs(Set<Point> points) {
return points.stream()
.map(
point -> new PointTO(point.getName())
.setPosition(
new TripleTO(
point.getPose().getPosition().getX(),
point.getPose().getPosition().getY(),
point.getPose().getPosition().getZ()
)
)
.setType(point.getType().name())
.setVehicleOrientationAngle(point.getPose().getOrientationAngle())
.setVehicleEnvelopes(envelopeConverter.toEnvelopeTOs(point.getVehicleEnvelopes()))
.setProperties(pConverter.toPropertyTOs(point.getProperties()))
.setLayout(
new PointTO.Layout()
.setLabelOffset(
new CoupleTO(
point.getLayout().getLabelOffset().getX(),
point.getLayout().getLabelOffset().getY()
)
)
.setPosition(
new CoupleTO(
point.getLayout().getPosition().getX(),
point.getLayout().getPosition().getY()
)
)
.setLayerId(point.getLayout().getLayerId())
)
)
.sorted(Comparator.comparing(PointTO::getName))
.collect(Collectors.toCollection(ArrayList::new));
}
public List<PointCreationTO> toPointCreationTOs(List<PointTO> points) {
return points.stream()
.map(
point -> new PointCreationTO(point.getName())
.withProperties(pConverter.toPropertyMap(point.getProperties()))
.withPose(
new Pose(
new Triple(
point.getPosition().getX(),
point.getPosition().getY(),
point.getPosition().getZ()
),
point.getVehicleOrientationAngle()
)
)
.withType(Point.Type.valueOf(point.getType()))
.withLayout(
new PointCreationTO.Layout(
new Couple(
point.getLayout().getPosition().getX(),
point.getLayout().getPosition().getY()
),
new Couple(
point.getLayout().getLabelOffset().getX(),
point.getLayout().getLabelOffset().getY()
),
point.getLayout().getLayerId()
)
)
.withVehicleEnvelopes(
envelopeConverter
.toVehicleEnvelopeMap(point.getVehicleEnvelopes())
)
)
.collect(Collectors.toCollection(ArrayList::new));
}
}

View File

@@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.converter;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.PropertyTO;
/**
* Includes the conversion methods for all Property classes.
*/
public class PropertyConverter {
public PropertyConverter() {
}
public List<PropertyTO> toPropertyTOs(Map<String, String> properties) {
return properties.entrySet().stream()
.map(property -> new PropertyTO(property.getKey(), property.getValue()))
.sorted(Comparator.comparing(PropertyTO::getName))
.collect(Collectors.toList());
}
public Map<String, String> toPropertyMap(List<PropertyTO> properties) {
return properties.stream()
.collect(Collectors.toMap(PropertyTO::getName, PropertyTO::getValue));
}
}

View File

@@ -0,0 +1,82 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.converter;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.opentcs.access.to.model.BoundingBoxCreationTO;
import org.opentcs.access.to.model.VehicleCreationTO;
import org.opentcs.data.model.Vehicle;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.VehicleTO;
import org.opentcs.util.Colors;
/**
* Includes the conversion methods for all Vehicle classes.
*/
public class VehicleConverter {
private final PropertyConverter pConverter;
@Inject
public VehicleConverter(PropertyConverter pConverter) {
this.pConverter = requireNonNull(pConverter, "pConverter");
}
public List<VehicleCreationTO> toVehicleCreationTOs(List<VehicleTO> vehicles) {
return vehicles.stream()
.map(
vehicle -> new VehicleCreationTO(vehicle.getName())
.withProperties(pConverter.toPropertyMap(vehicle.getProperties()))
.withBoundingBox(new BoundingBoxCreationTO(vehicle.getLength(), 1000, 1000))
.withEnergyLevelThresholdSet(
new VehicleCreationTO.EnergyLevelThresholdSet(
vehicle.getEnergyLevelCritical(),
vehicle.getEnergyLevelGood(),
vehicle.getEnergyLevelSufficientlyRecharged(),
vehicle.getEnergyLevelFullyRecharged()
)
)
.withMaxVelocity(vehicle.getMaxVelocity())
.withMaxReverseVelocity(vehicle.getMaxReverseVelocity())
.withLayout(
new VehicleCreationTO.Layout(
Colors.decodeFromHexRGB(vehicle.getLayout().getRouteColor())
)
)
)
.collect(Collectors.toCollection(ArrayList::new));
}
public List<VehicleTO> toVehicleTOs(Set<Vehicle> vehicles) {
return vehicles.stream()
.map(
vehicle -> new VehicleTO(vehicle.getName())
.setLength((int) vehicle.getBoundingBox().getLength())
.setEnergyLevelCritical(
vehicle.getEnergyLevelThresholdSet().getEnergyLevelCritical()
)
.setEnergyLevelGood(vehicle.getEnergyLevelThresholdSet().getEnergyLevelGood())
.setEnergyLevelFullyRecharged(
vehicle.getEnergyLevelThresholdSet().getEnergyLevelFullyRecharged()
)
.setEnergyLevelSufficientlyRecharged(
vehicle.getEnergyLevelThresholdSet().getEnergyLevelSufficientlyRecharged()
)
.setMaxVelocity(vehicle.getMaxVelocity())
.setMaxReverseVelocity(vehicle.getMaxReverseVelocity())
.setLayout(
new VehicleTO.Layout()
.setRouteColor(Colors.encodeToHexRGB(vehicle.getLayout().getRouteColor()))
)
.setProperties(pConverter.toPropertyTOs(vehicle.getProperties()))
)
.sorted(Comparator.comparing(VehicleTO::getName))
.collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,99 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.converter;
import static java.util.Objects.requireNonNull;
import jakarta.inject.Inject;
import java.util.List;
import java.util.stream.Collectors;
import org.opentcs.access.to.model.VisualLayoutCreationTO;
import org.opentcs.data.model.visualization.Layer;
import org.opentcs.data.model.visualization.LayerGroup;
import org.opentcs.data.model.visualization.VisualLayout;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.LayerGroupTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.LayerTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.VisualLayoutTO;
/**
* Includes the conversion methods for all VisualLayout classes.
*/
public class VisualLayoutConverter {
private final PropertyConverter pConverter;
@Inject
public VisualLayoutConverter(PropertyConverter pConverter) {
this.pConverter = requireNonNull(pConverter, "pConverter");
}
public VisualLayoutCreationTO toVisualLayoutCreationTO(VisualLayoutTO vLayout) {
return new VisualLayoutCreationTO(vLayout.getName())
.withProperties(pConverter.toPropertyMap(vLayout.getProperties()))
.withScaleX(vLayout.getScaleX())
.withScaleY(vLayout.getScaleY())
.withLayers(convertLayers(vLayout.getLayers()))
.withLayerGroups(convertLayerGroups(vLayout.getLayerGroups()));
}
public VisualLayoutTO toVisualLayoutTO(VisualLayout visualLayout) {
return new VisualLayoutTO(visualLayout.getName())
.setProperties(pConverter.toPropertyTOs(visualLayout.getProperties()))
.setScaleX(visualLayout.getScaleX())
.setScaleY(visualLayout.getScaleY())
.setLayers(toLayerTOs(visualLayout.getLayers()))
.setLayerGroups(toLayerGroupTOs(visualLayout.getLayerGroups()));
}
private List<LayerGroup> convertLayerGroups(List<LayerGroupTO> layerGroups) {
return layerGroups.stream()
.map(
layerGroup -> new LayerGroup(
layerGroup.getId(),
layerGroup.getName(),
layerGroup.isVisible()
)
)
.collect(Collectors.toList());
}
private List<LayerGroupTO> toLayerGroupTOs(List<LayerGroup> layerGroups) {
return layerGroups.stream()
.map(
layerGroup -> new LayerGroupTO(
layerGroup.getId(),
layerGroup.getName(),
layerGroup.isVisible()
)
)
.collect(Collectors.toList());
}
private List<Layer> convertLayers(List<LayerTO> layers) {
return layers.stream()
.map(
layer -> new Layer(
layer.getId(),
layer.getOrdinal(),
layer.isVisible(),
layer.getName(),
layer.getGroupId()
)
)
.collect(Collectors.toList());
}
private List<LayerTO> toLayerTOs(List<Layer> layers) {
return layers.stream()
.map(
layer -> new LayerTO(
layer.getId(),
layer.getOrdinal(),
layer.isVisible(),
layer.getName(),
layer.getGroupId()
)
)
.collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,61 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import spark.Request;
/**
*/
class AuthenticatorTest {
private ServiceWebApiConfiguration configuration;
private Authenticator authenticator;
@BeforeEach
void setUp() {
configuration = mock(ServiceWebApiConfiguration.class);
authenticator = new Authenticator(configuration);
}
@Test
void allowRequestsIfNoAccessKeyConfigured() {
when(configuration.accessKey()).thenReturn("");
assertTrue(authenticator.isAuthenticated(aRequestWithAccessKey(null)));
assertTrue(authenticator.isAuthenticated(aRequestWithAccessKey("")));
assertTrue(authenticator.isAuthenticated(aRequestWithAccessKey("some random value")));
}
@Test
void allowRequestIfCorrectAccessKeyGiven() {
when(configuration.accessKey()).thenReturn("my access key");
assertTrue(authenticator.isAuthenticated(aRequestWithAccessKey("my access key")));
}
@Test
void disallowRequestIfWrongAccessKeyGiven() {
when(configuration.accessKey()).thenReturn("my access key");
assertFalse(authenticator.isAuthenticated(aRequestWithAccessKey(null)));
assertFalse(authenticator.isAuthenticated(aRequestWithAccessKey("")));
assertFalse(authenticator.isAuthenticated(aRequestWithAccessKey("some random value")));
}
private Request aRequestWithAccessKey(String accessKey) {
Request request = mock(Request.class);
when(request.headers(HttpConstants.HEADER_NAME_ACCESS_KEY)).thenReturn(accessKey);
return request;
}
}

View File

@@ -0,0 +1,64 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import org.approvaltests.Approvals;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* Unit tests for {@link JsonBinder}.
*/
class JsonBinderTest {
private JsonBinder jsonBinder;
@BeforeEach
void setUp() {
jsonBinder = new JsonBinder();
}
@Test
void writeAndParseObject() {
String json = jsonBinder.toJson(new TestObject().setName("some-name"));
Approvals.verify(json);
TestObject parsedObject = jsonBinder.fromJson(json, TestObject.class);
assertThat(parsedObject.getName(), is(equalTo("some-name")));
}
@Test
void writeAndParseThrowable() {
Approvals.verify(jsonBinder.toJson(new TestException("some-message")));
}
private static class TestObject {
private String name;
public String getName() {
return name;
}
public TestObject setName(String name) {
this.name = name;
return this;
}
}
private static class TestException
extends
Exception {
TestException(String message) {
super(message);
}
}
}

View File

@@ -0,0 +1,78 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.opentcs.access.KernelRuntimeException;
import org.opentcs.data.ObjectExistsException;
import org.opentcs.data.ObjectUnknownException;
/**
* Tests for {@link KernelExecutorWrapper}.
*/
class KernelExecutorWrapperTest {
private ExecutorService executorService;
private KernelExecutorWrapper executorWrapper;
@BeforeEach
void setUp() {
this.executorService = Executors.newSingleThreadExecutor();
this.executorWrapper = new KernelExecutorWrapper(executorService);
}
@AfterEach
void tearDown() {
executorService.shutdown();
}
@Test
void returnValueReturnedInCallable() {
assertThat(
executorWrapper.callAndWait(() -> "my result"),
is("my result")
);
}
@Test
void forwardCausingExceptionIfRuntimeException() {
assertThrows(
ObjectUnknownException.class,
() -> {
executorWrapper.callAndWait(() -> {
throw new ObjectUnknownException("some exception");
});
}
);
assertThrows(
ObjectExistsException.class,
() -> {
executorWrapper.callAndWait(() -> {
throw new ObjectExistsException("some exception");
});
}
);
}
@Test
void wrapUnhandledExceptionInKernelRuntimeException() {
assertThrows(
KernelRuntimeException.class,
() -> {
executorWrapper.callAndWait(() -> {
throw new Exception("some exception");
});
}
);
}
}

View File

@@ -0,0 +1,286 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.opentcs.data.model.Location;
import org.opentcs.data.model.LocationType;
import org.opentcs.data.model.Vehicle;
import org.opentcs.data.order.OrderSequence;
import org.opentcs.data.order.TransportOrder;
import org.opentcs.data.peripherals.PeripheralJob;
import org.opentcs.data.peripherals.PeripheralOperation;
/**
* Unit tests for {@link Filters}.
*/
class FiltersTest {
@Test
void acceptTransportOrdersRegardlessOfIntendedVehicle() {
assertThat(
Filters.transportOrderWithIntendedVehicle(null)
.test(
new TransportOrder("some-order", List.of())
.withIntendedVehicle(null)
),
is(true)
);
assertThat(
Filters.transportOrderWithIntendedVehicle(null)
.test(
new TransportOrder("some-order", List.of())
.withIntendedVehicle(new Vehicle("some-vehicle").getReference())
),
is(true)
);
}
@Test
void acceptTransportOrdersWithGivenIntendedVehicle() {
Vehicle vehicle = new Vehicle("some-vehicle");
assertThat(
Filters.transportOrderWithIntendedVehicle(vehicle.getReference())
.test(
new TransportOrder("some-order", List.of())
.withIntendedVehicle(null)
),
is(false)
);
assertThat(
Filters.transportOrderWithIntendedVehicle(null)
.test(
new TransportOrder("some-order", List.of())
.withIntendedVehicle(vehicle.getReference())
),
is(true)
);
}
@Test
void acceptOrderSequencesRegardlessOfIntendedVehicle() {
assertThat(
Filters.orderSequenceWithIntendedVehicle(null)
.test(
new OrderSequence("some-sequence")
.withIntendedVehicle(null)
),
is(true)
);
assertThat(
Filters.orderSequenceWithIntendedVehicle(null)
.test(
new OrderSequence("some-sequence")
.withIntendedVehicle(new Vehicle("some-vehicle").getReference())
),
is(true)
);
}
@Test
void acceptOrderSequenceWithGivenIntendedVehicle() {
Vehicle vehicle = new Vehicle("some-vehicle");
assertThat(
Filters.orderSequenceWithIntendedVehicle(vehicle.getReference())
.test(
new OrderSequence("some-sequence")
.withIntendedVehicle(null)
),
is(false)
);
assertThat(
Filters.orderSequenceWithIntendedVehicle(null)
.test(
new OrderSequence("some-sequence")
.withIntendedVehicle(new Vehicle("some-vehicle").getReference())
),
is(true)
);
}
@Test
void acceptPeripheralJobsRegardlessOfRelatedVehicle() {
Location location
= new Location("some-location", new LocationType("some-location-type").getReference());
PeripheralJob job = new PeripheralJob(
"some-job",
"some-token",
new PeripheralOperation(
location.getReference(),
"some-operation",
PeripheralOperation.ExecutionTrigger.AFTER_ALLOCATION,
true
)
);
assertThat(
Filters.peripheralJobWithRelatedVehicle(null)
.test(
job.withRelatedVehicle(null)
),
is(true)
);
assertThat(
Filters.peripheralJobWithRelatedVehicle(null)
.test(
job.withRelatedVehicle(new Vehicle("some-vehicle").getReference())
),
is(true)
);
}
@Test
void acceptPeripheralJobsWithGivenRelatedVehicle() {
Location location
= new Location("some-location", new LocationType("some-location-type").getReference());
PeripheralJob job = new PeripheralJob(
"some-job",
"some-token",
new PeripheralOperation(
location.getReference(),
"some-operation",
PeripheralOperation.ExecutionTrigger.AFTER_ALLOCATION,
true
)
);
Vehicle vehicle = new Vehicle("some-vehicle");
assertThat(
Filters.peripheralJobWithRelatedVehicle(vehicle.getReference())
.test(
job.withRelatedVehicle(null)
),
is(false)
);
assertThat(
Filters.peripheralJobWithRelatedVehicle(vehicle.getReference())
.test(
job.withRelatedVehicle(vehicle.getReference())
),
is(true)
);
}
@Test
void acceptPeripheralJobsRegardlessOfRelatedTransportOrder() {
Location location
= new Location("some-location", new LocationType("some-location-type").getReference());
PeripheralJob job = new PeripheralJob(
"some-job",
"some-token",
new PeripheralOperation(
location.getReference(),
"some-operation",
PeripheralOperation.ExecutionTrigger.AFTER_ALLOCATION,
true
)
);
assertThat(
Filters.peripheralJobWithRelatedTransportOrder(null)
.test(
job.withRelatedTransportOrder(null)
),
is(true)
);
assertThat(
Filters.peripheralJobWithRelatedTransportOrder(null)
.test(
job.withRelatedTransportOrder(
new TransportOrder("some-order", List.of()).getReference()
)
),
is(true)
);
}
@Test
void acceptPeripheralJobsWithGivenRelatedTransportOrder() {
Location location
= new Location("some-location", new LocationType("some-location-type").getReference());
PeripheralJob job = new PeripheralJob(
"some-job",
"some-token",
new PeripheralOperation(
location.getReference(),
"some-operation",
PeripheralOperation.ExecutionTrigger.AFTER_ALLOCATION,
true
)
);
TransportOrder order = new TransportOrder("some-order", List.of());
assertThat(
Filters.peripheralJobWithRelatedTransportOrder(order.getReference())
.test(
job.withRelatedTransportOrder(null)
),
is(false)
);
assertThat(
Filters.peripheralJobWithRelatedTransportOrder(order.getReference())
.test(
job.withRelatedTransportOrder(order.getReference())
),
is(true)
);
}
@ParameterizedTest
@EnumSource(Vehicle.ProcState.class)
void acceptVehiclesWithAnyProcState(Vehicle.ProcState procState) {
assertThat(
Filters.vehicleWithProcState(null)
.test(
new Vehicle("some-vehicle")
.withProcState(procState)
),
is(true)
);
}
@Test
void acceptVehiclesWithGivenProcStateOnly() {
assertThat(
Filters.vehicleWithProcState(Vehicle.ProcState.IDLE)
.test(
new Vehicle("some-vehicle")
.withProcState(Vehicle.ProcState.IDLE)
),
is(true)
);
assertThat(
Filters.vehicleWithProcState(Vehicle.ProcState.IDLE)
.test(
new Vehicle("some-vehicle")
.withProcState(Vehicle.ProcState.AWAITING_ORDER)
),
is(false)
);
assertThat(
Filters.vehicleWithProcState(Vehicle.ProcState.IDLE)
.test(
new Vehicle("some-vehicle")
.withProcState(Vehicle.ProcState.PROCESSING_ORDER)
),
is(false)
);
}
}

View File

@@ -0,0 +1,69 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
import java.util.concurrent.Executors;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.opentcs.components.kernel.services.PlantModelService;
import org.opentcs.data.ObjectUnknownException;
import org.opentcs.data.model.Location;
import org.opentcs.data.model.LocationType;
import org.opentcs.kernel.extensions.servicewebapi.KernelExecutorWrapper;
/**
* Tests for {@link LocationHandler}.
*/
class LocationHandlerTest {
private PlantModelService plantModelService;
private KernelExecutorWrapper executorWrapper;
private LocationHandler handler;
private Location location;
@BeforeEach
void setUp() {
plantModelService = mock();
executorWrapper = new KernelExecutorWrapper(Executors.newSingleThreadExecutor());
handler = new LocationHandler(plantModelService, executorWrapper);
location = new Location(
"some-location",
new LocationType("some-location-type").getReference()
);
given(plantModelService.fetchObject(Location.class, "some-location"))
.willReturn(location);
}
@Test
void lockLocation() {
handler.updateLocationLock("some-location", "true");
then(plantModelService).should().updateLocationLock(location.getReference(), true);
}
@ParameterizedTest
@ValueSource(strings = {"false", "flase", "some-value-that-is-not-true"})
void unlockLocationOnAnyNontrueValue(String value) {
handler.updateLocationLock("some-location", value);
then(plantModelService).should().updateLocationLock(location.getReference(), false);
}
@ParameterizedTest
@ValueSource(strings = {"true", "false"})
void throwOnLockUnknownLocation(String value) {
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(() -> handler.updateLocationLock("some-unknown-location", value));
}
}

View File

@@ -0,0 +1,73 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
import java.util.concurrent.Executors;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.opentcs.components.kernel.services.PlantModelService;
import org.opentcs.components.kernel.services.TCSObjectService;
import org.opentcs.data.ObjectUnknownException;
import org.opentcs.data.model.Path;
import org.opentcs.data.model.Point;
import org.opentcs.kernel.extensions.servicewebapi.KernelExecutorWrapper;
/**
* Tests for {@link PathHandler}.
*/
class PathHandlerTest {
private TCSObjectService objectService;
private PlantModelService plantModelService;
private KernelExecutorWrapper executorWrapper;
private PathHandler handler;
private Path path;
@BeforeEach
void setUp() {
objectService = mock();
plantModelService = mock();
executorWrapper = new KernelExecutorWrapper(Executors.newSingleThreadExecutor());
handler = new PathHandler(objectService, executorWrapper, plantModelService);
path = new Path(
"some-path",
new Point("some-point-1").getReference(),
new Point("some-point-2").getReference()
);
given(objectService.fetchObject(Path.class, "some-path"))
.willReturn(path);
}
@Test
void lockPath() {
handler.updatePathLock("some-path", "true");
then(plantModelService).should().updatePathLock(path.getReference(), true);
}
@ParameterizedTest
@ValueSource(strings = {"false", "flase", "some-value-that-is-not-true"})
void unlockPathOnAnyNontrueValue(String value) {
handler.updatePathLock("some-path", value);
then(plantModelService).should().updatePathLock(path.getReference(), false);
}
@ParameterizedTest
@ValueSource(strings = {"true", "false"})
void throwOnLockUnknownPath(String value) {
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(() -> handler.updatePathLock("some-unknown-path", value));
}
}

View File

@@ -0,0 +1,157 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
import java.util.List;
import java.util.concurrent.Executors;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.opentcs.common.peripherals.NullPeripheralCommAdapterDescription;
import org.opentcs.components.kernel.services.PeripheralService;
import org.opentcs.data.ObjectUnknownException;
import org.opentcs.data.model.Location;
import org.opentcs.data.model.LocationType;
import org.opentcs.drivers.peripherals.PeripheralCommAdapterDescription;
import org.opentcs.drivers.peripherals.management.PeripheralAttachmentInformation;
import org.opentcs.kernel.extensions.servicewebapi.KernelExecutorWrapper;
/**
* Unit tests for {@link PeripheralJobHandler}.
*/
class PeripheralHandlerTest {
private PeripheralService peripheralService;
private KernelExecutorWrapper executorWrapper;
private PeripheralHandler handler;
private Location location;
private PeripheralCommAdapterDescription adapterDescriptionNull;
private PeripheralCommAdapterDescription adapterDescriptionMock;
private PeripheralAttachmentInformation attachmentInfo;
@BeforeEach
void setUp() {
peripheralService = mock();
executorWrapper = new KernelExecutorWrapper(Executors.newSingleThreadExecutor());
handler = new PeripheralHandler(peripheralService, executorWrapper);
location = new Location("some-location", new LocationType("some-location-type").getReference());
adapterDescriptionNull = new NullPeripheralCommAdapterDescription();
adapterDescriptionMock = new MockPeripheralCommAdapterDescription();
attachmentInfo = new PeripheralAttachmentInformation(
location.getReference(),
List.of(
adapterDescriptionNull,
adapterDescriptionMock
),
adapterDescriptionNull
);
given(peripheralService.fetchObject(Location.class, "some-location"))
.willReturn(location);
given(peripheralService.fetchAttachmentInformation(location.getReference()))
.willReturn(attachmentInfo);
}
@Test
void attachNullPeripheralAdapter() {
handler.putPeripheralCommAdapter(
"some-location",
NullPeripheralCommAdapterDescription.class.getName()
);
then(peripheralService)
.should()
.attachCommAdapter(location.getReference(), adapterDescriptionNull);
}
@Test
void attachMockPeripheralAdapter() {
handler.putPeripheralCommAdapter(
"some-location",
MockPeripheralCommAdapterDescription.class.getName()
);
then(peripheralService)
.should()
.attachCommAdapter(location.getReference(), adapterDescriptionMock);
}
@Test
void throwOnAttachAdapterForUnknownLocation() {
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(
() -> handler.putPeripheralCommAdapter(
"some-unknown-location",
NullPeripheralCommAdapterDescription.class.getName()
)
);
}
@Test
void throwOnAttachUnknownAdapter() {
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(
() -> handler.putPeripheralCommAdapter(
"some-location",
"some-unknown-adapter-class-name"
)
);
}
@Test
void enableCommAdapter() {
handler.putPeripheralCommAdapterEnabled("some-location", "true");
then(peripheralService).should().enableCommAdapter(location.getReference());
}
@ParameterizedTest
@ValueSource(strings = {"false", "flase", "some-value-that-is-not-true"})
void disableCommAdapterOnAnyNontrueValue(String value) {
handler.putPeripheralCommAdapterEnabled("some-location", value);
then(peripheralService).should().disableCommAdapter(location.getReference());
}
@ParameterizedTest
@ValueSource(strings = {"true ", "false"})
void throwOnEnableUnknownLocation(String value) {
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(() -> handler.putPeripheralCommAdapterEnabled("some-unknown-location", value));
}
@Test
void fetchAttachmentInformation() {
assertThat(handler.getPeripheralCommAdapterAttachmentInformation("some-location"))
.isSameAs(attachmentInfo);
}
@Test
void throwOnFetchInfoForUnknownLocation() {
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(
() -> handler.getPeripheralCommAdapterAttachmentInformation("some-unknown-location")
);
}
static class MockPeripheralCommAdapterDescription
extends
PeripheralCommAdapterDescription {
@Override
public String getDescription() {
return "some-peripheral-comm-adapter";
}
}
}

View File

@@ -0,0 +1,89 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
import java.util.concurrent.Executors;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.opentcs.components.kernel.services.PeripheralDispatcherService;
import org.opentcs.components.kernel.services.PeripheralJobService;
import org.opentcs.data.ObjectUnknownException;
import org.opentcs.data.model.Location;
import org.opentcs.data.model.LocationType;
import org.opentcs.data.peripherals.PeripheralJob;
import org.opentcs.data.peripherals.PeripheralOperation;
import org.opentcs.kernel.extensions.servicewebapi.KernelExecutorWrapper;
/**
* Unit tests for {@link PeripheralJobDispatcherHandler}.
*/
class PeripheralJobDispatcherHandlerTest {
private PeripheralJobService jobService;
private PeripheralDispatcherService jobDispatcherService;
private KernelExecutorWrapper executorWrapper;
private PeripheralJobDispatcherHandler handler;
private Location location;
private PeripheralJob job;
@BeforeEach
void setUp() {
jobService = mock();
jobDispatcherService = mock();
executorWrapper = new KernelExecutorWrapper(Executors.newSingleThreadExecutor());
handler = new PeripheralJobDispatcherHandler(
jobService,
jobDispatcherService,
executorWrapper
);
location = new Location("some-location", new LocationType("some-location-type").getReference());
job = new PeripheralJob(
"some-job",
"some-token",
new PeripheralOperation(
location.getReference(),
"some-operation",
PeripheralOperation.ExecutionTrigger.AFTER_ALLOCATION,
true
)
);
given(jobService.fetchObject(Location.class, "some-location"))
.willReturn(location);
given(jobService.fetchObject(PeripheralJob.class, "some-job"))
.willReturn(job);
}
@Test
void withdrawPeripheralJobByLocation() {
handler.withdrawPeripheralJobByLocation("some-location");
then(jobDispatcherService).should().withdrawByLocation(location.getReference());
}
@Test
void throwOnWithdrawPeripheralJobByUnknownLocation() {
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(() -> handler.withdrawPeripheralJobByLocation("some-unknown-location"));
}
@Test
void withdrawPeripheralJob() {
handler.withdrawPeripheralJob("some-job");
then(jobDispatcherService).should().withdrawByPeripheralJob(job.getReference());
}
@Test
void throwOnWithdrawUnknownPeripheralJob() {
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(() -> handler.withdrawPeripheralJob("some-unknown-job"));
}
}

View File

@@ -0,0 +1,267 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.theInstance;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executors;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatchers;
import org.opentcs.access.to.peripherals.PeripheralJobCreationTO;
import org.opentcs.components.kernel.services.PeripheralDispatcherService;
import org.opentcs.components.kernel.services.PeripheralJobService;
import org.opentcs.data.ObjectUnknownException;
import org.opentcs.data.model.Location;
import org.opentcs.data.model.LocationType;
import org.opentcs.data.model.Vehicle;
import org.opentcs.data.order.TransportOrder;
import org.opentcs.data.peripherals.PeripheralJob;
import org.opentcs.data.peripherals.PeripheralOperation;
import org.opentcs.kernel.extensions.servicewebapi.KernelExecutorWrapper;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.GetPeripheralJobResponseTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostPeripheralJobRequestTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.PeripheralOperationDescription;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.Property;
/**
* Unit tests for {@link PeripheralJobHandler}.
*/
class PeripheralJobHandlerTest {
private PeripheralJobService jobService;
private PeripheralDispatcherService jobDispatcherService;
private KernelExecutorWrapper executorWrapper;
private PeripheralJobHandler handler;
@BeforeEach
void setUp() {
jobService = mock();
jobDispatcherService = mock();
executorWrapper = new KernelExecutorWrapper(Executors.newSingleThreadExecutor());
handler = new PeripheralJobHandler(
jobService,
jobDispatcherService,
executorWrapper
);
}
@Test
void createPeripheralJob() {
// Arrange
Location location
= new Location("some-location", new LocationType("some-location-type").getReference());
Vehicle vehicle = new Vehicle("some-vehicle");
TransportOrder order = new TransportOrder("some-order", List.of());
PeripheralJob job = new PeripheralJob(
"some-job",
"some-token",
new PeripheralOperation(
location.getReference(),
"some-operation",
PeripheralOperation.ExecutionTrigger.AFTER_ALLOCATION,
true
)
)
.withRelatedVehicle(vehicle.getReference())
.withRelatedTransportOrder(order.getReference())
.withProperty("some-prop-key", "some-prop-value");
given(jobService.fetchObject(Location.class, "some-location"))
.willReturn(location);
given(jobService.fetchObject(Vehicle.class, "some-vehicle"))
.willReturn(vehicle);
given(jobService.fetchObject(TransportOrder.class, "some-order"))
.willReturn(order);
given(jobService.createPeripheralJob(any(PeripheralJobCreationTO.class)))
.willReturn(job);
// Act
PeripheralJob result = handler.createPeripheralJob(
"some-job",
new PostPeripheralJobRequestTO()
.setIncompleteName(false)
.setReservationToken("some-token")
.setPeripheralOperation(
new PeripheralOperationDescription()
.setLocationName("some-location")
.setOperation("some-operation")
.setExecutionTrigger(PeripheralOperation.ExecutionTrigger.AFTER_ALLOCATION)
.setCompletionRequired(true)
)
.setRelatedVehicle("some-vehicle")
.setRelatedTransportOrder("some-order")
.setProperties(List.of(new Property("some-prop-key", "some-prop-value")))
);
// Assert
assertThat(result, is(theInstance(job)));
then(jobService).should().createPeripheralJob(any(PeripheralJobCreationTO.class));
}
@Test
void retrievePeripheralJobsUnfiltered() {
// Arrange
Location location
= new Location("some-location", new LocationType("some-location-type").getReference());
PeripheralJob job1 = new PeripheralJob(
"some-job",
"some-token",
new PeripheralOperation(
location.getReference(),
"some-operation",
PeripheralOperation.ExecutionTrigger.AFTER_ALLOCATION,
true
)
);
PeripheralJob job2 = new PeripheralJob(
"some-job-2",
"some-token",
new PeripheralOperation(
location.getReference(),
"some-operation",
PeripheralOperation.ExecutionTrigger.AFTER_ALLOCATION,
true
)
);
given(
jobService.fetchObjects(ArgumentMatchers.<Class<PeripheralJob>>any(), any())
)
.willReturn(Set.of(job1, job2));
// Act
List<GetPeripheralJobResponseTO> result = handler.getPeripheralJobs(null, null);
// Assert
assertThat(result, hasSize(2));
then(jobService).should().fetchObjects(ArgumentMatchers.<Class<PeripheralJob>>any(), any());
}
@Test
void retrievePeripheralJobsFilteredByRelatedVehicle() {
// Arrange
Location location
= new Location("some-location", new LocationType("some-location-type").getReference());
PeripheralJob job1 = new PeripheralJob(
"some-job",
"some-token",
new PeripheralOperation(
location.getReference(),
"some-operation",
PeripheralOperation.ExecutionTrigger.AFTER_ALLOCATION,
true
)
);
PeripheralJob job2 = new PeripheralJob(
"some-job-2",
"some-token",
new PeripheralOperation(
location.getReference(),
"some-operation",
PeripheralOperation.ExecutionTrigger.AFTER_ALLOCATION,
true
)
);
Vehicle vehicle = new Vehicle("some-vehicle");
given(jobService.fetchObject(Vehicle.class, "some-vehicle"))
.willReturn(vehicle);
given(
jobService.fetchObjects(ArgumentMatchers.<Class<PeripheralJob>>any(), any())
)
.willReturn(Set.of(job1, job2));
// Act & Assert: happy path
List<GetPeripheralJobResponseTO> result = handler.getPeripheralJobs("some-vehicle", null);
assertThat(result, hasSize(2));
then(jobService).should().fetchObjects(ArgumentMatchers.<Class<PeripheralJob>>any(), any());
// Act & Assert: nonexistent vehicle
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(() -> handler.getPeripheralJobs("some-unknown-vehicle", null));
}
@Test
void retrievePeripheralJobsFilteredByRelatedTransportOrder() {
// Arrange
Location location
= new Location("some-location", new LocationType("some-location-type").getReference());
PeripheralJob job1 = new PeripheralJob(
"some-job",
"some-token",
new PeripheralOperation(
location.getReference(),
"some-operation",
PeripheralOperation.ExecutionTrigger.AFTER_ALLOCATION,
true
)
);
PeripheralJob job2 = new PeripheralJob(
"some-job-2",
"some-token",
new PeripheralOperation(
location.getReference(),
"some-operation",
PeripheralOperation.ExecutionTrigger.AFTER_ALLOCATION,
true
)
);
TransportOrder transportOrder = new TransportOrder("some-order", List.of());
given(jobService.fetchObject(TransportOrder.class, "some-order"))
.willReturn(transportOrder);
given(
jobService.fetchObjects(ArgumentMatchers.<Class<PeripheralJob>>any(), any())
)
.willReturn(Set.of(job1, job2));
// Act & Assert: happy path
List<GetPeripheralJobResponseTO> result = handler.getPeripheralJobs(null, "some-order");
assertThat(result, hasSize(2));
then(jobService).should().fetchObjects(ArgumentMatchers.<Class<PeripheralJob>>any(), any());
// Act & Assert: nonexistent vehicle
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(() -> handler.getPeripheralJobs(null, "some-unknown-order"));
}
@Test
void retrievPeripheralJobByName() {
// Arrange
Location location
= new Location("some-location", new LocationType("some-location-type").getReference());
PeripheralJob job = new PeripheralJob(
"some-job",
"some-token",
new PeripheralOperation(
location.getReference(),
"some-operation",
PeripheralOperation.ExecutionTrigger.AFTER_ALLOCATION,
true
)
);
given(jobService.fetchObject(PeripheralJob.class, "some-job"))
.willReturn(job);
// Act & Assert: happy path
GetPeripheralJobResponseTO result = handler.getPeripheralJobByName("some-job");
assertThat(result, is(notNullValue()));
then(jobService).should().fetchObject(PeripheralJob.class, "some-job");
// Act & Assert: nonexistent order
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(() -> handler.getPeripheralJobByName("some-unknown-order"));
}
}

View File

@@ -0,0 +1,253 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executors;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.opentcs.access.to.model.PlantModelCreationTO;
import org.opentcs.components.kernel.services.PlantModelService;
import org.opentcs.components.kernel.services.RouterService;
import org.opentcs.data.model.Block;
import org.opentcs.data.model.Location;
import org.opentcs.data.model.LocationType;
import org.opentcs.data.model.Path;
import org.opentcs.data.model.PlantModel;
import org.opentcs.data.model.Point;
import org.opentcs.data.model.Vehicle;
import org.opentcs.data.model.visualization.VisualLayout;
import org.opentcs.kernel.extensions.servicewebapi.KernelExecutorWrapper;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PlantModelTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostTopologyUpdateRequestTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.BlockTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.LocationTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.LocationTypeTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.PathTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.PointTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.VehicleTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.plantmodel.VisualLayoutTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.PropertyTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.TripleTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.converter.BlockConverter;
import org.opentcs.kernel.extensions.servicewebapi.v1.converter.EnvelopeConverter;
import org.opentcs.kernel.extensions.servicewebapi.v1.converter.LocationConverter;
import org.opentcs.kernel.extensions.servicewebapi.v1.converter.LocationTypeConverter;
import org.opentcs.kernel.extensions.servicewebapi.v1.converter.PathConverter;
import org.opentcs.kernel.extensions.servicewebapi.v1.converter.PeripheralOperationConverter;
import org.opentcs.kernel.extensions.servicewebapi.v1.converter.PointConverter;
import org.opentcs.kernel.extensions.servicewebapi.v1.converter.PropertyConverter;
import org.opentcs.kernel.extensions.servicewebapi.v1.converter.VehicleConverter;
import org.opentcs.kernel.extensions.servicewebapi.v1.converter.VisualLayoutConverter;
/**
* Unit tests for {@link PlantModelHandler}.
*/
class PlantModelHandlerTest {
private PlantModelService orderService;
private KernelExecutorWrapper executorWrapper;
private PlantModelHandler handler;
private RouterService routerService;
@BeforeEach
void setUp() {
orderService = mock();
executorWrapper = new KernelExecutorWrapper(Executors.newSingleThreadExecutor());
EnvelopeConverter envelopeConverter = new EnvelopeConverter();
PropertyConverter propertyConverter = new PropertyConverter();
routerService = mock();
handler = new PlantModelHandler(
orderService,
executorWrapper,
new PointConverter(propertyConverter, envelopeConverter),
new PathConverter(
propertyConverter,
new PeripheralOperationConverter(),
envelopeConverter
),
new LocationTypeConverter(propertyConverter),
new LocationConverter(propertyConverter),
new BlockConverter(propertyConverter),
new VehicleConverter(propertyConverter),
new VisualLayoutConverter(propertyConverter),
propertyConverter,
routerService
);
}
@Test
void putPlantModel() {
// Act
handler.putPlantModel(
new PlantModelTO("name")
.setPoints(
List.of(
new PointTO("some-point"),
new PointTO("some-other-point")
)
)
.setPaths(
List.of(
new PathTO("some-path", "src-point", "dest-point")
)
)
.setLocationTypes(
List.of(
new LocationTypeTO("some-loc-type"),
new LocationTypeTO("some-other-loc-type")
)
)
.setLocations(
List.of(
new LocationTO(
"some-location",
"some-loc-type",
new TripleTO(1, 2, 3)
),
new LocationTO(
"some-other-location",
"some-other-loc-type",
new TripleTO(4, 5, 6)
)
)
)
.setBlocks(
List.of(
new BlockTO("some-block")
)
)
.setVehicles(
List.of(
new VehicleTO("some-vehicle"),
new VehicleTO("some-other-vehicle")
)
)
.setVisualLayout(new VisualLayoutTO("some-layout"))
.setProperties(
List.of(
new PropertyTO("some-key", "some-value")
)
)
);
// Assert
ArgumentCaptor<PlantModelCreationTO> captor
= ArgumentCaptor.forClass(PlantModelCreationTO.class);
then(orderService).should().createPlantModel(captor.capture());
assertThat(captor.getValue().getPoints()).hasSize(2);
assertThat(captor.getValue().getPaths()).hasSize(1);
assertThat(captor.getValue().getLocationTypes()).hasSize(2);
assertThat(captor.getValue().getLocations()).hasSize(2);
assertThat(captor.getValue().getBlocks()).hasSize(1);
assertThat(captor.getValue().getVehicles()).hasSize(2);
assertThat(captor.getValue().getVisualLayout()).isNotNull();
assertThat(captor.getValue().getProperties()).hasSize(1);
}
@Test
void getPlantModel() {
// Arrange
PlantModel plantModel
= new PlantModel("some-plant-model")
.withPoints(
Set.of(
new Point("some-point"),
new Point("some-other-point")
)
)
.withPaths(
Set.of(
new Path(
"some-path",
new Point("src-point").getReference(),
new Point("dest-point").getReference()
)
)
)
.withLocationTypes(
Set.of(
new LocationType("some-loc-type"),
new LocationType("some-other-loc-type")
)
)
.withLocations(
Set.of(
new Location(
"some-location",
new LocationType("loc-type").getReference()
),
new Location(
"some-other-location",
new LocationType("loc-other-type").getReference()
)
)
)
.withBlocks(
Set.of(
new Block("some-block")
)
)
.withVehicles(
Set.of(
new Vehicle("some-vehicle"),
new Vehicle("some-other-vehicle")
)
)
.withVisualLayout(new VisualLayout("some-layout"))
.withProperties(
Map.of("some-key", "some-value")
);
given(orderService.getPlantModel())
.willReturn(plantModel);
// Act
PlantModelTO to = handler.getPlantModel();
// Assert
then(orderService).should().getPlantModel();
assertThat(to)
.returns("some-plant-model", PlantModelTO::getName);
assertThat(to.getPoints()).hasSize(2);
assertThat(to.getPaths()).hasSize(1);
assertThat(to.getLocationTypes()).hasSize(2);
assertThat(to.getLocations()).hasSize(2);
assertThat(to.getBlocks()).hasSize(1);
assertThat(to.getVehicles()).hasSize(2);
assertThat(to.getVisualLayout()).isNotNull();
assertThat(to.getProperties()).hasSize(1);
}
@Test
void requestTopologyUpdate() {
handler.requestTopologyUpdate(
new PostTopologyUpdateRequestTO(List.of())
);
then(routerService).should().updateRoutingTopology(Set.of());
}
@Test
void requestTopologyUpdateWithPath() {
Path path1 = new Path(
"path1",
new Point("source").getReference(),
new Point("dest").getReference()
);
given(orderService.fetchObject(Path.class, "path1")).willReturn(path1);
handler.requestTopologyUpdate(
new PostTopologyUpdateRequestTO(List.of("path1"))
);
then(routerService).should().updateRoutingTopology(Set.of(path1.getReference()));
}
}

View File

@@ -0,0 +1,163 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import java.util.List;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.opentcs.access.Kernel;
import org.opentcs.access.KernelStateTransitionEvent;
import org.opentcs.data.TCSObjectEvent;
import org.opentcs.data.model.Location;
import org.opentcs.data.model.LocationType;
import org.opentcs.data.model.Point;
import org.opentcs.data.model.Vehicle;
import org.opentcs.data.order.TransportOrder;
import org.opentcs.data.peripherals.PeripheralJob;
import org.opentcs.data.peripherals.PeripheralOperation;
import org.opentcs.kernel.extensions.servicewebapi.ServiceWebApiConfiguration;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.GetEventsResponseTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.getevents.OrderStatusMessage;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.getevents.PeripheralJobStatusMessage;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.getevents.VehicleStatusMessage;
import org.opentcs.util.event.EventSource;
import org.opentcs.util.event.SimpleEventBus;
/**
* Unit tests for {@link StatusEventDispatcher}.
*/
class StatusEventDispatcherTest {
private ServiceWebApiConfiguration configuration;
private EventSource eventSource;
private StatusEventDispatcher statusEventDispatcher;
@BeforeEach
void setUp() {
configuration = mock(ServiceWebApiConfiguration.class);
eventSource = new SimpleEventBus();
statusEventDispatcher = new StatusEventDispatcher(configuration, eventSource);
given(configuration.statusEventsCapacity())
.willReturn(10);
statusEventDispatcher.initialize();
}
@AfterEach
void tearDown() {
statusEventDispatcher.terminate();
}
@Test
void returnEmptyListInitially() {
GetEventsResponseTO result = statusEventDispatcher.fetchEvents(0, Long.MAX_VALUE, 1);
assertThat(result.getStatusMessages()).isEmpty();
}
@Test
void suppressEventCollectionInModellingMode() {
// Arrange
statusEventDispatcher.onEvent(
new KernelStateTransitionEvent(Kernel.State.MODELLING, Kernel.State.OPERATING, true)
);
statusEventDispatcher.onEvent(
new KernelStateTransitionEvent(Kernel.State.OPERATING, Kernel.State.MODELLING, true)
);
TransportOrder order = new TransportOrder("some-order", List.of());
for (int i = 0; i < 5; i++) {
statusEventDispatcher.onEvent(
new TCSObjectEvent(order, order, TCSObjectEvent.Type.OBJECT_MODIFIED)
);
}
// Act
GetEventsResponseTO result = statusEventDispatcher.fetchEvents(0, Long.MAX_VALUE, 1);
// Assert
assertThat(result.getStatusMessages()).isEmpty();
}
@Test
void respectConfiguredCapacity() {
// Arrange
statusEventDispatcher.onEvent(
new KernelStateTransitionEvent(Kernel.State.MODELLING, Kernel.State.OPERATING, true)
);
TransportOrder order = new TransportOrder("some-order", List.of());
for (int i = 0; i < 20; i++) {
statusEventDispatcher.onEvent(
new TCSObjectEvent(order, order, TCSObjectEvent.Type.OBJECT_MODIFIED)
);
}
// Act
GetEventsResponseTO result = statusEventDispatcher.fetchEvents(0, Long.MAX_VALUE, 1);
// Assert
assertThat(result.getStatusMessages()).hasSize(10);
assertThat(result.getStatusMessages().get(9).getSequenceNumber()).isEqualTo(19);
}
@Test
void processEventsForRelatedObjects() {
// Arrange
statusEventDispatcher.onEvent(
new KernelStateTransitionEvent(Kernel.State.MODELLING, Kernel.State.OPERATING, true)
);
TransportOrder order = new TransportOrder("some-order", List.of());
Vehicle vehicle = new Vehicle("some-vehicle");
PeripheralJob job = new PeripheralJob(
"some-job",
"some-token",
new PeripheralOperation(
new Location(
"some-location",
new LocationType("some-location-type").getReference()
).getReference(),
"some-operation",
PeripheralOperation.ExecutionTrigger.AFTER_ALLOCATION,
true
)
);
Point point = new Point("some-point");
statusEventDispatcher.onEvent(
new TCSObjectEvent(order, order, TCSObjectEvent.Type.OBJECT_MODIFIED)
);
statusEventDispatcher.onEvent(
new TCSObjectEvent(vehicle, vehicle, TCSObjectEvent.Type.OBJECT_MODIFIED)
);
statusEventDispatcher.onEvent(
new TCSObjectEvent(job, job, TCSObjectEvent.Type.OBJECT_MODIFIED)
);
// Events for other object types, e.g. points, should be ignored.
statusEventDispatcher.onEvent(
new TCSObjectEvent(point, point, TCSObjectEvent.Type.OBJECT_MODIFIED)
);
// Act
GetEventsResponseTO list = statusEventDispatcher.fetchEvents(0, Long.MAX_VALUE, 1);
// Assert
assertThat(list.getStatusMessages()).hasSize(3);
assertThat(list.getStatusMessages().get(0))
.isInstanceOf(OrderStatusMessage.class)
.matches(msg -> msg.getSequenceNumber() == 0);
assertThat(list.getStatusMessages().get(1))
.isInstanceOf(VehicleStatusMessage.class)
.matches(msg -> msg.getSequenceNumber() == 1);
assertThat(list.getStatusMessages().get(2))
.isInstanceOf(PeripheralJobStatusMessage.class)
.matches(msg -> msg.getSequenceNumber() == 2);
}
}

View File

@@ -0,0 +1,162 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
import java.util.List;
import java.util.concurrent.Executors;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.opentcs.components.kernel.services.DispatcherService;
import org.opentcs.components.kernel.services.VehicleService;
import org.opentcs.data.ObjectUnknownException;
import org.opentcs.data.model.Vehicle;
import org.opentcs.data.order.ReroutingType;
import org.opentcs.data.order.TransportOrder;
import org.opentcs.kernel.extensions.servicewebapi.KernelExecutorWrapper;
/**
* Unit tests for {@link TransportOrderDispatcherHandler}.
*/
class TransportOrderDispatcherHandlerTest {
private VehicleService vehicleService;
private DispatcherService dispatcherService;
private KernelExecutorWrapper executorWrapper;
private TransportOrderDispatcherHandler handler;
private Vehicle vehicle;
private TransportOrder order;
@BeforeEach
void setUp() {
vehicleService = mock();
dispatcherService = mock();
executorWrapper = new KernelExecutorWrapper(Executors.newSingleThreadExecutor());
handler = new TransportOrderDispatcherHandler(
vehicleService,
dispatcherService,
executorWrapper
);
vehicle = new Vehicle("some-vehicle");
order = new TransportOrder("some-order", List.of())
.withProcessingVehicle(vehicle.getReference());
given(vehicleService.fetchObject(Vehicle.class, "some-vehicle"))
.willReturn(vehicle);
given(vehicleService.fetchObject(TransportOrder.class, "some-order"))
.willReturn(order);
}
@Test
void triggerDispatcher() {
handler.triggerDispatcher();
then(dispatcherService).should().dispatch();
}
@Test
void tryImmediateAssignmentUsingKnownOrder() {
handler.tryImmediateAssignment("some-order");
then(dispatcherService).should().assignNow(order.getReference());
}
@Test
void throwOnImmediateAssignmentUsingUnknownOrderName() {
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(() -> handler.tryImmediateAssignment("some-unknown-order"));
}
@Test
void withdrawByTransportOrderRegularly() {
handler.withdrawByTransportOrder("some-order", false, false);
then(dispatcherService).should().withdrawByTransportOrder(order.getReference(), false);
}
@Test
void withdrawByTransportOrderImmediately() {
handler.withdrawByTransportOrder("some-order", true, false);
then(dispatcherService).should().withdrawByTransportOrder(order.getReference(), true);
}
@Test
void withdrawByTransportOrderAlsoDisablingVehicle() {
handler.withdrawByTransportOrder("some-order", false, true);
then(vehicleService)
.should()
.updateVehicleIntegrationLevel(
vehicle.getReference(),
Vehicle.IntegrationLevel.TO_BE_RESPECTED
);
then(dispatcherService).should().withdrawByTransportOrder(order.getReference(), false);
}
@Test
void throwOnWithdrawUsingUnknownOrderName() {
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(() -> handler.withdrawByTransportOrder("some-unknown-order", false, false));
}
@Test
void withdrawByVehicleRegularly() {
handler.withdrawByVehicle("some-vehicle", false, false);
then(dispatcherService).should().withdrawByVehicle(vehicle.getReference(), false);
}
@Test
void withdrawByVehicleImmediately() {
handler.withdrawByVehicle("some-vehicle", true, false);
then(dispatcherService).should().withdrawByVehicle(vehicle.getReference(), true);
}
@Test
void withdrawByVehicleAlsoDisablingVehicle() {
handler.withdrawByVehicle("some-vehicle", false, true);
then(vehicleService)
.should()
.updateVehicleIntegrationLevel(
vehicle.getReference(),
Vehicle.IntegrationLevel.TO_BE_RESPECTED
);
then(dispatcherService).should().withdrawByVehicle(vehicle.getReference(), false);
}
@Test
void throwOnWithdrawUsingUnknownVehicleName() {
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(() -> handler.withdrawByVehicle("some-unknown-vehicle", false, false));
}
@Test
void rerouteRegularly() {
handler.reroute("some-vehicle", false);
then(dispatcherService).should().reroute(vehicle.getReference(), ReroutingType.REGULAR);
}
@Test
void rerouteForcibly() {
handler.reroute("some-vehicle", true);
then(dispatcherService).should().reroute(vehicle.getReference(), ReroutingType.FORCED);
}
@Test
void throwOnRerouteUsingUnknownVehicleName() {
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(() -> handler.reroute("some-unknown-vehicle", false));
}
}

View File

@@ -0,0 +1,341 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.from;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.theInstance;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executors;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
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.services.TransportOrderService;
import org.opentcs.data.ObjectUnknownException;
import org.opentcs.data.model.Vehicle;
import org.opentcs.data.order.OrderSequence;
import org.opentcs.data.order.TransportOrder;
import org.opentcs.kernel.extensions.servicewebapi.KernelExecutorWrapper;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.GetOrderSequenceResponseTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.GetTransportOrderResponseTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostOrderSequenceRequestTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostTransportOrderRequestTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.posttransportorder.Destination;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.Property;
/**
* Unit tests for {@link TransportOrderHandler}.
*/
class TransportOrderHandlerTest {
private TransportOrderService orderService;
private KernelExecutorWrapper executorWrapper;
private TransportOrderHandler handler;
@BeforeEach
void setUp() {
orderService = mock();
executorWrapper = new KernelExecutorWrapper(Executors.newSingleThreadExecutor());
handler = new TransportOrderHandler(orderService, executorWrapper);
}
@Test
void createTransportOrder() {
// Arrange
TransportOrder transportOrder = new TransportOrder("some-order", List.of());
given(orderService.createTransportOrder(any(TransportOrderCreationTO.class)))
.willReturn(transportOrder);
// Act
TransportOrder result = handler.createOrder(
"some-order",
new PostTransportOrderRequestTO(
false,
false,
Instant.MAX,
null,
null,
null,
null,
List.of(
new Destination(
"some-location",
"some-operation",
List.of(
new Property("some-dest-key", "some-dest-value")
)
)
),
List.of(
new Property("some-key", "some-value")
),
null
)
);
// Assert
assertThat(result, is(theInstance(transportOrder)));
ArgumentCaptor<TransportOrderCreationTO> captor
= ArgumentCaptor.forClass(TransportOrderCreationTO.class);
then(orderService).should().createTransportOrder(captor.capture());
assertThat(captor.getValue())
.returns("some-order", from(TransportOrderCreationTO::getName))
.returns(false, from(TransportOrderCreationTO::hasIncompleteName))
.returns(false, from(TransportOrderCreationTO::isDispensable))
.returns(Instant.MAX, from(TransportOrderCreationTO::getDeadline))
.returns(null, from(TransportOrderCreationTO::getWrappingSequence))
.returns(null, from(TransportOrderCreationTO::getPeripheralReservationToken))
.returns(Set.of(), from(TransportOrderCreationTO::getDependencyNames))
.returns(Map.of("some-key", "some-value"), from(TransportOrderCreationTO::getProperties));
assertThat(captor.getValue().getDestinations()).hasSize(1);
assertThat(captor.getValue().getDestinations().get(0))
.returns("some-location", from(DestinationCreationTO::getDestLocationName))
.returns("some-operation", from(DestinationCreationTO::getDestOperation))
.returns(
Map.of("some-dest-key", "some-dest-value"),
from(DestinationCreationTO::getProperties)
);
}
@Test
void setTransportOrderIntendedVehicle() {
// Arrange
TransportOrder transportOrder = new TransportOrder("some-order", List.of());
Vehicle vehicle = new Vehicle("some-vehicle");
given(orderService.fetchObject(TransportOrder.class, "some-order"))
.willReturn(transportOrder);
given(orderService.fetchObject(Vehicle.class, "some-vehicle"))
.willReturn(vehicle);
// Act & Assert: set to vehicle
handler.updateTransportOrderIntendedVehicle("some-order", "some-vehicle");
then(orderService).should().updateTransportOrderIntendedVehicle(
transportOrder.getReference(),
vehicle.getReference()
);
// Act & Assert: set to null
handler.updateTransportOrderIntendedVehicle("some-order", null);
then(orderService).should().updateTransportOrderIntendedVehicle(
transportOrder.getReference(),
null
);
// Act & Assert: nonexistent transport order
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(() -> handler.updateTransportOrderIntendedVehicle("some-unknown-order", null));
// Act & Assert: nonexistent vehicle
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(
() -> handler.updateTransportOrderIntendedVehicle("some-order", "some-unknown-vehicle")
);
}
@Test
void retrieveTransportOrdersUnfiltered() {
// Arrange
TransportOrder transportOrder1 = new TransportOrder("some-order", List.of());
TransportOrder transportOrder2 = new TransportOrder("some-order-2", List.of());
given(
orderService.fetchObjects(ArgumentMatchers.<Class<TransportOrder>>any(), any())
)
.willReturn(Set.of(transportOrder1, transportOrder2));
// Act
List<GetTransportOrderResponseTO> result = handler.getTransportOrders(null);
// Assert
assertThat(result, hasSize(2));
then(orderService).should().fetchObjects(ArgumentMatchers.<Class<TransportOrder>>any(), any());
}
@Test
void retrieveTransportOrdersFilteredByIntendedVehicle() {
// Arrange
TransportOrder transportOrder1 = new TransportOrder("some-order", List.of());
TransportOrder transportOrder2 = new TransportOrder("some-order-2", List.of());
Vehicle vehicle = new Vehicle("some-vehicle");
given(
orderService.fetchObject(Vehicle.class, "some-vehicle")
)
.willReturn(vehicle);
given(
orderService.fetchObjects(ArgumentMatchers.<Class<TransportOrder>>any(), any())
)
.willReturn(Set.of(transportOrder1, transportOrder2));
// Act & Assert: happy path
List<GetTransportOrderResponseTO> result = handler.getTransportOrders("some-vehicle");
assertThat(result, hasSize(2));
then(orderService).should().fetchObjects(ArgumentMatchers.<Class<TransportOrder>>any(), any());
// Act & Assert: nonexistent vehicle
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(() -> handler.getTransportOrders("some-other-vehicle"));
}
@Test
void retrieveTransportOrderByName() {
// Arrange
TransportOrder transportOrder = new TransportOrder("some-order", List.of());
given(
orderService.fetchObject(TransportOrder.class, "some-order")
)
.willReturn(transportOrder);
// Act & Assert: happy path
GetTransportOrderResponseTO result = handler.getTransportOrderByName("some-order");
assertThat(result, is(notNullValue()));
then(orderService).should().fetchObject(TransportOrder.class, "some-order");
// Act & Assert: nonexistent order
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(() -> handler.getTransportOrderByName("some-other-order"));
}
@Test
void createOrderSequence() {
// Arrange
OrderSequence orderSequence = new OrderSequence("some-sequence");
given(orderService.createOrderSequence(any(OrderSequenceCreationTO.class)))
.willReturn(orderSequence);
// Act
OrderSequence result = handler.createOrderSequence(
"some-sequence",
new PostOrderSequenceRequestTO()
.setIncompleteName(false)
.setFailureFatal(false)
.setIntendedVehicle(null)
.setType("some-type")
.setProperties(
List.of(
new Property("some-key", "some-value")
)
)
);
// Assert
assertThat(result, is(theInstance(orderSequence)));
ArgumentCaptor<OrderSequenceCreationTO> captor
= ArgumentCaptor.forClass(OrderSequenceCreationTO.class);
then(orderService).should().createOrderSequence(captor.capture());
assertThat(captor.getValue())
.returns(false, from(OrderSequenceCreationTO::hasIncompleteName))
.returns(null, from(OrderSequenceCreationTO::getIntendedVehicleName))
.returns(false, from(OrderSequenceCreationTO::isFailureFatal))
.returns("some-type", from(OrderSequenceCreationTO::getType))
.returns(
Map.of("some-key", "some-value"),
from(OrderSequenceCreationTO::getProperties)
);
}
@Test
void setOrderSequenceComplete() {
// Arrange
OrderSequence orderSequence = new OrderSequence("some-sequence");
given(orderService.fetchObject(OrderSequence.class, "some-sequence"))
.willReturn(orderSequence);
// Act & Assert: happy path
handler.putOrderSequenceComplete("some-sequence");
then(orderService).should().markOrderSequenceComplete(orderSequence.getReference());
// Act & Assert: nonexistent order sequence
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(() -> handler.putOrderSequenceComplete("some-other-sequence"));
}
@Test
void retrieveOrderSequencesUnfiltered() {
// Arrange
OrderSequence sequence1 = new OrderSequence("some-sequence");
OrderSequence sequence2 = new OrderSequence("some-sequence-2");
given(
orderService.fetchObjects(ArgumentMatchers.<Class<OrderSequence>>any(), any())
)
.willReturn(Set.of(sequence1, sequence2));
// Act
List<GetOrderSequenceResponseTO> result = handler.getOrderSequences(null);
// Assert
assertThat(result, hasSize(2));
then(orderService).should().fetchObjects(ArgumentMatchers.<Class<OrderSequence>>any(), any());
}
@Test
void retrieveOrderSequencesFilteredByIntendedVehicle() {
// Arrange
OrderSequence sequence1 = new OrderSequence("some-sequence");
OrderSequence sequence2 = new OrderSequence("some-sequence-2");
Vehicle vehicle = new Vehicle("some-vehicle");
given(
orderService.fetchObject(Vehicle.class, "some-vehicle")
)
.willReturn(vehicle);
given(
orderService.fetchObjects(ArgumentMatchers.<Class<OrderSequence>>any(), any())
)
.willReturn(Set.of(sequence1, sequence2));
// Act & Assert: happy path
List<GetOrderSequenceResponseTO> result = handler.getOrderSequences("some-vehicle");
assertThat(result, hasSize(2));
then(orderService).should().fetchObjects(ArgumentMatchers.<Class<OrderSequence>>any(), any());
// Act & Assert: nonexistent vehicle
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(() -> handler.getOrderSequences("some-other-vehicle"));
}
@Test
void retrieveOrderSequenceByName() {
// Arrange
OrderSequence orderSequence = new OrderSequence("some-sequence");
given(
orderService.fetchObject(OrderSequence.class, "some-sequence")
)
.willReturn(orderSequence);
// Act & Assert: happy path
GetOrderSequenceResponseTO result = handler.getOrderSequenceByName("some-sequence");
assertThat(result, is(notNullValue()));
then(orderService).should().fetchObject(OrderSequence.class, "some-sequence");
// Act & Assert: nonexistent order
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(() -> handler.getOrderSequenceByName("some-other-sequence"));
}
}

View File

@@ -0,0 +1,470 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executors;
import org.hamcrest.MatcherAssert;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import org.opentcs.components.kernel.services.RouterService;
import org.opentcs.components.kernel.services.VehicleService;
import org.opentcs.data.ObjectUnknownException;
import org.opentcs.data.model.Location;
import org.opentcs.data.model.LocationType;
import org.opentcs.data.model.Path;
import org.opentcs.data.model.Point;
import org.opentcs.data.model.Vehicle;
import org.opentcs.drivers.vehicle.VehicleCommAdapterDescription;
import org.opentcs.drivers.vehicle.management.VehicleAttachmentInformation;
import org.opentcs.kernel.extensions.servicewebapi.KernelExecutorWrapper;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.GetVehicleResponseTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PostVehicleRoutesRequestTO;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.PutVehicleAllowedOrderTypesTO;
/**
* Unit tests for {@link VehicleHandler}.
*/
class VehicleHandlerTest {
private VehicleService vehicleService;
private RouterService routerService;
private KernelExecutorWrapper executorWrapper;
private VehicleHandler handler;
private Vehicle vehicle;
private VehicleCommAdapterDescription adapterDescriptionMock;
private VehicleAttachmentInformation attachmentInfo;
@BeforeEach
void setUp() {
vehicleService = mock();
routerService = mock();
executorWrapper = new KernelExecutorWrapper(Executors.newSingleThreadExecutor());
handler = new VehicleHandler(vehicleService, routerService, executorWrapper);
vehicle = new Vehicle("some-vehicle");
adapterDescriptionMock = new MockVehicleCommAdapterDescription();
attachmentInfo = new VehicleAttachmentInformation(
vehicle.getReference(),
List.of(adapterDescriptionMock),
adapterDescriptionMock
);
given(vehicleService.fetchObject(Vehicle.class, "some-vehicle"))
.willReturn(vehicle);
given(vehicleService.fetchAttachmentInformation(vehicle.getReference()))
.willReturn(attachmentInfo);
}
@Test
void attachMockVehicleAdapter() {
// Act
handler.putVehicleCommAdapter(
"some-vehicle",
MockVehicleCommAdapterDescription.class.getName()
);
// Assert
then(vehicleService)
.should()
.attachCommAdapter(vehicle.getReference(), adapterDescriptionMock);
}
@Test
void throwOnAttachAdapterForUnknownVehicle() {
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(
() -> handler.putVehicleCommAdapter(
"some-unknown-vehicle",
MockVehicleCommAdapterDescription.class.getName()
)
);
}
@Test
void throwOnAttachUnknownAdapter() {
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(
() -> handler.putVehicleCommAdapter(
"some-vehicle",
"some-unknown-adapter-class-name"
)
);
}
@Test
void enableCommAdapter() {
handler.putVehicleCommAdapterEnabled("some-vehicle", "true");
then(vehicleService).should().enableCommAdapter(vehicle.getReference());
}
@ParameterizedTest
@ValueSource(strings = {"false", "flase", "some-value-that-is-not-true"})
void disableCommAdapterOnAnyNontrueValue(String value) {
handler.putVehicleCommAdapterEnabled("some-vehicle", value);
then(vehicleService).should().disableCommAdapter(vehicle.getReference());
}
@ParameterizedTest
@ValueSource(strings = {"true ", "false"})
void throwOnEnableUnknownVehicle(String value) {
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(() -> handler.putVehicleCommAdapterEnabled("some-unknown-vehicle", value));
}
@Test
void fetchAttachmentInformation() {
assertThat(handler.getVehicleCommAdapterAttachmentInformation("some-vehicle"))
.isSameAs(attachmentInfo);
}
@Test
void throwOnFetchInfoForUnknownLocation() {
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(
() -> handler.getVehicleCommAdapterAttachmentInformation("some-unknown-vehicle")
);
}
@ParameterizedTest
@EnumSource(Vehicle.ProcState.class)
void retrieveVehiclesByProcState(Vehicle.ProcState procState) {
// Arrange
Vehicle vehicleWithProcState = vehicle.withProcState(procState);
given(vehicleService.fetchObjects(ArgumentMatchers.<Class<Vehicle>>any(), any()))
.willReturn(Set.of(vehicleWithProcState));
// Act & Assert
List<GetVehicleResponseTO> result = handler.getVehiclesState(procState.name());
MatcherAssert.assertThat(result, hasSize(1));
then(vehicleService).should().fetchObjects(ArgumentMatchers.<Class<Vehicle>>any(), any());
}
@Test
void throwOnRetrieveVehiclesForUnknownProcState() {
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> handler.getVehiclesState("some-unknown-proc-state"));
}
@Test
void retrieveVehicleByName() {
// Act & Assert: happy path
GetVehicleResponseTO result = handler.getVehicleStateByName("some-vehicle");
MatcherAssert.assertThat(result, is(notNullValue()));
then(vehicleService).should().fetchObject(Vehicle.class, "some-vehicle");
// Act & Assert: nonexistent vehicle
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(() -> handler.getVehicleStateByName("some-other-vehicle"));
}
@ParameterizedTest
@EnumSource(Vehicle.IntegrationLevel.class)
void updateVehicleIntegrationLevel(Vehicle.IntegrationLevel integrationLevel) {
handler.putVehicleIntegrationLevel("some-vehicle", integrationLevel.name());
then(vehicleService)
.should()
.updateVehicleIntegrationLevel(vehicle.getReference(), integrationLevel);
}
@Test
void throwOnUpdateIntegrationLevelForUnknownVehicleOrIntegrationLevel() {
// Act & Assert: nonexistent vehicle
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(
() -> handler.putVehicleIntegrationLevel(
"some-unknown-vehicle",
"TO_BE_UTILIZED"
)
);
// Act & Assert: unknown integration level
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(
() -> handler.putVehicleIntegrationLevel(
"some-vehicle",
"some-unknown-integration-level"
)
);
}
@Test
void pauseVehicle() {
handler.putVehiclePaused("some-vehicle", "true");
then(vehicleService).should().updateVehiclePaused(vehicle.getReference(), true);
}
@ParameterizedTest
@ValueSource(strings = {"false", "flase", "some-value-that-is-not-true"})
void unpauseVehicleOnAnyNontrueValue(String value) {
handler.putVehiclePaused("some-vehicle", value);
then(vehicleService).should().updateVehiclePaused(vehicle.getReference(), false);
}
@ParameterizedTest
@ValueSource(strings = {"true ", "false"})
void throwOnPauseUnknownVehicle(String value) {
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(() -> handler.putVehiclePaused("some-unknown-vehicle", value));
}
@Test
void setVehicleEnvelopeKey() {
handler.putVehicleEnvelopeKey("some-vehicle", "some-key");
then(vehicleService).should().updateVehicleEnvelopeKey(vehicle.getReference(), "some-key");
}
@Test
void nullVehicleEnvelopeKey() {
handler.putVehicleEnvelopeKey("some-vehicle", null);
then(vehicleService).should().updateVehicleEnvelopeKey(vehicle.getReference(), null);
}
@Test
void throwOnSetEnvelopeUnknownVehicle() {
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(() -> handler.putVehicleEnvelopeKey("some-unknown-vehicle", "some-key"));
}
@Test
void updateVehicleAllowedOrderTypes() {
// Act
handler.putVehicleAllowedOrderTypes(
"some-vehicle",
new PutVehicleAllowedOrderTypesTO(List.of("some-order-type", "some-other-order-type"))
);
// Assert
@SuppressWarnings("unchecked")
ArgumentCaptor<Set<String>> captor = ArgumentCaptor.forClass(Set.class);
then(vehicleService)
.should()
.updateVehicleAllowedOrderTypes(eq(vehicle.getReference()), captor.capture());
assertThat(captor.getValue())
.hasSize(2)
.contains("some-order-type", "some-other-order-type");
}
@Test
void throwOnUpdateAllowedOrderTypesForUnknownVehicle() {
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(
() -> handler.putVehicleAllowedOrderTypes(
"some-unknown-vehicle",
new PutVehicleAllowedOrderTypesTO(List.of())
)
);
}
@Test
void retrieveVehicleRoutesForCurrentPosition() {
// Arrange
Point vehiclePosition = new Point("some-point");
Point destinationPoint1 = new Point("some-destination-point");
Point destinationPoint2 = new Point("some-destination-point-2");
Vehicle vehicleWithPosition = vehicle.withCurrentPosition(vehiclePosition.getReference());
given(vehicleService.fetchObject(Point.class, "some-point"))
.willReturn(vehiclePosition);
given(vehicleService.fetchObject(Point.class, "some-destination-point"))
.willReturn(destinationPoint1);
given(vehicleService.fetchObject(Point.class, "some-destination-point-2"))
.willReturn(destinationPoint2);
given(vehicleService.fetchObject(Vehicle.class, "some-vehicle"))
.willReturn(vehicleWithPosition);
// Act & Assert: happy path
handler.getVehicleRoutes(
"some-vehicle",
new PostVehicleRoutesRequestTO(
List.of("some-destination-point", "some-destination-point-2")
)
);
then(routerService)
.should()
.computeRoutes(
vehicle.getReference(),
vehiclePosition.getReference(),
Set.of(destinationPoint1.getReference(), destinationPoint2.getReference()),
Set.of()
);
// Act & Assert: nonexistent vehicle
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(
() -> handler.getVehicleRoutes(
"some-unknown-vehicle",
new PostVehicleRoutesRequestTO(List.of("some-destination-point"))
)
);
// Act & Assert: nonexistent destination point
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(
() -> handler.getVehicleRoutes(
"some-vehicle",
new PostVehicleRoutesRequestTO(List.of("some-unknown-destination-point"))
)
);
// Act & Assert: unknown vehicle position
given(vehicleService.fetchObject(Vehicle.class, "some-vehicle"))
.willReturn(vehicle);
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(
() -> handler.getVehicleRoutes(
"some-vehicle",
new PostVehicleRoutesRequestTO(List.of("some-destination-point"))
)
);
}
@Test
void retrieveVehicleRoutesForPositionProvidedInRequest() {
// Arrange
Point sourcePoint = new Point("some-source-point");
Point destinationPoint1 = new Point("some-destination-point");
Point destinationPoint2 = new Point("some-destination-point-2");
given(vehicleService.fetchObject(Point.class, "some-source-point"))
.willReturn(sourcePoint);
given(vehicleService.fetchObject(Point.class, "some-destination-point"))
.willReturn(destinationPoint1);
given(vehicleService.fetchObject(Point.class, "some-destination-point-2"))
.willReturn(destinationPoint2);
// Act & Assert: happy path
handler.getVehicleRoutes(
"some-vehicle",
new PostVehicleRoutesRequestTO(
List.of("some-destination-point", "some-destination-point-2")
).setSourcePoint("some-source-point")
);
then(routerService)
.should()
.computeRoutes(
vehicle.getReference(),
sourcePoint.getReference(),
Set.of(destinationPoint1.getReference(), destinationPoint2.getReference()),
Set.of()
);
// Act & Assert: nonexistent source point
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(
() -> handler.getVehicleRoutes(
"some-vehicle",
new PostVehicleRoutesRequestTO(List.of("some-destination-point"))
.setSourcePoint("some-unknown-source-point")
)
);
}
@Test
void retrieveVehicleRoutesForResourcesToAvoid() {
// Arrange
Point sourcePoint = new Point("some-source-point");
Point destinationPoint1 = new Point("some-destination-point");
Point destinationPoint2 = new Point("some-destination-point-2");
Point pointToAvoid = new Point("some-point-to-avoid");
Path pathToAvoid = new Path(
"some-path",
sourcePoint.getReference(),
destinationPoint1.getReference()
);
Location locationToAvoid = new Location(
"some-location",
new LocationType("some-locType").getReference()
);
given(vehicleService.fetchObject(Point.class, "some-source-point"))
.willReturn(sourcePoint);
given(vehicleService.fetchObject(Point.class, "some-destination-point"))
.willReturn(destinationPoint1);
given(vehicleService.fetchObject(Point.class, "some-destination-point-2"))
.willReturn(destinationPoint2);
given(vehicleService.fetchObject(Point.class, "some-point-to-avoid"))
.willReturn(pointToAvoid);
given(vehicleService.fetchObject(Path.class, "some-path"))
.willReturn(pathToAvoid);
given(vehicleService.fetchObject(Location.class, "some-location"))
.willReturn(locationToAvoid);
// Act & Assert: happy path
handler.getVehicleRoutes(
"some-vehicle",
new PostVehicleRoutesRequestTO(
List.of("some-destination-point", "some-destination-point-2")
)
.setSourcePoint("some-source-point")
.setResourcesToAvoid(
List.of("some-point-to-avoid", "some-path", "some-location")
)
);
then(routerService)
.should()
.computeRoutes(
vehicle.getReference(),
sourcePoint.getReference(),
Set.of(destinationPoint1.getReference(), destinationPoint2.getReference()),
Set.of(
pointToAvoid.getReference(),
pathToAvoid.getReference(),
locationToAvoid.getReference()
)
);
// Act & Assert: nonexistent resource to avoid
assertThatExceptionOfType(ObjectUnknownException.class)
.isThrownBy(
() -> handler.getVehicleRoutes(
"some-vehicle",
new PostVehicleRoutesRequestTO(List.of("some-destination-point"))
.setSourcePoint("some-source-point")
.setResourcesToAvoid(List.of("some-unknown-resource"))
)
);
}
static class MockVehicleCommAdapterDescription
extends
VehicleCommAdapterDescription {
@Override
public String getDescription() {
return "some-vehicle-comm-adapter";
}
@Override
public boolean isSimVehicleCommAdapter() {
return false;
}
}
}

View File

@@ -0,0 +1,133 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding;
import java.time.Instant;
import java.util.List;
import org.approvaltests.Approvals;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.opentcs.data.model.Vehicle;
import org.opentcs.data.peripherals.PeripheralJob;
import org.opentcs.data.peripherals.PeripheralOperation;
import org.opentcs.kernel.extensions.servicewebapi.JsonBinder;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.getevents.OrderStatusMessage;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.getevents.PeripheralJobStatusMessage;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.getevents.VehicleStatusMessage;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.DestinationState;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.PeripheralOperationDescription;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.Property;
/**
* Unit tests for {@link GetEventsResponseTO}.
*/
class GetEventsResponseTOTest {
private JsonBinder jsonBinder;
@BeforeEach
void setUp() {
jsonBinder = new JsonBinder();
}
@ParameterizedTest
@ValueSource(doubles = {Double.NaN, 90.0})
void jsonSample(double orientationAngle) {
GetEventsResponseTO to
= new GetEventsResponseTO()
.setTimeStamp(Instant.EPOCH)
.setStatusMessages(
List.of(
createVehicleStatusMessage(0).setOrientationAngle(orientationAngle),
createOrderStatusMessage(1),
createPeripheralJobStatusMessage(2)
)
);
Approvals.verify(
jsonBinder.toJson(to),
Approvals.NAMES.withParameters("orientationAngle-" + orientationAngle)
);
}
private VehicleStatusMessage createVehicleStatusMessage(long sequenceNo) {
return new VehicleStatusMessage()
.setSequenceNumber(sequenceNo)
.setCreationTimeStamp(Instant.EPOCH)
.setVehicleName("some-vehicle")
.setTransportOrderName("some-transport-order")
.setPosition("some-point")
.setPrecisePosition(new VehicleStatusMessage.PrecisePosition(1, 2, 3))
.setPaused(false)
.setState(Vehicle.State.IDLE)
.setProcState(Vehicle.ProcState.IDLE)
.setAllocatedResources(
List.of(
List.of("some-path", "some-point"),
List.of("some-other-path", "some-other-point")
)
)
.setClaimedResources(
List.of(
List.of("some-path", "some-point"),
List.of("some-other-path", "some-other-point")
)
);
}
private OrderStatusMessage createOrderStatusMessage(long sequenceNo) {
return new OrderStatusMessage()
.setSequenceNumber(sequenceNo)
.setCreationTimeStamp(Instant.EPOCH)
.setOrderName("some-order")
.setProcessingVehicleName("some-vehicle")
.setOrderState(OrderStatusMessage.OrderState.BEING_PROCESSED)
.setDestinations(
List.of(
new DestinationState()
.setLocationName("some-location")
.setOperation("some-operation")
.setState(DestinationState.State.TRAVELLING)
.setProperties(
List.of(
new Property("some-key", "some-value"),
new Property("some-other-key", "some-other-value")
)
)
)
)
.setProperties(
List.of(
new Property("some-key", "some-value"),
new Property("some-other-key", "some-other-value")
)
);
}
private PeripheralJobStatusMessage createPeripheralJobStatusMessage(long sequenceNo) {
return new PeripheralJobStatusMessage()
.setSequenceNumber(sequenceNo)
.setCreationTimeStamp(Instant.EPOCH)
.setName("some-peripheral-job")
.setReservationToken("some-token")
.setRelatedVehicle("some-vehicle")
.setRelatedTransportOrder("some-order")
.setPeripheralOperation(
new PeripheralOperationDescription()
.setOperation("some-operation")
.setLocationName("some-location")
.setExecutionTrigger(PeripheralOperation.ExecutionTrigger.AFTER_ALLOCATION)
.setCompletionRequired(true)
)
.setState(PeripheralJob.State.BEING_PROCESSED)
.setCreationTime(Instant.EPOCH)
.setFinishedTime(Instant.MAX)
.setProperties(
List.of(
new Property("some-key", "some-value"),
new Property("some-other-key", "some-other-value")
)
);
}
}

View File

@@ -0,0 +1,72 @@
{
"timeStamp" : "1970-01-01T00:00:00Z",
"statusMessages" : [ {
"type" : "Vehicle",
"sequenceNumber" : 0,
"creationTimeStamp" : "1970-01-01T00:00:00Z",
"vehicleName" : "some-vehicle",
"transportOrderName" : "some-transport-order",
"position" : "some-point",
"precisePosition" : {
"x" : 1,
"y" : 2,
"z" : 3
},
"orientationAngle" : 90.0,
"paused" : false,
"state" : "IDLE",
"procState" : "IDLE",
"allocatedResources" : [ [ "some-path", "some-point" ], [ "some-other-path", "some-other-point" ] ],
"claimedResources" : [ [ "some-path", "some-point" ], [ "some-other-path", "some-other-point" ] ]
}, {
"type" : "TransportOrder",
"sequenceNumber" : 1,
"creationTimeStamp" : "1970-01-01T00:00:00Z",
"orderName" : "some-order",
"processingVehicleName" : "some-vehicle",
"orderState" : "BEING_PROCESSED",
"destinations" : [ {
"locationName" : "some-location",
"operation" : "some-operation",
"state" : "TRAVELLING",
"properties" : [ {
"key" : "some-key",
"value" : "some-value"
}, {
"key" : "some-other-key",
"value" : "some-other-value"
} ]
} ],
"properties" : [ {
"key" : "some-key",
"value" : "some-value"
}, {
"key" : "some-other-key",
"value" : "some-other-value"
} ]
}, {
"type" : "PeripheralJob",
"sequenceNumber" : 2,
"creationTimeStamp" : "1970-01-01T00:00:00Z",
"name" : "some-peripheral-job",
"reservationToken" : "some-token",
"relatedVehicle" : "some-vehicle",
"relatedTransportOrder" : "some-order",
"peripheralOperation" : {
"operation" : "some-operation",
"locationName" : "some-location",
"executionTrigger" : "AFTER_ALLOCATION",
"completionRequired" : true
},
"state" : "BEING_PROCESSED",
"creationTime" : "1970-01-01T00:00:00Z",
"finishedTime" : "+1000000000-12-31T23:59:59.999999999Z",
"properties" : [ {
"key" : "some-key",
"value" : "some-value"
}, {
"key" : "some-other-key",
"value" : "some-other-value"
} ]
} ]
}

View File

@@ -0,0 +1,72 @@
{
"timeStamp" : "1970-01-01T00:00:00Z",
"statusMessages" : [ {
"type" : "Vehicle",
"sequenceNumber" : 0,
"creationTimeStamp" : "1970-01-01T00:00:00Z",
"vehicleName" : "some-vehicle",
"transportOrderName" : "some-transport-order",
"position" : "some-point",
"precisePosition" : {
"x" : 1,
"y" : 2,
"z" : 3
},
"orientationAngle" : "NaN",
"paused" : false,
"state" : "IDLE",
"procState" : "IDLE",
"allocatedResources" : [ [ "some-path", "some-point" ], [ "some-other-path", "some-other-point" ] ],
"claimedResources" : [ [ "some-path", "some-point" ], [ "some-other-path", "some-other-point" ] ]
}, {
"type" : "TransportOrder",
"sequenceNumber" : 1,
"creationTimeStamp" : "1970-01-01T00:00:00Z",
"orderName" : "some-order",
"processingVehicleName" : "some-vehicle",
"orderState" : "BEING_PROCESSED",
"destinations" : [ {
"locationName" : "some-location",
"operation" : "some-operation",
"state" : "TRAVELLING",
"properties" : [ {
"key" : "some-key",
"value" : "some-value"
}, {
"key" : "some-other-key",
"value" : "some-other-value"
} ]
} ],
"properties" : [ {
"key" : "some-key",
"value" : "some-value"
}, {
"key" : "some-other-key",
"value" : "some-other-value"
} ]
}, {
"type" : "PeripheralJob",
"sequenceNumber" : 2,
"creationTimeStamp" : "1970-01-01T00:00:00Z",
"name" : "some-peripheral-job",
"reservationToken" : "some-token",
"relatedVehicle" : "some-vehicle",
"relatedTransportOrder" : "some-order",
"peripheralOperation" : {
"operation" : "some-operation",
"locationName" : "some-location",
"executionTrigger" : "AFTER_ALLOCATION",
"completionRequired" : true
},
"state" : "BEING_PROCESSED",
"creationTime" : "1970-01-01T00:00:00Z",
"finishedTime" : "+1000000000-12-31T23:59:59.999999999Z",
"properties" : [ {
"key" : "some-key",
"value" : "some-value"
}, {
"key" : "some-other-key",
"value" : "some-other-value"
} ]
} ]
}

View File

@@ -0,0 +1,43 @@
// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.kernel.extensions.servicewebapi.v1.binding;
import java.util.List;
import org.approvaltests.Approvals;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.opentcs.kernel.extensions.servicewebapi.JsonBinder;
import org.opentcs.kernel.extensions.servicewebapi.v1.binding.shared.Property;
/**
*/
class GetOrderSequenceResponseTOTest {
private JsonBinder jsonBinder;
@BeforeEach
void setUp() {
jsonBinder = new JsonBinder();
}
@Test
void jsonSample() {
GetOrderSequenceResponseTO to = new GetOrderSequenceResponseTO("some-order-sequence")
.setType("Charge")
.setOrders(List.of("some-order", "another-order", "order-3"))
.setFinishedIndex(3)
.setComplete(false)
.setFinished(false)
.setFailureFatal(true)
.setIntendedVehicle("some-vehicle")
.setProcessingVehicle(null)
.setProperties(
List.of(
new Property("some-key", "some-value"),
new Property("another-key", "another-value")
)
);
Approvals.verify(jsonBinder.toJson(to));
}
}

View File

@@ -0,0 +1,18 @@
{
"name" : "some-order-sequence",
"type" : "Charge",
"orders" : [ "some-order", "another-order", "order-3" ],
"finishedIndex" : 3,
"complete" : false,
"finished" : false,
"failureFatal" : true,
"intendedVehicle" : "some-vehicle",
"processingVehicle" : null,
"properties" : [ {
"key" : "some-key",
"value" : "some-value"
}, {
"key" : "another-key",
"value" : "another-value"
} ]
}

Some files were not shown because too many files have changed in this diff Show More