This commit is contained in:
BIN
opentcs-kernelcontrolcenter/src/dist/bin/splash-image.gif
vendored
Normal file
BIN
opentcs-kernelcontrolcenter/src/dist/bin/splash-image.gif
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
2
opentcs-kernelcontrolcenter/src/dist/bin/splash-image.gif.license
vendored
Normal file
2
opentcs-kernelcontrolcenter/src/dist/bin/splash-image.gif.license
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# SPDX-FileCopyrightText: The openTCS Authors
|
||||
# SPDX-License-Identifier: CC-BY-4.0
|
||||
83
opentcs-kernelcontrolcenter/src/dist/config/logging.config
vendored
Normal file
83
opentcs-kernelcontrolcenter/src/dist/config/logging.config
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
# SPDX-FileCopyrightText: The openTCS Authors
|
||||
# SPDX-License-Identifier: CC-BY-4.0
|
||||
|
||||
############################################################
|
||||
# Default Logging Configuration File
|
||||
#
|
||||
# You can use a different file by specifying a filename
|
||||
# with the java.util.logging.config.file system property.
|
||||
# For example java -Djava.util.logging.config.file=myfile
|
||||
############################################################
|
||||
|
||||
############################################################
|
||||
# Global properties
|
||||
############################################################
|
||||
|
||||
# "handlers" specifies a comma separated list of log Handler
|
||||
# classes. These handlers will be installed during VM startup.
|
||||
# Note that these classes must be on the system classpath.
|
||||
# By default we only configure a ConsoleHandler, which will only
|
||||
# show messages at the INFO and above levels.
|
||||
#handlers= java.util.logging.ConsoleHandler
|
||||
|
||||
# To also add the FileHandler, use the following line instead.
|
||||
handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
|
||||
|
||||
# Default global logging level.
|
||||
# This specifies which kinds of events are logged across
|
||||
# all loggers. For any given facility this global level
|
||||
# can be overriden by a facility specific level
|
||||
# Note that the ConsoleHandler also has a separate level
|
||||
# setting to limit messages printed to the console.
|
||||
.level= INFO
|
||||
|
||||
############################################################
|
||||
# Handler specific properties.
|
||||
# Describes specific configuration info for Handlers.
|
||||
############################################################
|
||||
|
||||
# default file output is in user's home directory.
|
||||
java.util.logging.FileHandler.pattern = ./log/opentcs-kernelcontrolcenter.%g.log
|
||||
java.util.logging.FileHandler.limit = 500000
|
||||
java.util.logging.FileHandler.count = 10
|
||||
#java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
|
||||
java.util.logging.FileHandler.formatter = org.opentcs.util.logging.SingleLineFormatter
|
||||
java.util.logging.FileHandler.append = true
|
||||
java.util.logging.FileHandler.level = FINE
|
||||
|
||||
# Limit the message that are printed on the console to INFO and above.
|
||||
java.util.logging.ConsoleHandler.level = FINE
|
||||
#java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
|
||||
java.util.logging.ConsoleHandler.formatter = org.opentcs.util.logging.SingleLineFormatter
|
||||
|
||||
# Our own handler for the GUI:
|
||||
#org.opentcs.kernel.controlcenter.ControlCenterInfoHandler.level = WARNING
|
||||
|
||||
|
||||
############################################################
|
||||
# Facility specific properties.
|
||||
# Provides extra control for each logger.
|
||||
############################################################
|
||||
|
||||
# For example, set the com.xyz.foo logger to only log SEVERE
|
||||
# messages:
|
||||
#com.xyz.foo.level = SEVERE
|
||||
|
||||
# Logging configuration for single classes. Remember that you might also have to
|
||||
# adjust handler levels!
|
||||
|
||||
#org.opentcs.kernel.vehicles.StandardVehicleController.level = FINE
|
||||
#org.opentcs.strategies.basic.dispatching.DefaultDispatcher.level = FINE
|
||||
org.opentcs.kernel.util.RegistryProvider.level = FINE
|
||||
org.opentcs.kernel.services.StandardRemoteKernelClientPortal.level = FINE
|
||||
org.opentcs.kernel.services.StandardRemotePlantModelService.level = FINE
|
||||
org.opentcs.kernel.services.StandardRemoteTransportOrderService.level = FINE
|
||||
org.opentcs.kernel.services.StandardRemoteVehicleService.level = FINE
|
||||
org.opentcs.kernel.services.StandardRemoteNotificationService.level = FINE
|
||||
org.opentcs.kernel.services.StandardRemoteDispatcherService.level = FINE
|
||||
org.opentcs.kernel.services.StandardRemoteRouterService.level = FINE
|
||||
org.opentcs.kernel.services.StandardRemoteSchedulerService.level = FINE
|
||||
org.opentcs.kernel.StandardRemoteKernel.level = FINE
|
||||
org.opentcs.kernel.UserManager.level = FINE
|
||||
org.opentcs.kernelcontrolcenter.KernelControlCenter.level = FINE
|
||||
#org.opentcs.access.services.DefaultNotificationService.level = FINE
|
||||
2
opentcs-kernelcontrolcenter/src/dist/config/opentcs-kernelcontrolcenter.properties
vendored
Normal file
2
opentcs-kernelcontrolcenter/src/dist/config/opentcs-kernelcontrolcenter.properties
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# SPDX-FileCopyrightText: The openTCS Authors
|
||||
# SPDX-License-Identifier: CC-BY-4.0
|
||||
0
opentcs-kernelcontrolcenter/src/dist/lib/openTCS-extensions/.keepme
vendored
Normal file
0
opentcs-kernelcontrolcenter/src/dist/lib/openTCS-extensions/.keepme
vendored
Normal file
0
opentcs-kernelcontrolcenter/src/dist/log/statistics/.keepme
vendored
Normal file
0
opentcs-kernelcontrolcenter/src/dist/log/statistics/.keepme
vendored
Normal file
37
opentcs-kernelcontrolcenter/src/dist/startKernelControlCenter.bat
vendored
Normal file
37
opentcs-kernelcontrolcenter/src/dist/startKernelControlCenter.bat
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
@echo off
|
||||
rem SPDX-FileCopyrightText: The openTCS Authors
|
||||
rem SPDX-License-Identifier: MIT
|
||||
rem
|
||||
rem Start the openTCS kernel control center.
|
||||
rem
|
||||
|
||||
rem Set window title
|
||||
title KernelControlCenter (openTCS)
|
||||
|
||||
rem Don't export variables to the parent shell
|
||||
setlocal
|
||||
|
||||
rem Set base directory names.
|
||||
set OPENTCS_BASE=.
|
||||
set OPENTCS_HOME=.
|
||||
set OPENTCS_CONFIGDIR=%OPENTCS_HOME%\config
|
||||
set OPENTCS_LIBDIR=%OPENTCS_BASE%\lib
|
||||
|
||||
rem Set the class path
|
||||
set OPENTCS_CP=%OPENTCS_LIBDIR%\*;
|
||||
set OPENTCS_CP=%OPENTCS_CP%;%OPENTCS_LIBDIR%\openTCS-extensions\*;
|
||||
|
||||
rem XXX Be a bit more clever to find out the name of the JVM runtime.
|
||||
set JAVA=javaw
|
||||
|
||||
rem Start kernel control center
|
||||
start /b %JAVA% -enableassertions ^
|
||||
-Dopentcs.base="%OPENTCS_BASE%" ^
|
||||
-Dopentcs.home="%OPENTCS_HOME%" ^
|
||||
-Dopentcs.configuration.provider=gestalt ^
|
||||
-Dopentcs.configuration.reload.interval=10000 ^
|
||||
-Djava.util.logging.config.file="%OPENTCS_CONFIGDIR%\logging.config" ^
|
||||
-XX:-OmitStackTraceInFastThrow ^
|
||||
-classpath "%OPENTCS_CP%" ^
|
||||
-splash:bin/splash-image.gif ^
|
||||
org.opentcs.kernelcontrolcenter.RunKernelControlCenter
|
||||
35
opentcs-kernelcontrolcenter/src/dist/startKernelControlCenter.sh
vendored
Normal file
35
opentcs-kernelcontrolcenter/src/dist/startKernelControlCenter.sh
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
#!/bin/sh
|
||||
# SPDX-FileCopyrightText: The openTCS Authors
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Start the openTCS kernel control center.
|
||||
#
|
||||
|
||||
# Set base directory names.
|
||||
export OPENTCS_BASE=.
|
||||
export OPENTCS_HOME=.
|
||||
export OPENTCS_CONFIGDIR="${OPENTCS_HOME}/config"
|
||||
export OPENTCS_LIBDIR="${OPENTCS_BASE}/lib"
|
||||
|
||||
# Set the class path
|
||||
export OPENTCS_CP="${OPENTCS_LIBDIR}/*"
|
||||
export OPENTCS_CP="${OPENTCS_CP}:${OPENTCS_LIBDIR}/openTCS-extensions/*"
|
||||
|
||||
if [ -n "${OPENTCS_JAVAVM}" ]; then
|
||||
export JAVA="${OPENTCS_JAVAVM}"
|
||||
else
|
||||
# XXX Be a bit more clever to find out the name of the JVM runtime.
|
||||
export JAVA="java"
|
||||
fi
|
||||
|
||||
# Start kernel control center
|
||||
${JAVA} -enableassertions \
|
||||
-Dopentcs.base="${OPENTCS_BASE}" \
|
||||
-Dopentcs.home="${OPENTCS_HOME}" \
|
||||
-Dopentcs.configuration.provider=gestalt \
|
||||
-Dopentcs.configuration.reload.interval=10000 \
|
||||
-Djava.util.logging.config.file=${OPENTCS_CONFIGDIR}/logging.config \
|
||||
-XX:-OmitStackTraceInFastThrow \
|
||||
-classpath "${OPENTCS_CP}" \
|
||||
-splash:bin/splash-image.gif \
|
||||
org.opentcs.kernelcontrolcenter.RunKernelControlCenter
|
||||
@@ -0,0 +1,57 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter;
|
||||
|
||||
import com.google.inject.assistedinject.FactoryModuleBuilder;
|
||||
import com.google.inject.multibindings.Multibinder;
|
||||
import jakarta.inject.Singleton;
|
||||
import org.opentcs.components.kernelcontrolcenter.ControlCenterPanel;
|
||||
import org.opentcs.customizations.controlcenter.ControlCenterInjectionModule;
|
||||
import org.opentcs.kernelcontrolcenter.peripherals.PeripheralsPanel;
|
||||
import org.opentcs.kernelcontrolcenter.util.KernelControlCenterConfiguration;
|
||||
import org.opentcs.kernelcontrolcenter.vehicles.DriverGUI;
|
||||
|
||||
/**
|
||||
* Configures the default extensions of the openTCS kernel control center application.
|
||||
*/
|
||||
public class DefaultKernelControlCenterExtensionsModule
|
||||
extends
|
||||
ControlCenterInjectionModule {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public DefaultKernelControlCenterExtensionsModule() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
configureControlCenterDependencies();
|
||||
}
|
||||
|
||||
private void configureControlCenterDependencies() {
|
||||
KernelControlCenterConfiguration configuration
|
||||
= getConfigBindingProvider().get(
|
||||
KernelControlCenterConfiguration.PREFIX,
|
||||
KernelControlCenterConfiguration.class
|
||||
);
|
||||
bind(KernelControlCenterConfiguration.class).toInstance(configuration);
|
||||
|
||||
// Ensure these binders are initialized.
|
||||
commAdapterPanelFactoryBinder();
|
||||
peripheralCommAdapterPanelFactoryBinder();
|
||||
|
||||
Multibinder<ControlCenterPanel> modellingBinder = controlCenterPanelBinderModelling();
|
||||
// No extensions for modelling mode, yet.
|
||||
|
||||
Multibinder<ControlCenterPanel> operatingBinder = controlCenterPanelBinderOperating();
|
||||
operatingBinder.addBinding().to(DriverGUI.class);
|
||||
if (configuration.enablePeripheralsPanel()) {
|
||||
operatingBinder.addBinding().to(PeripheralsPanel.class);
|
||||
}
|
||||
|
||||
install(new FactoryModuleBuilder().build(ControlCenterInfoHandlerFactory.class));
|
||||
|
||||
bind(KernelControlCenter.class).in(Singleton.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter;
|
||||
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.assistedinject.FactoryModuleBuilder;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import javax.swing.ToolTipManager;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.UnsupportedLookAndFeelException;
|
||||
import org.opentcs.access.KernelServicePortal;
|
||||
import org.opentcs.access.SslParameterSet;
|
||||
import org.opentcs.access.rmi.KernelServicePortalBuilder;
|
||||
import org.opentcs.access.rmi.factories.NullSocketFactoryProvider;
|
||||
import org.opentcs.access.rmi.factories.SecureSocketFactoryProvider;
|
||||
import org.opentcs.access.rmi.factories.SocketFactoryProvider;
|
||||
import org.opentcs.common.DefaultPortalManager;
|
||||
import org.opentcs.common.GuestUserCredentials;
|
||||
import org.opentcs.common.KernelClientApplication;
|
||||
import org.opentcs.common.PortalManager;
|
||||
import org.opentcs.customizations.ApplicationEventBus;
|
||||
import org.opentcs.customizations.ApplicationHome;
|
||||
import org.opentcs.customizations.ConfigurableInjectionModule;
|
||||
import org.opentcs.customizations.ServiceCallWrapper;
|
||||
import org.opentcs.kernelcontrolcenter.exchange.DefaultServiceCallWrapper;
|
||||
import org.opentcs.kernelcontrolcenter.exchange.SslConfiguration;
|
||||
import org.opentcs.kernelcontrolcenter.util.KernelControlCenterConfiguration;
|
||||
import org.opentcs.kernelcontrolcenter.vehicles.LocalVehicleEntryPool;
|
||||
import org.opentcs.util.CallWrapper;
|
||||
import org.opentcs.util.event.EventBus;
|
||||
import org.opentcs.util.event.EventHandler;
|
||||
import org.opentcs.util.event.EventSource;
|
||||
import org.opentcs.util.event.SimpleEventBus;
|
||||
import org.opentcs.util.gui.dialog.ConnectionParamSet;
|
||||
import org.opentcs.virtualvehicle.AdapterPanelComponentsFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A Guice module for the openTCS kernel control center application.
|
||||
*/
|
||||
public class DefaultKernelControlCenterInjectionModule
|
||||
extends
|
||||
ConfigurableInjectionModule {
|
||||
|
||||
/**
|
||||
* This class' logger.
|
||||
*/
|
||||
private static final Logger LOG
|
||||
= LoggerFactory.getLogger(DefaultKernelControlCenterInjectionModule.class);
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public DefaultKernelControlCenterInjectionModule() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
File applicationHome = new File(System.getProperty("opentcs.home", "."));
|
||||
bind(File.class)
|
||||
.annotatedWith(ApplicationHome.class)
|
||||
.toInstance(applicationHome);
|
||||
|
||||
bind(LocalVehicleEntryPool.class)
|
||||
.in(Singleton.class);
|
||||
|
||||
bind(KernelClientApplication.class)
|
||||
.to(KernelControlCenterApplication.class)
|
||||
.in(Singleton.class);
|
||||
|
||||
install(new FactoryModuleBuilder().build(AdapterPanelComponentsFactory.class));
|
||||
|
||||
configureEventBus();
|
||||
configureKernelControlCenterDependencies();
|
||||
configureExchangeInjectionModules();
|
||||
}
|
||||
|
||||
private void configureEventBus() {
|
||||
EventBus newEventBus = new SimpleEventBus();
|
||||
bind(EventHandler.class)
|
||||
.annotatedWith(ApplicationEventBus.class)
|
||||
.toInstance(newEventBus);
|
||||
bind(EventSource.class)
|
||||
.annotatedWith(ApplicationEventBus.class)
|
||||
.toInstance(newEventBus);
|
||||
bind(EventBus.class)
|
||||
.annotatedWith(ApplicationEventBus.class)
|
||||
.toInstance(newEventBus);
|
||||
}
|
||||
|
||||
private void configureExchangeInjectionModules() {
|
||||
bind(PortalManager.class)
|
||||
.to(DefaultPortalManager.class)
|
||||
.in(Singleton.class);
|
||||
}
|
||||
|
||||
private void configureKernelControlCenterDependencies() {
|
||||
KernelControlCenterConfiguration configuration
|
||||
= getConfigBindingProvider().get(
|
||||
KernelControlCenterConfiguration.PREFIX,
|
||||
KernelControlCenterConfiguration.class
|
||||
);
|
||||
bind(KernelControlCenterConfiguration.class)
|
||||
.toInstance(configuration);
|
||||
configureKernelControlCenter(configuration);
|
||||
configureSocketConnections();
|
||||
|
||||
bind(CallWrapper.class)
|
||||
.annotatedWith(ServiceCallWrapper.class)
|
||||
.to(DefaultServiceCallWrapper.class)
|
||||
.in(Singleton.class);
|
||||
|
||||
bind(new TypeLiteral<List<ConnectionParamSet>>() {
|
||||
})
|
||||
.toInstance(configuration.connectionBookmarks());
|
||||
}
|
||||
|
||||
private void configureSocketConnections() {
|
||||
SslConfiguration sslConfiguration = getConfigBindingProvider().get(
|
||||
SslConfiguration.PREFIX,
|
||||
SslConfiguration.class
|
||||
);
|
||||
|
||||
//Create the data object for the ssl configuration
|
||||
SslParameterSet sslParamSet = new SslParameterSet(
|
||||
SslParameterSet.DEFAULT_KEYSTORE_TYPE,
|
||||
null,
|
||||
null,
|
||||
new File(sslConfiguration.truststoreFile()),
|
||||
sslConfiguration.truststorePassword()
|
||||
);
|
||||
bind(SslParameterSet.class).toInstance(sslParamSet);
|
||||
|
||||
SocketFactoryProvider socketFactoryProvider;
|
||||
if (sslConfiguration.enable()) {
|
||||
socketFactoryProvider = new SecureSocketFactoryProvider(sslParamSet);
|
||||
}
|
||||
else {
|
||||
LOG.warn("SSL encryption disabled, connections will not be secured!");
|
||||
socketFactoryProvider = new NullSocketFactoryProvider();
|
||||
}
|
||||
|
||||
//Bind socket provider to the kernel portal
|
||||
bind(KernelServicePortal.class)
|
||||
.toInstance(
|
||||
new KernelServicePortalBuilder(
|
||||
GuestUserCredentials.USER,
|
||||
GuestUserCredentials.PASSWORD
|
||||
)
|
||||
.setSocketFactoryProvider(socketFactoryProvider)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
private void configureKernelControlCenter(KernelControlCenterConfiguration configuration) {
|
||||
Locale.setDefault(Locale.forLanguageTag(configuration.locale()));
|
||||
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
}
|
||||
catch (ClassNotFoundException | InstantiationException | IllegalAccessException
|
||||
| UnsupportedLookAndFeelException ex) {
|
||||
LOG.warn("Could not set look-and-feel", ex);
|
||||
}
|
||||
// Show tooltips for 30 seconds (Default: 4 sec)
|
||||
ToolTipManager.sharedInstance().setDismissDelay(30 * 1000);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter;
|
||||
|
||||
import org.opentcs.customizations.controlcenter.ControlCenterInjectionModule;
|
||||
import org.opentcs.virtualvehicle.LoopbackCommAdapterPanelFactory;
|
||||
|
||||
/**
|
||||
* Registers the loopback adapter's panels.
|
||||
*/
|
||||
public class LoopbackCommAdapterPanelsModule
|
||||
extends
|
||||
ControlCenterInjectionModule {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public LoopbackCommAdapterPanelsModule() {
|
||||
}
|
||||
|
||||
// tag::documentation_createCommAdapterPanelsModule[]
|
||||
@Override
|
||||
protected void configure() {
|
||||
commAdapterPanelFactoryBinder().addBinding().to(LoopbackCommAdapterPanelFactory.class);
|
||||
}
|
||||
// end::documentation_createCommAdapterPanelsModule[]
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.util.Modules;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.ServiceLoader;
|
||||
import org.opentcs.configuration.ConfigurationBindingProvider;
|
||||
import org.opentcs.configuration.gestalt.GestaltConfigurationBindingProvider;
|
||||
import org.opentcs.customizations.ConfigurableInjectionModule;
|
||||
import org.opentcs.customizations.controlcenter.ControlCenterInjectionModule;
|
||||
import org.opentcs.util.Environment;
|
||||
import org.opentcs.util.logging.UncaughtExceptionLogger;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The kernel control center process's default entry point.
|
||||
*/
|
||||
public class RunKernelControlCenter {
|
||||
|
||||
/**
|
||||
* This class' logger.
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RunKernelControlCenter.class);
|
||||
|
||||
/**
|
||||
* Prevents external instantiation.
|
||||
*/
|
||||
private RunKernelControlCenter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* The kernel control center client's main entry point.
|
||||
*
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(final String[] args) {
|
||||
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionLogger(false));
|
||||
|
||||
Environment.logSystemInfo();
|
||||
|
||||
Injector injector = Guice.createInjector(customConfigurationModule());
|
||||
injector.getInstance(KernelControlCenterApplication.class).initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a Guice module containing the custom configuration for the kernel control
|
||||
* center application, including additions and overrides by the user.
|
||||
*
|
||||
* @return The custom configuration module.
|
||||
*/
|
||||
private static Module customConfigurationModule() {
|
||||
ConfigurationBindingProvider bindingProvider = configurationBindingProvider();
|
||||
ConfigurableInjectionModule kernelControlCenterInjectionModule
|
||||
= new DefaultKernelControlCenterInjectionModule();
|
||||
kernelControlCenterInjectionModule.setConfigBindingProvider(bindingProvider);
|
||||
return Modules.override(kernelControlCenterInjectionModule)
|
||||
.with(findRegisteredModules(bindingProvider));
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and returns all Guice modules registered via ServiceLoader.
|
||||
*
|
||||
* @return The registered/found modules.
|
||||
*/
|
||||
private static List<ControlCenterInjectionModule> findRegisteredModules(
|
||||
ConfigurationBindingProvider bindingProvider
|
||||
) {
|
||||
List<ControlCenterInjectionModule> registeredModules = new ArrayList<>();
|
||||
for (ControlCenterInjectionModule module : ServiceLoader.load(
|
||||
ControlCenterInjectionModule.class
|
||||
)) {
|
||||
LOG.info(
|
||||
"Integrating injection module {} (source: {})",
|
||||
module.getClass().getName(),
|
||||
module.getClass().getProtectionDomain().getCodeSource()
|
||||
);
|
||||
module.setConfigBindingProvider(bindingProvider);
|
||||
registeredModules.add(module);
|
||||
}
|
||||
return registeredModules;
|
||||
}
|
||||
|
||||
private static ConfigurationBindingProvider configurationBindingProvider() {
|
||||
String chosenProvider = System.getProperty("opentcs.configuration.provider", "gestalt");
|
||||
switch (chosenProvider) {
|
||||
case "gestalt":
|
||||
default:
|
||||
LOG.info("Using gestalt as the configuration provider.");
|
||||
return gestaltConfigurationBindingProvider();
|
||||
}
|
||||
}
|
||||
|
||||
private static ConfigurationBindingProvider gestaltConfigurationBindingProvider() {
|
||||
return new GestaltConfigurationBindingProvider(
|
||||
Paths.get(
|
||||
System.getProperty("opentcs.base", "."),
|
||||
"config",
|
||||
"opentcs-kernelcontrolcenter-defaults-baseline.properties"
|
||||
)
|
||||
.toAbsolutePath(),
|
||||
Paths.get(
|
||||
System.getProperty("opentcs.base", "."),
|
||||
"config",
|
||||
"opentcs-kernelcontrolcenter-defaults-custom.properties"
|
||||
)
|
||||
.toAbsolutePath(),
|
||||
Paths.get(
|
||||
System.getProperty("opentcs.home", "."),
|
||||
"config",
|
||||
"opentcs-kernelcontrolcenter.properties"
|
||||
)
|
||||
.toAbsolutePath()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# SPDX-FileCopyrightText: The openTCS Authors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
org.opentcs.kernelcontrolcenter.DefaultKernelControlCenterExtensionsModule
|
||||
org.opentcs.kernelcontrolcenter.LoopbackCommAdapterPanelsModule
|
||||
@@ -0,0 +1,266 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
|
||||
<Properties>
|
||||
<Property name="defaultCloseOperation" type="int" value="2"/>
|
||||
<Property name="title" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="aboutDialog.title" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
<Property name="resizable" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
<SyntheticProperties>
|
||||
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
|
||||
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
|
||||
</SyntheticProperties>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,79,0,0,1,58"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="logoPanel">
|
||||
<Properties>
|
||||
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="ff" green="ff" red="ff" type="rgb"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="2" ipadX="6" ipadY="6" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="opentcsLogoLbl">
|
||||
<Properties>
|
||||
<Property name="horizontalAlignment" type="int" value="0"/>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/opentcs/kernelcontrolcenter/res/logos/opentcs.gif"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="Center"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="contactPanel">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
|
||||
<Property name="axis" type="int" value="1"/>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="opentcsContactPanel">
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="opentcsLbl">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font bold="true" component="opentcsLbl" property="font" relativeSize="true" size="0"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" value="open Transportation Control System"/>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="2" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="6" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="17" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="versionLbl">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="aboutDialog.label_baselineVersion.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="versionTxtLbl">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="Environment.getBaselineVersion()" type="code"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="1" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="6" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="customVersionLbl">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="aboutDialog.label_customVersion.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="customVersionTxtLbl">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="Environment.getCustomizationName() + " " + Environment.getCustomizationVersion()" type="code"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="1" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="6" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="homepageLbl">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="aboutDialog.label_homepage.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="homepageTxtLbl">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="https://www.opentcs.org/"/>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="1" gridY="3" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="6" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="emailLbl">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="aboutDialog.label_email.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="4" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="emailTxtLbl">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="business-info@opentcs.org"/>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="1" gridY="4" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="6" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="imlPanel">
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="fraunhoferImlLbl">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font bold="true" component="fraunhoferImlLbl" property="font" relativeSize="true" size="0"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="aboutDialog.label_fraunhoferIml.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="0" gridWidth="2" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="6" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="17" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="homepageImlLbl">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="aboutDialog.label_homepage.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="homepageImlTxtLbl">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="http://www.iml.fraunhofer.de/"/>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="1" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="6" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JLabel" name="fillingLbl">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value=" "/>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="1.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="closeButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="aboutDialog.button_close.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="closeButtonActionPerformed"/>
|
||||
</Events>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="fillingLbl2">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value=" "/>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="4" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="1.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
@@ -0,0 +1,253 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter;
|
||||
|
||||
import java.awt.Frame;
|
||||
import javax.swing.JDialog;
|
||||
import org.opentcs.util.Environment;
|
||||
|
||||
/**
|
||||
* An about dialog.
|
||||
*/
|
||||
public class AboutDialog
|
||||
extends
|
||||
JDialog {
|
||||
|
||||
/**
|
||||
* Creates new AboutDialog.
|
||||
*
|
||||
* @param parent The parent frame.
|
||||
* @param modal Whether the dialog blocks user input to other top-level windows when shown.
|
||||
*/
|
||||
@SuppressWarnings("this-escape")
|
||||
public AboutDialog(Frame parent, boolean modal) {
|
||||
super(parent, modal);
|
||||
initComponents();
|
||||
}
|
||||
|
||||
// FORMATTER:OFF
|
||||
// CHECKSTYLE:OFF
|
||||
/**
|
||||
* This method is called from within the constructor to
|
||||
* initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is
|
||||
* always regenerated by the Form Editor.
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
java.awt.GridBagConstraints gridBagConstraints;
|
||||
|
||||
logoPanel = new javax.swing.JPanel();
|
||||
opentcsLogoLbl = new javax.swing.JLabel();
|
||||
contactPanel = new javax.swing.JPanel();
|
||||
opentcsContactPanel = new javax.swing.JPanel();
|
||||
opentcsLbl = new javax.swing.JLabel();
|
||||
versionLbl = new javax.swing.JLabel();
|
||||
versionTxtLbl = new javax.swing.JLabel();
|
||||
customVersionLbl = new javax.swing.JLabel();
|
||||
customVersionTxtLbl = new javax.swing.JLabel();
|
||||
homepageLbl = new javax.swing.JLabel();
|
||||
homepageTxtLbl = new javax.swing.JLabel();
|
||||
emailLbl = new javax.swing.JLabel();
|
||||
emailTxtLbl = new javax.swing.JLabel();
|
||||
imlPanel = new javax.swing.JPanel();
|
||||
fraunhoferImlLbl = new javax.swing.JLabel();
|
||||
homepageImlLbl = new javax.swing.JLabel();
|
||||
homepageImlTxtLbl = new javax.swing.JLabel();
|
||||
fillingLbl = new javax.swing.JLabel();
|
||||
closeButton = new javax.swing.JButton();
|
||||
fillingLbl2 = new javax.swing.JLabel();
|
||||
|
||||
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
|
||||
java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("i18n/org/opentcs/kernelcontrolcenter/Bundle"); // NOI18N
|
||||
setTitle(bundle.getString("aboutDialog.title")); // NOI18N
|
||||
setResizable(false);
|
||||
getContentPane().setLayout(new java.awt.GridBagLayout());
|
||||
|
||||
logoPanel.setBackground(new java.awt.Color(255, 255, 255));
|
||||
logoPanel.setLayout(new java.awt.BorderLayout());
|
||||
|
||||
opentcsLogoLbl.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
|
||||
opentcsLogoLbl.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/opentcs/kernelcontrolcenter/res/logos/opentcs.gif"))); // NOI18N
|
||||
logoPanel.add(opentcsLogoLbl, java.awt.BorderLayout.CENTER);
|
||||
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||
gridBagConstraints.ipadx = 6;
|
||||
gridBagConstraints.ipady = 6;
|
||||
getContentPane().add(logoPanel, gridBagConstraints);
|
||||
|
||||
contactPanel.setLayout(new javax.swing.BoxLayout(contactPanel, javax.swing.BoxLayout.Y_AXIS));
|
||||
|
||||
opentcsContactPanel.setLayout(new java.awt.GridBagLayout());
|
||||
|
||||
opentcsLbl.setFont(opentcsLbl.getFont().deriveFont(opentcsLbl.getFont().getStyle() | java.awt.Font.BOLD));
|
||||
opentcsLbl.setText("open Transportation Control System");
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridwidth = 2;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
|
||||
gridBagConstraints.insets = new java.awt.Insets(6, 0, 0, 0);
|
||||
opentcsContactPanel.add(opentcsLbl, gridBagConstraints);
|
||||
|
||||
versionLbl.setText(bundle.getString("aboutDialog.label_baselineVersion.text")); // NOI18N
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 1;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||
opentcsContactPanel.add(versionLbl, gridBagConstraints);
|
||||
|
||||
versionTxtLbl.setText(Environment.getBaselineVersion());
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 1;
|
||||
gridBagConstraints.gridy = 1;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||
gridBagConstraints.insets = new java.awt.Insets(0, 6, 0, 0);
|
||||
opentcsContactPanel.add(versionTxtLbl, gridBagConstraints);
|
||||
|
||||
customVersionLbl.setText(bundle.getString("aboutDialog.label_customVersion.text")); // NOI18N
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 2;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||
opentcsContactPanel.add(customVersionLbl, gridBagConstraints);
|
||||
|
||||
customVersionTxtLbl.setText(Environment.getCustomizationName() + " " + Environment.getCustomizationVersion());
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 1;
|
||||
gridBagConstraints.gridy = 2;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||
gridBagConstraints.insets = new java.awt.Insets(0, 6, 0, 0);
|
||||
opentcsContactPanel.add(customVersionTxtLbl, gridBagConstraints);
|
||||
|
||||
homepageLbl.setText(bundle.getString("aboutDialog.label_homepage.text")); // NOI18N
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 3;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||
opentcsContactPanel.add(homepageLbl, gridBagConstraints);
|
||||
|
||||
homepageTxtLbl.setText("https://www.opentcs.org/");
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 1;
|
||||
gridBagConstraints.gridy = 3;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||
gridBagConstraints.insets = new java.awt.Insets(0, 6, 0, 0);
|
||||
opentcsContactPanel.add(homepageTxtLbl, gridBagConstraints);
|
||||
|
||||
emailLbl.setText(bundle.getString("aboutDialog.label_email.text")); // NOI18N
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 4;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||
opentcsContactPanel.add(emailLbl, gridBagConstraints);
|
||||
|
||||
emailTxtLbl.setText("business-info@opentcs.org");
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 1;
|
||||
gridBagConstraints.gridy = 4;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||
gridBagConstraints.insets = new java.awt.Insets(0, 6, 0, 0);
|
||||
opentcsContactPanel.add(emailTxtLbl, gridBagConstraints);
|
||||
|
||||
contactPanel.add(opentcsContactPanel);
|
||||
|
||||
imlPanel.setLayout(new java.awt.GridBagLayout());
|
||||
|
||||
fraunhoferImlLbl.setFont(fraunhoferImlLbl.getFont().deriveFont(fraunhoferImlLbl.getFont().getStyle() | java.awt.Font.BOLD));
|
||||
fraunhoferImlLbl.setText(bundle.getString("aboutDialog.label_fraunhoferIml.text")); // NOI18N
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 0;
|
||||
gridBagConstraints.gridwidth = 2;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
|
||||
gridBagConstraints.insets = new java.awt.Insets(6, 0, 0, 0);
|
||||
imlPanel.add(fraunhoferImlLbl, gridBagConstraints);
|
||||
|
||||
homepageImlLbl.setText(bundle.getString("aboutDialog.label_homepage.text")); // NOI18N
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 1;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||
imlPanel.add(homepageImlLbl, gridBagConstraints);
|
||||
|
||||
homepageImlTxtLbl.setText("http://www.iml.fraunhofer.de/");
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 1;
|
||||
gridBagConstraints.gridy = 1;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||
gridBagConstraints.insets = new java.awt.Insets(0, 6, 0, 0);
|
||||
imlPanel.add(homepageImlTxtLbl, gridBagConstraints);
|
||||
|
||||
contactPanel.add(imlPanel);
|
||||
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 1;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
|
||||
getContentPane().add(contactPanel, gridBagConstraints);
|
||||
|
||||
fillingLbl.setText(" ");
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 2;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
|
||||
gridBagConstraints.weighty = 1.0;
|
||||
getContentPane().add(fillingLbl, gridBagConstraints);
|
||||
|
||||
closeButton.setText(bundle.getString("aboutDialog.button_close.text")); // NOI18N
|
||||
closeButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
closeButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 3;
|
||||
getContentPane().add(closeButton, gridBagConstraints);
|
||||
|
||||
fillingLbl2.setText(" ");
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 4;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||
gridBagConstraints.weighty = 1.0;
|
||||
getContentPane().add(fillingLbl2, gridBagConstraints);
|
||||
|
||||
pack();
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
// CHECKSTYLE:ON
|
||||
// FORMATTER:ON
|
||||
|
||||
private void closeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeButtonActionPerformed
|
||||
this.setVisible(false);
|
||||
}//GEN-LAST:event_closeButtonActionPerformed
|
||||
|
||||
// FORMATTER:OFF
|
||||
// CHECKSTYLE:OFF
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton closeButton;
|
||||
private javax.swing.JPanel contactPanel;
|
||||
private javax.swing.JLabel customVersionLbl;
|
||||
private javax.swing.JLabel customVersionTxtLbl;
|
||||
private javax.swing.JLabel emailLbl;
|
||||
private javax.swing.JLabel emailTxtLbl;
|
||||
private javax.swing.JLabel fillingLbl;
|
||||
private javax.swing.JLabel fillingLbl2;
|
||||
private javax.swing.JLabel fraunhoferImlLbl;
|
||||
private javax.swing.JLabel homepageImlLbl;
|
||||
private javax.swing.JLabel homepageImlTxtLbl;
|
||||
private javax.swing.JLabel homepageLbl;
|
||||
private javax.swing.JLabel homepageTxtLbl;
|
||||
private javax.swing.JPanel imlPanel;
|
||||
private javax.swing.JPanel logoPanel;
|
||||
private javax.swing.JPanel opentcsContactPanel;
|
||||
private javax.swing.JLabel opentcsLbl;
|
||||
private javax.swing.JLabel opentcsLogoLbl;
|
||||
private javax.swing.JLabel versionLbl;
|
||||
private javax.swing.JLabel versionTxtLbl;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
// CHECKSTYLE:ON
|
||||
// FORMATTER:ON
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
import jakarta.inject.Inject;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.FormatStyle;
|
||||
import java.util.Locale;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.DefaultCaret;
|
||||
import org.opentcs.access.NotificationPublicationEvent;
|
||||
import org.opentcs.common.ClientConnectionMode;
|
||||
import org.opentcs.common.PortalManager;
|
||||
import org.opentcs.data.notification.UserNotification;
|
||||
import org.opentcs.kernelcontrolcenter.util.KernelControlCenterConfiguration;
|
||||
import org.opentcs.util.event.EventHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A logging handler that writes all INFO-logs to KernelControlCenter's logging text area.
|
||||
*/
|
||||
public class ControlCenterInfoHandler
|
||||
implements
|
||||
EventHandler {
|
||||
|
||||
/**
|
||||
* This class's Logger.
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ControlCenterInfoHandler.class);
|
||||
/**
|
||||
* Formats time stamps.
|
||||
*/
|
||||
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter
|
||||
.ofLocalizedDateTime(FormatStyle.SHORT)
|
||||
.withLocale(Locale.getDefault())
|
||||
.withZone(ZoneId.systemDefault());
|
||||
/**
|
||||
* This class's configuration.
|
||||
*/
|
||||
private final KernelControlCenterConfiguration configuration;
|
||||
/**
|
||||
* The text area we're writing in.
|
||||
*/
|
||||
private final JTextArea textArea;
|
||||
/**
|
||||
* A flag whether the text area scrolls.
|
||||
*/
|
||||
private boolean autoScroll;
|
||||
|
||||
/**
|
||||
* Creates a new ControlCenterInfoHandler.
|
||||
*
|
||||
* @param textArea The textArea we are writing to.
|
||||
* @param configuration This class' configuration.
|
||||
*/
|
||||
@Inject
|
||||
public ControlCenterInfoHandler(
|
||||
@Assisted
|
||||
JTextArea textArea,
|
||||
KernelControlCenterConfiguration configuration
|
||||
) {
|
||||
this.textArea = requireNonNull(textArea, "textArea");
|
||||
this.configuration = requireNonNull(configuration, "configuration");
|
||||
|
||||
autoScroll = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(Object event) {
|
||||
if (event instanceof PortalManager.ConnectionState) {
|
||||
PortalManager.ConnectionState connectionState = (PortalManager.ConnectionState) event;
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
publish(
|
||||
new UserNotification(
|
||||
"Kernel connection state: " + connectionState.name(),
|
||||
UserNotification.Level.INFORMATIONAL
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
else if (event instanceof ClientConnectionMode) {
|
||||
ClientConnectionMode applicationState = (ClientConnectionMode) event;
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
publish(
|
||||
new UserNotification(
|
||||
"Application state: " + applicationState.name(),
|
||||
UserNotification.Level.INFORMATIONAL
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
else if (event instanceof NotificationPublicationEvent) {
|
||||
SwingUtilities.invokeLater(
|
||||
() -> publish(((NotificationPublicationEvent) event).getNotification())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines if the textArea autoscrolls.
|
||||
*
|
||||
* @param autoScroll true if it should, false otherwise
|
||||
*/
|
||||
public void setAutoScroll(boolean autoScroll) {
|
||||
this.autoScroll = autoScroll;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the notification.
|
||||
*
|
||||
* @param notification The notification
|
||||
*/
|
||||
private void publish(UserNotification notification) {
|
||||
DefaultCaret caret = (DefaultCaret) textArea.getCaret();
|
||||
if (autoScroll) {
|
||||
caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
|
||||
textArea.setCaretPosition(textArea.getDocument().getLength());
|
||||
}
|
||||
else {
|
||||
caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
|
||||
}
|
||||
|
||||
textArea.append(format(notification));
|
||||
textArea.append("\n");
|
||||
checkLength();
|
||||
}
|
||||
|
||||
private String format(UserNotification notification) {
|
||||
return DATE_FORMAT.format(notification.getTimestamp())
|
||||
+ " " + notification.getLevel()
|
||||
+ ": [" + notification.getSource() + "] "
|
||||
+ notification.getText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the length of the document in our textArea is greater than our {@code maxDocLength}
|
||||
* and cuts it if neccessary.
|
||||
*/
|
||||
private synchronized void checkLength() {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
int docLength = textArea.getDocument().getLength();
|
||||
|
||||
if (docLength > configuration.loggingAreaCapacity()) {
|
||||
try {
|
||||
textArea.getDocument().remove(0, docLength - configuration.loggingAreaCapacity());
|
||||
}
|
||||
catch (BadLocationException e) {
|
||||
LOG.warn("Caught exception", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter;
|
||||
|
||||
import javax.swing.JTextArea;
|
||||
|
||||
/**
|
||||
* A factory providing {@link ControlCenterInfoHandler} instances.
|
||||
*/
|
||||
public interface ControlCenterInfoHandlerFactory {
|
||||
|
||||
/**
|
||||
* Creates a new ControlCenterInfoHandler.
|
||||
*
|
||||
* @param textArea The text area.
|
||||
* @return A new ControlCenterInfoHandler.
|
||||
*/
|
||||
ControlCenterInfoHandler createHandler(JTextArea textArea);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter;
|
||||
|
||||
/**
|
||||
* Defines constants regarding internationalization.
|
||||
*/
|
||||
public interface I18nKernelControlCenter {
|
||||
|
||||
/**
|
||||
* The path to the project's resource bundle.
|
||||
*/
|
||||
String BUNDLE_PATH = "i18n/org/opentcs/kernelcontrolcenter/Bundle";
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.3" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JFrameFormInfo">
|
||||
<NonVisualComponents>
|
||||
<Menu class="javax.swing.JMenuBar" name="menuBarMain">
|
||||
<SubComponents>
|
||||
<Menu class="javax.swing.JMenu" name="menuKernel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="KernelControlCenter"/>
|
||||
</Properties>
|
||||
<SubComponents>
|
||||
<MenuItem class="javax.swing.JMenuItem" name="menuButtonConnect">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="kernelControlCenter.menu_kernel.menuItem_connect.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="menuButtonConnectActionPerformed"/>
|
||||
</Events>
|
||||
</MenuItem>
|
||||
<MenuItem class="javax.swing.JMenuItem" name="menuButtonDisconnect">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="kernelControlCenter.menu_kernel.menuItem_disconnect.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="menuButtonDisconnectActionPerformed"/>
|
||||
</Events>
|
||||
</MenuItem>
|
||||
<MenuItem class="javax.swing.JPopupMenu$Separator" name="jSeparator1">
|
||||
</MenuItem>
|
||||
<MenuItem class="javax.swing.JMenuItem" name="menuButtonExit">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="kernelControlCenter.menu_kernel.menuItem_exit.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="menuButtonExitActionPerformed"/>
|
||||
</Events>
|
||||
</MenuItem>
|
||||
</SubComponents>
|
||||
</Menu>
|
||||
<Menu class="javax.swing.JMenu" name="menuHelp">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="kernelControlCenter.menu_help.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<SubComponents>
|
||||
<MenuItem class="javax.swing.JMenuItem" name="menuAbout">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="kernelControlCenter.menu_about.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="menuAboutActionPerformed"/>
|
||||
</Events>
|
||||
</MenuItem>
|
||||
</SubComponents>
|
||||
</Menu>
|
||||
</SubComponents>
|
||||
</Menu>
|
||||
</NonVisualComponents>
|
||||
<Properties>
|
||||
<Property name="defaultCloseOperation" type="int" value="0"/>
|
||||
<Property name="title" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="kernelControlCenter.title" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[1200, 750]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<SyntheticProperties>
|
||||
<SyntheticProperty name="menuBar" type="java.lang.String" value="menuBarMain"/>
|
||||
<SyntheticProperty name="formSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,3,14,0,0,4,-72"/>
|
||||
<SyntheticProperty name="formSizePolicy" type="int" value="0"/>
|
||||
<SyntheticProperty name="generateSize" type="boolean" value="true"/>
|
||||
<SyntheticProperty name="generateCenter" type="boolean" value="true"/>
|
||||
</SyntheticProperties>
|
||||
<Events>
|
||||
<EventHandler event="windowClosing" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="formWindowClosing"/>
|
||||
</Events>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JTabbedPane" name="tabbedPaneMain">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="Center"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="loggingPanel">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
|
||||
<JTabbedPaneConstraints tabName="Logging">
|
||||
<Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="kernelControlCenter.tab_logging.title" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</JTabbedPaneConstraints>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="loggingScrollPane">
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="Center"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTextArea" name="loggingTextArea">
|
||||
<Properties>
|
||||
<Property name="editable" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="loggingPropertyPanel">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="First"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JCheckBox" name="autoScrollCheckBox">
|
||||
<Properties>
|
||||
<Property name="selected" type="boolean" value="true"/>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="kernelControlCenter.checkBox_autoScroll.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="autoScrollCheckBoxActionPerformed"/>
|
||||
</Events>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="17" weightX="1.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
@@ -0,0 +1,531 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.opentcs.common.PortalManager.ConnectionState.CONNECTED;
|
||||
import static org.opentcs.common.PortalManager.ConnectionState.DISCONNECTED;
|
||||
import static org.opentcs.kernelcontrolcenter.I18nKernelControlCenter.BUNDLE_PATH;
|
||||
|
||||
import jakarta.annotation.Nonnull;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Provider;
|
||||
import java.awt.EventQueue;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.opentcs.access.Kernel;
|
||||
import org.opentcs.access.KernelServicePortal;
|
||||
import org.opentcs.access.KernelStateTransitionEvent;
|
||||
import org.opentcs.access.ModelTransitionEvent;
|
||||
import org.opentcs.common.ClientConnectionMode;
|
||||
import org.opentcs.common.KernelClientApplication;
|
||||
import org.opentcs.common.PortalManager;
|
||||
import org.opentcs.components.Lifecycle;
|
||||
import org.opentcs.components.kernelcontrolcenter.ControlCenterPanel;
|
||||
import org.opentcs.customizations.ApplicationEventBus;
|
||||
import org.opentcs.customizations.ServiceCallWrapper;
|
||||
import org.opentcs.customizations.controlcenter.ActiveInModellingMode;
|
||||
import org.opentcs.customizations.controlcenter.ActiveInOperatingMode;
|
||||
import org.opentcs.util.CallWrapper;
|
||||
import org.opentcs.util.event.EventHandler;
|
||||
import org.opentcs.util.event.EventSource;
|
||||
import org.opentcs.util.gui.Icons;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A GUI frontend for basic control over the kernel.
|
||||
*/
|
||||
public class KernelControlCenter
|
||||
extends
|
||||
JFrame
|
||||
implements
|
||||
Lifecycle,
|
||||
EventHandler {
|
||||
|
||||
/**
|
||||
* This class's logger.
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(KernelControlCenter.class);
|
||||
/**
|
||||
* This class's resource bundle.
|
||||
*/
|
||||
private static final ResourceBundle BUNDLE = ResourceBundle.getBundle(BUNDLE_PATH);
|
||||
/**
|
||||
* The factory providing a ControlCenterInfoHandler.
|
||||
*/
|
||||
private final ControlCenterInfoHandlerFactory controlCenterInfoHandlerFactoy;
|
||||
/**
|
||||
* Providers for panels shown in modelling mode.
|
||||
*/
|
||||
private final Collection<Provider<ControlCenterPanel>> panelProvidersModelling;
|
||||
/**
|
||||
* Providers for panels shown in operating mode.
|
||||
*/
|
||||
private final Collection<Provider<ControlCenterPanel>> panelProvidersOperating;
|
||||
/**
|
||||
* An about dialog.
|
||||
*/
|
||||
private final AboutDialog aboutDialog;
|
||||
/**
|
||||
* Panels currently active/shown.
|
||||
*/
|
||||
private final Set<ControlCenterPanel> activePanels = Collections.synchronizedSet(new HashSet<>());
|
||||
/**
|
||||
* The application running this panel.
|
||||
*/
|
||||
private final KernelClientApplication application;
|
||||
/**
|
||||
* Where this instance registers for application events.
|
||||
*/
|
||||
private final EventSource eventSource;
|
||||
/**
|
||||
* The service portal to use for kernel interaction.
|
||||
*/
|
||||
private final KernelServicePortal servicePortal;
|
||||
/**
|
||||
* The portal manager.
|
||||
*/
|
||||
private final PortalManager portalManager;
|
||||
/**
|
||||
* The call wrapper to use for service calls.
|
||||
*/
|
||||
private final CallWrapper callWrapper;
|
||||
/**
|
||||
* Indicates whether this component is initialized.
|
||||
*/
|
||||
private boolean initialized;
|
||||
/**
|
||||
* The ControlCenterInfoHandler.
|
||||
*/
|
||||
private ControlCenterInfoHandler infoHandler;
|
||||
/**
|
||||
* The current Model Name.
|
||||
*/
|
||||
private String currentModel = "";
|
||||
|
||||
/**
|
||||
* Creates new form KernelControlCenter.
|
||||
*
|
||||
* @param application The application running this panel.
|
||||
* @param servicePortal The service portal to use for kernel interaction.
|
||||
* @param callWrapper The call wrapper to use for service calls.
|
||||
* @param portalManager The portal manager.
|
||||
* @param eventSource Where this instance registers for application events.
|
||||
* @param controlCenterInfoHandlerFactory The factory providing a ControlCenterInfoHandler.
|
||||
* @param panelProvidersModelling Providers for panels in modelling mode.
|
||||
* @param panelProvidersOperating Providers for panels in operating mode.
|
||||
*/
|
||||
@Inject
|
||||
@SuppressWarnings("this-escape")
|
||||
public KernelControlCenter(
|
||||
@Nonnull
|
||||
KernelClientApplication application,
|
||||
@Nonnull
|
||||
KernelServicePortal servicePortal,
|
||||
@Nonnull
|
||||
@ServiceCallWrapper
|
||||
CallWrapper callWrapper,
|
||||
@Nonnull
|
||||
PortalManager portalManager,
|
||||
@Nonnull
|
||||
@ApplicationEventBus
|
||||
EventSource eventSource,
|
||||
@Nonnull
|
||||
ControlCenterInfoHandlerFactory controlCenterInfoHandlerFactory,
|
||||
@Nonnull
|
||||
@ActiveInModellingMode
|
||||
Collection<Provider<ControlCenterPanel>> panelProvidersModelling,
|
||||
@Nonnull
|
||||
@ActiveInOperatingMode
|
||||
Collection<Provider<ControlCenterPanel>> panelProvidersOperating
|
||||
) {
|
||||
this.application = requireNonNull(application, "application");
|
||||
this.servicePortal = requireNonNull(servicePortal, "servicePortal");
|
||||
this.callWrapper = requireNonNull(callWrapper, "callWrapper");
|
||||
this.portalManager = requireNonNull(portalManager, "portalManager");
|
||||
this.eventSource = requireNonNull(eventSource, "eventSource");
|
||||
this.controlCenterInfoHandlerFactoy = requireNonNull(
|
||||
controlCenterInfoHandlerFactory,
|
||||
"controlCenterInfoHandlerFactory"
|
||||
);
|
||||
this.panelProvidersModelling = requireNonNull(
|
||||
panelProvidersModelling,
|
||||
"panelProvidersModelling"
|
||||
);
|
||||
this.panelProvidersOperating = requireNonNull(
|
||||
panelProvidersOperating,
|
||||
"panelProvidersOperating"
|
||||
);
|
||||
|
||||
initComponents();
|
||||
setIconImages(Icons.getOpenTCSIcons());
|
||||
aboutDialog = new AboutDialog(this, false);
|
||||
aboutDialog.setAlwaysOnTop(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
if (initialized) {
|
||||
LOG.debug("Already initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
registerControlCenterInfoHandler();
|
||||
eventSource.subscribe(this);
|
||||
|
||||
enteringKernelState(Kernel.State.MODELLING);
|
||||
|
||||
try {
|
||||
EventQueue.invokeAndWait(() -> setVisible(true));
|
||||
}
|
||||
catch (InterruptedException | InvocationTargetException exc) {
|
||||
throw new IllegalStateException("Unexpected exception initializing", exc);
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void terminate() {
|
||||
if (!initialized) {
|
||||
LOG.debug("Not initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
removePanels(activePanels);
|
||||
|
||||
eventSource.unsubscribe(this);
|
||||
eventSource.unsubscribe(infoHandler);
|
||||
|
||||
// Hide the window.
|
||||
setVisible(false);
|
||||
dispose();
|
||||
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
private void onKernelConnect() {
|
||||
try {
|
||||
Kernel.State kernelState = callWrapper.call(() -> servicePortal.getState());
|
||||
enteringKernelState(kernelState);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LOG.warn("Error getting the kernel state", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void onKernelDisconnect() {
|
||||
leavingKernelState(Kernel.State.OPERATING);
|
||||
enteringKernelState(Kernel.State.MODELLING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(Object event) {
|
||||
if (event instanceof ClientConnectionMode) {
|
||||
ClientConnectionMode applicationState = (ClientConnectionMode) event;
|
||||
switch (applicationState) {
|
||||
case ONLINE:
|
||||
onKernelConnect();
|
||||
break;
|
||||
case OFFLINE:
|
||||
onKernelDisconnect();
|
||||
break;
|
||||
default:
|
||||
LOG.debug("Unhandled connection state: {}", applicationState.name());
|
||||
}
|
||||
}
|
||||
else if (event instanceof PortalManager.ConnectionState) {
|
||||
PortalManager.ConnectionState connectionState = (PortalManager.ConnectionState) event;
|
||||
switch (connectionState) {
|
||||
case CONNECTED:
|
||||
updateWindowTitle();
|
||||
break;
|
||||
case DISCONNECTED:
|
||||
updateWindowTitle();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
menuButtonConnect.setEnabled(!portalManager.isConnected());
|
||||
menuButtonDisconnect.setEnabled(portalManager.isConnected());
|
||||
}
|
||||
else if (event instanceof KernelStateTransitionEvent) {
|
||||
KernelStateTransitionEvent stateEvent = (KernelStateTransitionEvent) event;
|
||||
if (!stateEvent.isTransitionFinished()) {
|
||||
leavingKernelState(stateEvent.getLeftState());
|
||||
}
|
||||
else {
|
||||
enteringKernelState(stateEvent.getEnteredState());
|
||||
}
|
||||
}
|
||||
else if (event instanceof ModelTransitionEvent) {
|
||||
ModelTransitionEvent modelEvent = (ModelTransitionEvent) event;
|
||||
updateModelName(modelEvent.getNewModelName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perfoms some tasks when a state is being leaved.
|
||||
*
|
||||
* @param oldState The state we're leaving
|
||||
*/
|
||||
private void leavingKernelState(Kernel.State oldState) {
|
||||
requireNonNull(oldState, "oldState");
|
||||
|
||||
removePanels(activePanels);
|
||||
activePanels.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies this control center that the kernel has entered a different state.
|
||||
*
|
||||
* @param newState
|
||||
*/
|
||||
private void enteringKernelState(Kernel.State newState) {
|
||||
requireNonNull(newState, "newState");
|
||||
|
||||
switch (newState) {
|
||||
case OPERATING:
|
||||
addPanels(panelProvidersOperating);
|
||||
break;
|
||||
case MODELLING:
|
||||
addPanels(panelProvidersModelling);
|
||||
break;
|
||||
default:
|
||||
// Do nada.
|
||||
}
|
||||
// Updating the window title
|
||||
updateWindowTitle();
|
||||
}
|
||||
|
||||
private void addPanels(Collection<Provider<ControlCenterPanel>> providers) {
|
||||
for (Provider<ControlCenterPanel> provider : providers) {
|
||||
SwingUtilities.invokeLater(() -> addPanel(provider.get()));
|
||||
}
|
||||
}
|
||||
|
||||
private void addPanel(ControlCenterPanel panel) {
|
||||
panel.initialize();
|
||||
activePanels.add(panel);
|
||||
tabbedPaneMain.add(panel.getTitle(), panel);
|
||||
}
|
||||
|
||||
private void removePanels(Collection<ControlCenterPanel> panels) {
|
||||
List<ControlCenterPanel> panelsCopy = new ArrayList<>(panels);
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
for (ControlCenterPanel panel : panelsCopy) {
|
||||
tabbedPaneMain.remove(panel);
|
||||
panel.terminate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the model name to the current one.
|
||||
*
|
||||
* @param newModelName The new/updated model name.
|
||||
*/
|
||||
private void updateModelName(String newModelName) {
|
||||
this.currentModel = newModelName;
|
||||
updateWindowTitle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the ControlCenterInfoHandler to the root logger.
|
||||
*/
|
||||
private void registerControlCenterInfoHandler() {
|
||||
infoHandler = controlCenterInfoHandlerFactoy.createHandler(loggingTextArea);
|
||||
eventSource.subscribe(infoHandler);
|
||||
}
|
||||
|
||||
private void updateWindowTitle() {
|
||||
String titleBase = BUNDLE.getString("kernelControlCenter.title");
|
||||
String loadedModel = currentModel.equals("") ? "" : " - " + "\"" + currentModel + "\"";
|
||||
String connectedTo = " - " + BUNDLE.getString("kernelControlCenter.title.connectedTo")
|
||||
+ portalManager.getDescription()
|
||||
+ " (" + portalManager.getHost()
|
||||
+ ":"
|
||||
+ portalManager.getPort() + ")";
|
||||
|
||||
setTitle(titleBase + loadedModel + connectedTo);
|
||||
}
|
||||
|
||||
// FORMATTER:OFF
|
||||
// CHECKSTYLE:OFF
|
||||
// Generated code starts here.
|
||||
/**
|
||||
* This method is called from within the constructor to
|
||||
* initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is
|
||||
* always regenerated by the Form Editor.
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
java.awt.GridBagConstraints gridBagConstraints;
|
||||
|
||||
tabbedPaneMain = new javax.swing.JTabbedPane();
|
||||
loggingPanel = new javax.swing.JPanel();
|
||||
loggingScrollPane = new javax.swing.JScrollPane();
|
||||
loggingTextArea = new javax.swing.JTextArea();
|
||||
loggingPropertyPanel = new javax.swing.JPanel();
|
||||
autoScrollCheckBox = new javax.swing.JCheckBox();
|
||||
menuBarMain = new javax.swing.JMenuBar();
|
||||
menuKernel = new javax.swing.JMenu();
|
||||
menuButtonConnect = new javax.swing.JMenuItem();
|
||||
menuButtonDisconnect = new javax.swing.JMenuItem();
|
||||
jSeparator1 = new javax.swing.JPopupMenu.Separator();
|
||||
menuButtonExit = new javax.swing.JMenuItem();
|
||||
menuHelp = new javax.swing.JMenu();
|
||||
menuAbout = new javax.swing.JMenuItem();
|
||||
|
||||
setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
|
||||
java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("i18n/org/opentcs/kernelcontrolcenter/Bundle"); // NOI18N
|
||||
setTitle(bundle.getString("kernelControlCenter.title")); // NOI18N
|
||||
setMinimumSize(new java.awt.Dimension(1200, 750));
|
||||
addWindowListener(new java.awt.event.WindowAdapter() {
|
||||
public void windowClosing(java.awt.event.WindowEvent evt) {
|
||||
formWindowClosing(evt);
|
||||
}
|
||||
});
|
||||
|
||||
loggingPanel.setLayout(new java.awt.BorderLayout());
|
||||
|
||||
loggingTextArea.setEditable(false);
|
||||
loggingScrollPane.setViewportView(loggingTextArea);
|
||||
|
||||
loggingPanel.add(loggingScrollPane, java.awt.BorderLayout.CENTER);
|
||||
|
||||
loggingPropertyPanel.setLayout(new java.awt.GridBagLayout());
|
||||
|
||||
autoScrollCheckBox.setSelected(true);
|
||||
autoScrollCheckBox.setText(bundle.getString("kernelControlCenter.checkBox_autoScroll.text")); // NOI18N
|
||||
autoScrollCheckBox.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
autoScrollCheckBoxActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
|
||||
gridBagConstraints.weightx = 1.0;
|
||||
loggingPropertyPanel.add(autoScrollCheckBox, gridBagConstraints);
|
||||
|
||||
loggingPanel.add(loggingPropertyPanel, java.awt.BorderLayout.PAGE_START);
|
||||
|
||||
tabbedPaneMain.addTab(bundle.getString("kernelControlCenter.tab_logging.title"), loggingPanel); // NOI18N
|
||||
|
||||
getContentPane().add(tabbedPaneMain, java.awt.BorderLayout.CENTER);
|
||||
|
||||
menuKernel.setText("KernelControlCenter");
|
||||
|
||||
menuButtonConnect.setText(bundle.getString("kernelControlCenter.menu_kernel.menuItem_connect.text")); // NOI18N
|
||||
menuButtonConnect.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
menuButtonConnectActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
menuKernel.add(menuButtonConnect);
|
||||
|
||||
menuButtonDisconnect.setText(bundle.getString("kernelControlCenter.menu_kernel.menuItem_disconnect.text")); // NOI18N
|
||||
menuButtonDisconnect.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
menuButtonDisconnectActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
menuKernel.add(menuButtonDisconnect);
|
||||
menuKernel.add(jSeparator1);
|
||||
|
||||
menuButtonExit.setText(bundle.getString("kernelControlCenter.menu_kernel.menuItem_exit.text")); // NOI18N
|
||||
menuButtonExit.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
menuButtonExitActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
menuKernel.add(menuButtonExit);
|
||||
|
||||
menuBarMain.add(menuKernel);
|
||||
|
||||
menuHelp.setText(bundle.getString("kernelControlCenter.menu_help.text")); // NOI18N
|
||||
|
||||
menuAbout.setText(bundle.getString("kernelControlCenter.menu_about.text")); // NOI18N
|
||||
menuAbout.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
menuAboutActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
menuHelp.add(menuAbout);
|
||||
|
||||
menuBarMain.add(menuHelp);
|
||||
|
||||
setJMenuBar(menuBarMain);
|
||||
|
||||
setSize(new java.awt.Dimension(1208, 782));
|
||||
setLocationRelativeTo(null);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
// CHECKSTYLE:ON
|
||||
// FORMATTER:ON
|
||||
|
||||
private void menuButtonExitActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_menuButtonExitActionPerformed
|
||||
application.terminate();
|
||||
}//GEN-LAST:event_menuButtonExitActionPerformed
|
||||
|
||||
private void autoScrollCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_autoScrollCheckBoxActionPerformed
|
||||
if (autoScrollCheckBox.isSelected()) {
|
||||
infoHandler.setAutoScroll(true);
|
||||
}
|
||||
else {
|
||||
infoHandler.setAutoScroll(false);
|
||||
}
|
||||
}//GEN-LAST:event_autoScrollCheckBoxActionPerformed
|
||||
|
||||
private void menuAboutActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_menuAboutActionPerformed
|
||||
aboutDialog.setLocationRelativeTo(null);
|
||||
aboutDialog.setVisible(true);
|
||||
}//GEN-LAST:event_menuAboutActionPerformed
|
||||
|
||||
private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
|
||||
application.terminate();
|
||||
}//GEN-LAST:event_formWindowClosing
|
||||
|
||||
private void menuButtonConnectActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_menuButtonConnectActionPerformed
|
||||
application.online(false);
|
||||
}//GEN-LAST:event_menuButtonConnectActionPerformed
|
||||
|
||||
private void menuButtonDisconnectActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_menuButtonDisconnectActionPerformed
|
||||
application.offline();
|
||||
}//GEN-LAST:event_menuButtonDisconnectActionPerformed
|
||||
|
||||
// FORMATTER:OFF
|
||||
// CHECKSTYLE:OFF
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JCheckBox autoScrollCheckBox;
|
||||
private javax.swing.JPopupMenu.Separator jSeparator1;
|
||||
private javax.swing.JPanel loggingPanel;
|
||||
private javax.swing.JPanel loggingPropertyPanel;
|
||||
private javax.swing.JScrollPane loggingScrollPane;
|
||||
private javax.swing.JTextArea loggingTextArea;
|
||||
private javax.swing.JMenuItem menuAbout;
|
||||
private javax.swing.JMenuBar menuBarMain;
|
||||
private javax.swing.JMenuItem menuButtonConnect;
|
||||
private javax.swing.JMenuItem menuButtonDisconnect;
|
||||
private javax.swing.JMenuItem menuButtonExit;
|
||||
private javax.swing.JMenu menuHelp;
|
||||
private javax.swing.JMenu menuKernel;
|
||||
private javax.swing.JTabbedPane tabbedPaneMain;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
// CHECKSTYLE:ON
|
||||
// FORMATTER:ON
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
import org.opentcs.common.ClientConnectionMode;
|
||||
import org.opentcs.common.KernelClientApplication;
|
||||
import org.opentcs.common.PortalManager;
|
||||
import org.opentcs.customizations.ApplicationEventBus;
|
||||
import org.opentcs.kernelcontrolcenter.exchange.KernelEventFetcher;
|
||||
import org.opentcs.kernelcontrolcenter.util.KernelControlCenterConfiguration;
|
||||
import org.opentcs.util.event.EventBus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The kernel control center application's entry point.
|
||||
*/
|
||||
public class KernelControlCenterApplication
|
||||
implements
|
||||
KernelClientApplication {
|
||||
|
||||
/**
|
||||
* This class' logger.
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(KernelControlCenterApplication.class);
|
||||
/**
|
||||
* The instance fetching for kernel events.
|
||||
*/
|
||||
private final KernelEventFetcher eventFetcher;
|
||||
/**
|
||||
* The actual kernel control center.
|
||||
*/
|
||||
private final KernelControlCenter kernelControlCenter;
|
||||
/**
|
||||
* The service portal manager.
|
||||
*/
|
||||
private final PortalManager portalManager;
|
||||
/**
|
||||
* The application's event bus.
|
||||
*/
|
||||
private final EventBus eventBus;
|
||||
/**
|
||||
* The application's configuration.
|
||||
*/
|
||||
private final KernelControlCenterConfiguration configuration;
|
||||
/**
|
||||
* Whether this application is online or not.
|
||||
*/
|
||||
private ConnectionState connectionState = ConnectionState.OFFLINE;
|
||||
/**
|
||||
* Whether this application is initialized or not.
|
||||
*/
|
||||
private boolean initialized;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param eventFetcher The instance fetching for kernel events.
|
||||
* @param kernelControlCenter The actual kernel control center.
|
||||
* @param portalManager The service portal manager.
|
||||
* @param eventBus The application's event bus.
|
||||
* @param configuration The application's configuration.
|
||||
*/
|
||||
@Inject
|
||||
public KernelControlCenterApplication(
|
||||
KernelEventFetcher eventFetcher,
|
||||
KernelControlCenter kernelControlCenter,
|
||||
PortalManager portalManager,
|
||||
@ApplicationEventBus
|
||||
EventBus eventBus,
|
||||
KernelControlCenterConfiguration configuration
|
||||
) {
|
||||
this.eventFetcher = requireNonNull(eventFetcher, "eventHub");
|
||||
this.kernelControlCenter = requireNonNull(kernelControlCenter, "kernelControlCenter");
|
||||
this.portalManager = requireNonNull(portalManager, "portalManager");
|
||||
this.eventBus = requireNonNull(eventBus, "eventBus");
|
||||
this.configuration = requireNonNull(configuration, "configuration");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
if (isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
kernelControlCenter.initialize();
|
||||
eventFetcher.initialize();
|
||||
|
||||
// Trigger initial connect
|
||||
online(configuration.connectAutomaticallyOnStartup());
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void terminate() {
|
||||
if (!isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we want to terminate but are still online, go offline first
|
||||
offline();
|
||||
|
||||
eventFetcher.terminate();
|
||||
kernelControlCenter.terminate();
|
||||
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void online(boolean autoConnect) {
|
||||
if (isOnline() || isConnecting()) {
|
||||
return;
|
||||
}
|
||||
|
||||
connectionState = ConnectionState.CONNECTING;
|
||||
if (portalManager.connect(toConnectionMode(autoConnect))) {
|
||||
LOG.info("Switching application state to online...");
|
||||
connectionState = ConnectionState.ONLINE;
|
||||
eventBus.onEvent(ClientConnectionMode.ONLINE);
|
||||
}
|
||||
else {
|
||||
connectionState = ConnectionState.OFFLINE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void offline() {
|
||||
if (!isOnline() && !isConnecting()) {
|
||||
return;
|
||||
}
|
||||
|
||||
portalManager.disconnect();
|
||||
|
||||
LOG.info("Switching application state to offline...");
|
||||
connectionState = ConnectionState.OFFLINE;
|
||||
eventBus.onEvent(ClientConnectionMode.OFFLINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnline() {
|
||||
return connectionState == ConnectionState.ONLINE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if, and only if the control center is trying to establish a
|
||||
* connection to the kernel.
|
||||
*
|
||||
* @return <code>true</code> if, and only if the control center is trying to establish a
|
||||
* connection to the kernel
|
||||
*/
|
||||
public boolean isConnecting() {
|
||||
return connectionState == ConnectionState.CONNECTING;
|
||||
}
|
||||
|
||||
private PortalManager.ConnectionMode toConnectionMode(boolean autoConnect) {
|
||||
return autoConnect ? PortalManager.ConnectionMode.AUTO : PortalManager.ConnectionMode.MANUAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum to display the different states of the control center application connection to the
|
||||
* kernel.
|
||||
*/
|
||||
private enum ConnectionState {
|
||||
/**
|
||||
* The control center is not connected to the kernel and is not trying to.
|
||||
*/
|
||||
OFFLINE,
|
||||
/**
|
||||
* The control center is currently trying to connect to the kernel.
|
||||
*/
|
||||
CONNECTING,
|
||||
/**
|
||||
* The control center is connected to the kernel.
|
||||
*/
|
||||
ONLINE
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter.exchange;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.opentcs.kernelcontrolcenter.I18nKernelControlCenter.BUNDLE_PATH;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Executors;
|
||||
import javax.swing.JOptionPane;
|
||||
import org.opentcs.common.KernelClientApplication;
|
||||
import org.opentcs.common.PortalManager;
|
||||
import org.opentcs.components.kernel.services.ServiceUnavailableException;
|
||||
import org.opentcs.util.CallWrapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The default {@link CallWrapper} implementation used for calling kernel service methods.
|
||||
* <p>
|
||||
* If a service method is called using this implementation and the corresponding service is no
|
||||
* longer available, this implementation will ask to retry the service method call:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>
|
||||
* If 'Retry - Yes' is selected, this implementation will handle reestablishment of the kernel
|
||||
* connection and (upon successful reestablishment) try to call the service method again.
|
||||
* </li>
|
||||
* <li>
|
||||
* If 'Retry - No' is selected, this implementation will throw the exception thrown by the service
|
||||
* mehtod call itself.
|
||||
* </li>
|
||||
* <li>
|
||||
* If 'Cancel' is selected, this implementation will throw the exception thrown by the service
|
||||
* mehtod call itself and additionally will notify the application it's no longer online, i.e.
|
||||
* connected to the kernel.
|
||||
* </li>
|
||||
* </ul>
|
||||
*/
|
||||
public class DefaultServiceCallWrapper
|
||||
implements
|
||||
CallWrapper {
|
||||
|
||||
/**
|
||||
* This class' logger.
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DefaultServiceCallWrapper.class);
|
||||
/**
|
||||
* This class' resource bundle.
|
||||
*/
|
||||
private static final ResourceBundle BUNDLE = ResourceBundle.getBundle(BUNDLE_PATH);
|
||||
/**
|
||||
* The application using this utility.
|
||||
*/
|
||||
private final KernelClientApplication application;
|
||||
/**
|
||||
* The portal manager taking care of the portal connection.
|
||||
*/
|
||||
private final PortalManager portalManager;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param application The application.
|
||||
* @param portalManager The portal manager.
|
||||
*/
|
||||
@Inject
|
||||
public DefaultServiceCallWrapper(
|
||||
KernelClientApplication application,
|
||||
PortalManager portalManager
|
||||
) {
|
||||
this.application = requireNonNull(application, "application");
|
||||
this.portalManager = requireNonNull(portalManager, "portalManager");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> R call(Callable<R> callable)
|
||||
throws Exception {
|
||||
boolean retry = true;
|
||||
Exception failureReason = null;
|
||||
|
||||
while (retry) {
|
||||
try {
|
||||
return callable.call();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LOG.warn("Failed to call remote service method: {}", callable, ex);
|
||||
failureReason = ex;
|
||||
|
||||
if (ex instanceof ServiceUnavailableException) {
|
||||
portalManager.disconnect();
|
||||
retry = showRetryDialog();
|
||||
}
|
||||
else {
|
||||
retry = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// At this point the method call failed and we don't want to try it again anymore.
|
||||
// Therefore throw the exception we caught last.
|
||||
throw failureReason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void call(Runnable runnable)
|
||||
throws Exception {
|
||||
Callable<Object> callable = Executors.callable(runnable);
|
||||
call(callable);
|
||||
}
|
||||
|
||||
private boolean showRetryDialog() {
|
||||
int dialogSelection
|
||||
= JOptionPane.showConfirmDialog(
|
||||
null,
|
||||
BUNDLE.getString("defaultServiceCallWrapper.optionPane_retryConfirmation.message"),
|
||||
BUNDLE.getString("defaultServiceCallWrapper.optionPane_retryConfirmation.title"),
|
||||
JOptionPane.YES_NO_CANCEL_OPTION
|
||||
);
|
||||
|
||||
switch (dialogSelection) {
|
||||
case JOptionPane.YES_OPTION:
|
||||
// Only retry if we connected successfully
|
||||
return portalManager.connect(PortalManager.ConnectionMode.RECONNECT);
|
||||
case JOptionPane.NO_OPTION:
|
||||
return false;
|
||||
case JOptionPane.CANCEL_OPTION:
|
||||
application.offline();
|
||||
return false;
|
||||
case JOptionPane.CLOSED_OPTION:
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter.exchange;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.opentcs.util.Assertions.checkInRange;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.List;
|
||||
import org.opentcs.access.Kernel;
|
||||
import org.opentcs.access.KernelServicePortal;
|
||||
import org.opentcs.access.KernelStateTransitionEvent;
|
||||
import org.opentcs.common.ClientConnectionMode;
|
||||
import org.opentcs.common.KernelClientApplication;
|
||||
import org.opentcs.common.PortalManager;
|
||||
import org.opentcs.components.Lifecycle;
|
||||
import org.opentcs.customizations.ApplicationEventBus;
|
||||
import org.opentcs.customizations.ServiceCallWrapper;
|
||||
import org.opentcs.util.CallWrapper;
|
||||
import org.opentcs.util.CyclicTask;
|
||||
import org.opentcs.util.event.EventBus;
|
||||
import org.opentcs.util.event.EventHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Controls a task that periodically fetches for kernel events.
|
||||
*/
|
||||
public class KernelEventFetcher
|
||||
implements
|
||||
EventHandler,
|
||||
Lifecycle {
|
||||
|
||||
/**
|
||||
* This class' logger.
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(KernelEventFetcher.class);
|
||||
/**
|
||||
* The time to wait between event fetches with the service portal (in ms).
|
||||
*/
|
||||
private final long eventFetchInterval = 100;
|
||||
/**
|
||||
* The time to wait for events to arrive when fetching (in ms).
|
||||
*/
|
||||
private final long eventFetchTimeout = 1000;
|
||||
/**
|
||||
* The application using this event hub.
|
||||
*/
|
||||
private final KernelClientApplication application;
|
||||
/**
|
||||
* The service portal to fetch events from.
|
||||
*/
|
||||
private final KernelServicePortal servicePortal;
|
||||
/**
|
||||
* The call wrapper to use.
|
||||
*/
|
||||
private final CallWrapper callWrapper;
|
||||
/**
|
||||
* The application's event bus.
|
||||
*/
|
||||
private final EventBus eventBus;
|
||||
/**
|
||||
* The task fetching the service portal for new events.
|
||||
*/
|
||||
private EventFetcherTask eventFetcherTask;
|
||||
/**
|
||||
* Whether this event hub is initialized or not.
|
||||
*/
|
||||
private boolean initialized;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param application The application using this event hub.
|
||||
* @param servicePortal The service portal to fetch events from.
|
||||
* @param callWrapper The call wrapper to use for fetching events.
|
||||
* @param eventBus The application's event bus.
|
||||
*/
|
||||
@Inject
|
||||
public KernelEventFetcher(
|
||||
KernelClientApplication application,
|
||||
KernelServicePortal servicePortal,
|
||||
@ServiceCallWrapper
|
||||
CallWrapper callWrapper,
|
||||
@ApplicationEventBus
|
||||
EventBus eventBus
|
||||
) {
|
||||
this.application = requireNonNull(application, "application");
|
||||
this.servicePortal = requireNonNull(servicePortal, "servicePortal");
|
||||
this.callWrapper = requireNonNull(callWrapper, "callWrapper");
|
||||
this.eventBus = requireNonNull(eventBus, "eventBus");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
if (isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
eventBus.subscribe(this);
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void terminate() {
|
||||
if (!isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
eventBus.unsubscribe(this);
|
||||
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(Object event) {
|
||||
if (event == PortalManager.ConnectionState.DISCONNECTING) {
|
||||
onKernelDisconnect();
|
||||
}
|
||||
else if (event instanceof ClientConnectionMode) {
|
||||
ClientConnectionMode applicationState = (ClientConnectionMode) event;
|
||||
switch (applicationState) {
|
||||
case ONLINE:
|
||||
onKernelConnect();
|
||||
break;
|
||||
case OFFLINE:
|
||||
onKernelDisconnect();
|
||||
break;
|
||||
default:
|
||||
LOG.debug("Unhandled portal connection state: {}", applicationState.name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onKernelConnect() {
|
||||
if (eventFetcherTask != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
eventFetcherTask = new EventFetcherTask(eventFetchInterval, eventFetchTimeout);
|
||||
Thread eventFetcherThread = new Thread(eventFetcherTask, getClass().getName() + "-fetcherTask");
|
||||
eventFetcherThread.start();
|
||||
}
|
||||
|
||||
private void onKernelDisconnect() {
|
||||
if (eventFetcherTask == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop polling for events.
|
||||
eventFetcherTask.terminateAndWait();
|
||||
eventFetcherTask = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A task fetching the service portal for events in regular intervals.
|
||||
*/
|
||||
private class EventFetcherTask
|
||||
extends
|
||||
CyclicTask {
|
||||
|
||||
/**
|
||||
* The poll timeout.
|
||||
*/
|
||||
private final long timeout;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param interval The time to wait between polls in ms.
|
||||
* @param timeout The timeout in ms for which to wait for events to arrive with each polling
|
||||
* call.
|
||||
*/
|
||||
private EventFetcherTask(long interval, long timeout) {
|
||||
super(interval);
|
||||
this.timeout = checkInRange(timeout, 1, Long.MAX_VALUE, "timeout");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runActualTask() {
|
||||
boolean shutDown = false;
|
||||
try {
|
||||
LOG.debug("Fetching remote kernel for events");
|
||||
List<Object> events = callWrapper.call(() -> servicePortal.fetchEvents(timeout));
|
||||
|
||||
for (Object event : events) {
|
||||
LOG.debug("Processing fetched event: {}", event);
|
||||
// Forward received events to all registered listeners.
|
||||
eventBus.onEvent(event);
|
||||
|
||||
// Check if the kernel notifies us about a state change.
|
||||
if (event instanceof KernelStateTransitionEvent) {
|
||||
KernelStateTransitionEvent stateEvent = (KernelStateTransitionEvent) event;
|
||||
// If the kernel switches to SHUTDOWN, remember to shut down.
|
||||
shutDown = stateEvent.getEnteredState() == Kernel.State.SHUTDOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exc) {
|
||||
LOG.error("Exception fetching events", exc);
|
||||
}
|
||||
|
||||
if (shutDown) {
|
||||
application.offline();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter.exchange;
|
||||
|
||||
import org.opentcs.configuration.ConfigurationEntry;
|
||||
import org.opentcs.configuration.ConfigurationPrefix;
|
||||
|
||||
/**
|
||||
* Provides methods to configure the ssl connection.
|
||||
*/
|
||||
@ConfigurationPrefix(SslConfiguration.PREFIX)
|
||||
public interface SslConfiguration {
|
||||
|
||||
/**
|
||||
* This configuration's prefix.
|
||||
*/
|
||||
String PREFIX = "ssl";
|
||||
|
||||
@ConfigurationEntry(
|
||||
type = "Boolean",
|
||||
description = "Whether to use SSL to encrypt RMI connections to the kernel.",
|
||||
changesApplied = ConfigurationEntry.ChangesApplied.ON_APPLICATION_START,
|
||||
orderKey = "0_connection_0"
|
||||
)
|
||||
boolean enable();
|
||||
|
||||
@ConfigurationEntry(
|
||||
type = "String",
|
||||
description = "The path to the SSL truststore.",
|
||||
changesApplied = ConfigurationEntry.ChangesApplied.ON_APPLICATION_START,
|
||||
orderKey = "0_connection_1"
|
||||
)
|
||||
String truststoreFile();
|
||||
|
||||
@ConfigurationEntry(
|
||||
type = "String",
|
||||
description = "The password for the SSL truststore.",
|
||||
changesApplied = ConfigurationEntry.ChangesApplied.ON_APPLICATION_START,
|
||||
orderKey = "0_connection_2"
|
||||
)
|
||||
String truststorePassword();
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter.peripherals;
|
||||
|
||||
import java.awt.Component;
|
||||
import javax.swing.DefaultListCellRenderer;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.ListCellRenderer;
|
||||
import org.opentcs.drivers.peripherals.PeripheralCommAdapterDescription;
|
||||
|
||||
/**
|
||||
* ListCellRenderer for the adapter combo box.
|
||||
*/
|
||||
final class AdapterFactoryCellRenderer
|
||||
implements
|
||||
ListCellRenderer<PeripheralCommAdapterDescription> {
|
||||
|
||||
/**
|
||||
* A default renderer for creating the label.
|
||||
*/
|
||||
private final DefaultListCellRenderer defaultRenderer = new DefaultListCellRenderer();
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
AdapterFactoryCellRenderer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getListCellRendererComponent(
|
||||
JList<? extends PeripheralCommAdapterDescription> list,
|
||||
PeripheralCommAdapterDescription value,
|
||||
int index,
|
||||
boolean isSelected,
|
||||
boolean cellHasFocus
|
||||
) {
|
||||
JLabel label = (JLabel) defaultRenderer.getListCellRendererComponent(
|
||||
list,
|
||||
value,
|
||||
index,
|
||||
isSelected,
|
||||
cellHasFocus
|
||||
);
|
||||
if (value != null) {
|
||||
label.setText(value.getDescription());
|
||||
}
|
||||
else {
|
||||
label.setText(" ");
|
||||
}
|
||||
return label;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter.peripherals;
|
||||
|
||||
import java.awt.Component;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import org.opentcs.drivers.peripherals.PeripheralCommAdapterDescription;
|
||||
|
||||
/**
|
||||
* A {@link TableCellRenderer} for {@link PeripheralCommAdapterDescription} instances.
|
||||
* This class provides a representation of any PeripheralCommAdapterDescription instance by writing
|
||||
* its actual description on a JLabel.
|
||||
*/
|
||||
class CommAdapterFactoryTableCellRenderer
|
||||
extends
|
||||
DefaultTableCellRenderer {
|
||||
|
||||
CommAdapterFactoryTableCellRenderer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(
|
||||
JTable table,
|
||||
Object value,
|
||||
boolean isSelected,
|
||||
boolean hasFocus,
|
||||
int row,
|
||||
int column
|
||||
)
|
||||
throws IllegalArgumentException {
|
||||
|
||||
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||
|
||||
if (value == null) {
|
||||
setText("");
|
||||
}
|
||||
else if (value instanceof PeripheralCommAdapterDescription) {
|
||||
setText(((PeripheralCommAdapterDescription) value).getDescription());
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("value");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter.peripherals;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import jakarta.annotation.Nonnull;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyChangeSupport;
|
||||
import org.opentcs.data.model.Location;
|
||||
import org.opentcs.data.model.TCSResourceReference;
|
||||
import org.opentcs.drivers.peripherals.PeripheralCommAdapterDescription;
|
||||
import org.opentcs.drivers.peripherals.PeripheralProcessModel;
|
||||
|
||||
/**
|
||||
* An entry for a peripheral device registered with the kernel.
|
||||
*/
|
||||
public class LocalPeripheralEntry {
|
||||
|
||||
/**
|
||||
* Used for implementing property change events.
|
||||
*/
|
||||
@SuppressWarnings("this-escape")
|
||||
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
|
||||
/**
|
||||
* The location representing the peripheral device.
|
||||
*/
|
||||
private final TCSResourceReference<Location> location;
|
||||
/**
|
||||
* The description of the attached peripheral comm adapter.
|
||||
*/
|
||||
private PeripheralCommAdapterDescription attachedCommAdapter;
|
||||
/**
|
||||
* The process model that describes the peripheral device's state.
|
||||
*/
|
||||
private PeripheralProcessModel processModel;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param location The location representing the peripheral device.
|
||||
* @param attachedCommAdapter The description of the attached peripheral comm adapter.
|
||||
* @param processModel The process model that describes the peripheral device's state.
|
||||
*/
|
||||
public LocalPeripheralEntry(
|
||||
@Nonnull
|
||||
TCSResourceReference<Location> location,
|
||||
@Nonnull
|
||||
PeripheralCommAdapterDescription attachedCommAdapter,
|
||||
@Nonnull
|
||||
PeripheralProcessModel processModel
|
||||
) {
|
||||
this.location = requireNonNull(location, "location");
|
||||
this.attachedCommAdapter = requireNonNull(attachedCommAdapter, "attachedCommAdapter");
|
||||
this.processModel = requireNonNull(processModel, "processModel");
|
||||
}
|
||||
|
||||
public void addPropertyChangeListener(PropertyChangeListener listener) {
|
||||
pcs.addPropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
public void removePropertyChangeListener(PropertyChangeListener listener) {
|
||||
pcs.removePropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the location representing the peripheral device.
|
||||
*
|
||||
* @return The location representing the peripheral device.
|
||||
*/
|
||||
@Nonnull
|
||||
public TCSResourceReference<Location> getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of the attached peripheral comm adapter.
|
||||
*
|
||||
* @return The description of the attached peripheral comm adapter.
|
||||
*/
|
||||
@Nonnull
|
||||
public PeripheralCommAdapterDescription getAttachedCommAdapter() {
|
||||
return attachedCommAdapter;
|
||||
}
|
||||
|
||||
public void setAttachedCommAdapter(
|
||||
@Nonnull
|
||||
PeripheralCommAdapterDescription attachedCommAdapter
|
||||
) {
|
||||
PeripheralCommAdapterDescription oldAttachedCommAdapter = this.attachedCommAdapter;
|
||||
this.attachedCommAdapter = requireNonNull(attachedCommAdapter, "attachedCommAdapter");
|
||||
|
||||
pcs.firePropertyChange(
|
||||
Attribute.ATTACHED_COMM_ADAPTER.name(),
|
||||
oldAttachedCommAdapter,
|
||||
attachedCommAdapter
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the process model that describes the peripheral device's state.
|
||||
*
|
||||
* @return The process model that describes the peripheral device's state.
|
||||
*/
|
||||
@Nonnull
|
||||
public PeripheralProcessModel getProcessModel() {
|
||||
return processModel;
|
||||
}
|
||||
|
||||
public void setProcessModel(
|
||||
@Nonnull
|
||||
PeripheralProcessModel processModel
|
||||
) {
|
||||
PeripheralProcessModel oldProcessModel = this.processModel;
|
||||
this.processModel = requireNonNull(processModel, "processModel");
|
||||
|
||||
pcs.firePropertyChange(
|
||||
Attribute.PROCESS_MODEL.name(),
|
||||
oldProcessModel,
|
||||
processModel
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum elements used as notification arguments to specify which argument changed.
|
||||
*/
|
||||
public enum Attribute {
|
||||
/**
|
||||
* Indicates a change of the process model.
|
||||
*/
|
||||
PROCESS_MODEL,
|
||||
/**
|
||||
* Indicates a change of the attached comm adapter.
|
||||
*/
|
||||
ATTACHED_COMM_ADAPTER
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter.peripherals;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.opentcs.access.KernelServicePortal;
|
||||
import org.opentcs.components.Lifecycle;
|
||||
import org.opentcs.customizations.ApplicationEventBus;
|
||||
import org.opentcs.customizations.ServiceCallWrapper;
|
||||
import org.opentcs.data.model.Location;
|
||||
import org.opentcs.data.model.TCSResourceReference;
|
||||
import org.opentcs.drivers.peripherals.PeripheralProcessModel;
|
||||
import org.opentcs.drivers.peripherals.management.PeripheralAttachmentEvent;
|
||||
import org.opentcs.drivers.peripherals.management.PeripheralAttachmentInformation;
|
||||
import org.opentcs.drivers.peripherals.management.PeripheralProcessModelEvent;
|
||||
import org.opentcs.util.CallWrapper;
|
||||
import org.opentcs.util.event.EventHandler;
|
||||
import org.opentcs.util.event.EventSource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A pool of {@link LocalPeripheralEntry}'s for every location in the kernel that represents a
|
||||
* peripheral device.
|
||||
*/
|
||||
public class LocalPeripheralEntryPool
|
||||
implements
|
||||
EventHandler,
|
||||
Lifecycle {
|
||||
|
||||
/**
|
||||
* This class's logger.
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(LocalPeripheralEntryPool.class);
|
||||
/**
|
||||
* The service portal to use for kernel interactions.
|
||||
*/
|
||||
private final KernelServicePortal servicePortal;
|
||||
/**
|
||||
* The call wrapper to use for service calls.
|
||||
*/
|
||||
private final CallWrapper callWrapper;
|
||||
/**
|
||||
* Where this instance registers for application events.
|
||||
*/
|
||||
private final EventSource eventSource;
|
||||
/**
|
||||
* The entries of this pool.
|
||||
*/
|
||||
private final Map<TCSResourceReference<Location>, LocalPeripheralEntry> entries = new HashMap<>();
|
||||
/**
|
||||
* Whether the pool is initialized or not.
|
||||
*/
|
||||
private boolean initialized;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param servicePortal The service portal to use for kernel interactions.
|
||||
* @param callWrapper The call wrapper to use for service calls.
|
||||
* @param eventSource Where this instance registers for application events.
|
||||
*/
|
||||
@Inject
|
||||
public LocalPeripheralEntryPool(
|
||||
KernelServicePortal servicePortal,
|
||||
@ServiceCallWrapper
|
||||
CallWrapper callWrapper,
|
||||
@ApplicationEventBus
|
||||
EventSource eventSource
|
||||
) {
|
||||
this.servicePortal = requireNonNull(servicePortal, "servicePortal");
|
||||
this.callWrapper = requireNonNull(callWrapper, "callWrapper");
|
||||
this.eventSource = requireNonNull(eventSource, "eventSource");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
if (isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
initializeEntryMap();
|
||||
LOG.debug("Initialized peripheral entry pool: {}", entries);
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.warn("Error initializing peripheral entry pool.", e);
|
||||
entries.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
eventSource.subscribe(this);
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void terminate() {
|
||||
if (!isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
eventSource.unsubscribe(this);
|
||||
entries.clear();
|
||||
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(Object event) {
|
||||
if (event instanceof PeripheralProcessModelEvent) {
|
||||
onPeripheralProcessModelEvent((PeripheralProcessModelEvent) event);
|
||||
}
|
||||
else if (event instanceof PeripheralAttachmentEvent) {
|
||||
onPeripheralAttachmentEvent((PeripheralAttachmentEvent) event);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<TCSResourceReference<Location>, LocalPeripheralEntry> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
private void initializeEntryMap()
|
||||
throws Exception {
|
||||
Set<Location> locations
|
||||
= callWrapper.call(() -> servicePortal.getPlantModelService().fetchObjects(Location.class));
|
||||
for (Location location : locations) {
|
||||
PeripheralAttachmentInformation ai = callWrapper.call(() -> {
|
||||
return servicePortal.getPeripheralService()
|
||||
.fetchAttachmentInformation(location.getReference());
|
||||
});
|
||||
PeripheralProcessModel processModel = callWrapper.call(
|
||||
() -> servicePortal.getPeripheralService().fetchProcessModel(location.getReference())
|
||||
);
|
||||
entries.put(
|
||||
location.getReference(), new LocalPeripheralEntry(
|
||||
location.getReference(),
|
||||
ai.getAttachedCommAdapter(),
|
||||
processModel
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void onPeripheralProcessModelEvent(PeripheralProcessModelEvent event) {
|
||||
if (!entries.containsKey(event.getLocation())) {
|
||||
LOG.warn("Received an event for an unknown location: {}", event.getLocation().getName());
|
||||
return;
|
||||
}
|
||||
|
||||
entries.get(event.getLocation()).setProcessModel(event.getProcessModel());
|
||||
}
|
||||
|
||||
private void onPeripheralAttachmentEvent(PeripheralAttachmentEvent event) {
|
||||
if (!entries.containsKey(event.getLocation())) {
|
||||
LOG.warn("Received an event for an unknown location: {}", event.getLocation().getName());
|
||||
return;
|
||||
}
|
||||
|
||||
entries.get(event.getLocation())
|
||||
.setAttachedCommAdapter(event.getAttachmentInformation().getAttachedCommAdapter());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter.peripherals;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Objects;
|
||||
import javax.swing.JComboBox;
|
||||
import org.opentcs.drivers.peripherals.PeripheralCommAdapterDescription;
|
||||
|
||||
/**
|
||||
* A wide combobox which sets the selected item when receiving an update event from a
|
||||
* {@link LocalPeripheralEntry}.
|
||||
*/
|
||||
public class PeripheralAdapterComboBox
|
||||
extends
|
||||
JComboBox<PeripheralCommAdapterDescription>
|
||||
implements
|
||||
PropertyChangeListener {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public PeripheralAdapterComboBox() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PeripheralCommAdapterDescription getSelectedItem() {
|
||||
return (PeripheralCommAdapterDescription) super.getSelectedItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (!(evt.getSource() instanceof LocalPeripheralEntry)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LocalPeripheralEntry entry = (LocalPeripheralEntry) evt.getSource();
|
||||
if (Objects.equals(entry.getAttachedCommAdapter(), getModel().getSelectedItem())) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.setSelectedItem(entry.getAttachedCommAdapter());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.6" maxVersion="1.6" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<NonVisualComponents>
|
||||
<Container class="javax.swing.JPanel" name="noPeripheralDevicePanel">
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="noPeripheralDeviceLabel">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font component="noPeripheralDeviceLabel" property="font" relativeSize="true" size="3"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="horizontalAlignment" type="int" value="0"/>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="peripheralDetailPanel.label_noPeripheralDeviceAttached.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="Center"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</NonVisualComponents>
|
||||
<Properties>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
|
||||
<TitledBorder title="<User Code>">
|
||||
<Connection PropertyName="titleX" code="DEFAULT_BORDER_TITLE" type="code"/>
|
||||
</TitledBorder>
|
||||
</Border>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="2"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,-63,0,0,2,-123"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JTabbedPane" name="tabbedPane">
|
||||
<Properties>
|
||||
<Property name="tabLayoutPolicy" type="int" value="1"/>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="Center"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
@@ -0,0 +1,265 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter.peripherals;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.border.TitledBorder;
|
||||
import org.opentcs.components.Lifecycle;
|
||||
import org.opentcs.drivers.peripherals.management.PeripheralCommAdapterPanel;
|
||||
import org.opentcs.drivers.peripherals.management.PeripheralCommAdapterPanelFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Displays the comm adapter panels for a given peripheral device.
|
||||
*/
|
||||
public class PeripheralDetailPanel
|
||||
extends
|
||||
JPanel
|
||||
implements
|
||||
PropertyChangeListener,
|
||||
Lifecycle {
|
||||
|
||||
/**
|
||||
* This class's logger.
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PeripheralDetailPanel.class);
|
||||
/**
|
||||
* A panel's default border title.
|
||||
*/
|
||||
private static final String DEFAULT_BORDER_TITLE = "";
|
||||
/**
|
||||
* The adapter specific list of panels.
|
||||
*/
|
||||
private final List<PeripheralCommAdapterPanel> customPanelList = new ArrayList<>();
|
||||
/**
|
||||
* The set of factories to create adapter specific panels with.
|
||||
*/
|
||||
private final Set<PeripheralCommAdapterPanelFactory> panelFactories;
|
||||
/**
|
||||
* The peripheral device that is currently associated with/being displayed in this panel.
|
||||
*/
|
||||
private LocalPeripheralEntry peripheralEntry;
|
||||
/**
|
||||
* Whether this panel is initialized or not.
|
||||
*/
|
||||
private boolean initialized;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param panelFactories The factories to create adapter specific panels with.
|
||||
*/
|
||||
@Inject
|
||||
@SuppressWarnings("this-escape")
|
||||
public PeripheralDetailPanel(Set<PeripheralCommAdapterPanelFactory> panelFactories) {
|
||||
this.panelFactories = requireNonNull(panelFactories, "panelFactories");
|
||||
|
||||
initComponents();
|
||||
|
||||
// Make sure we start with an empty panel.
|
||||
detachFromEntry();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
if (isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (PeripheralCommAdapterPanelFactory panelFactory : panelFactories) {
|
||||
panelFactory.initialize();
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void terminate() {
|
||||
if (!isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
detachFromEntry();
|
||||
|
||||
for (PeripheralCommAdapterPanelFactory panelFactory : panelFactories) {
|
||||
panelFactory.terminate();
|
||||
}
|
||||
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (!(evt.getSource() instanceof LocalPeripheralEntry)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LocalPeripheralEntry entry = (LocalPeripheralEntry) evt.getSource();
|
||||
if (!Objects.equals(entry, peripheralEntry)) {
|
||||
// Since we're registered only to the currently selected/attached entry, this should never
|
||||
// happen.
|
||||
return;
|
||||
}
|
||||
|
||||
if (Objects.equals(
|
||||
evt.getPropertyName(),
|
||||
LocalPeripheralEntry.Attribute.PROCESS_MODEL.name()
|
||||
)) {
|
||||
for (PeripheralCommAdapterPanel panel : customPanelList) {
|
||||
panel.processModelChanged(entry.getProcessModel());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches this panel to a peripheral device.
|
||||
*
|
||||
* @param newPeripheralEntry The peripheral entry to attach to.
|
||||
*/
|
||||
public void attachToEntry(LocalPeripheralEntry newPeripheralEntry) {
|
||||
requireNonNull(newPeripheralEntry, "newPeripheralEntry");
|
||||
|
||||
// Clean up first - but only if we're not reattaching to the same entry.
|
||||
if (peripheralEntry != newPeripheralEntry) {
|
||||
detachFromEntry();
|
||||
}
|
||||
peripheralEntry = newPeripheralEntry;
|
||||
|
||||
setBorderTitle(peripheralEntry.getProcessModel().getLocation().getName());
|
||||
|
||||
// Ensure the tabbed pane containing peripheral information is shown.
|
||||
removeAll();
|
||||
add(tabbedPane);
|
||||
|
||||
updateCustomPanels();
|
||||
|
||||
// Update panel contents.
|
||||
validate();
|
||||
if (!customPanelList.isEmpty()) {
|
||||
tabbedPane.setSelectedIndex(0);
|
||||
}
|
||||
|
||||
peripheralEntry.addPropertyChangeListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches this panel from a peripheral device (if it is currently attached to any).
|
||||
*/
|
||||
private void detachFromEntry() {
|
||||
if (peripheralEntry != null) {
|
||||
peripheralEntry.removePropertyChangeListener(this);
|
||||
removeAndClearCustomPanels();
|
||||
peripheralEntry = null;
|
||||
}
|
||||
setBorderTitle(DEFAULT_BORDER_TITLE);
|
||||
// Remove the contents of this panel.
|
||||
removeAll();
|
||||
add(noPeripheralDevicePanel);
|
||||
// Update panel contents.
|
||||
validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the custom panels from this panel's tabbed pane.
|
||||
*/
|
||||
private void removeAndClearCustomPanels() {
|
||||
for (PeripheralCommAdapterPanel panel : customPanelList) {
|
||||
LOG.debug("Removing {} from tabbedPane.", panel);
|
||||
tabbedPane.remove(panel);
|
||||
}
|
||||
|
||||
customPanelList.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the list of custom panels in the tabbed pane.
|
||||
*/
|
||||
private void updateCustomPanels() {
|
||||
removeAndClearCustomPanels();
|
||||
|
||||
if (peripheralEntry == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (PeripheralCommAdapterPanelFactory panelFactory : panelFactories) {
|
||||
customPanelList.addAll(
|
||||
panelFactory.getPanelsFor(
|
||||
peripheralEntry.getAttachedCommAdapter(),
|
||||
peripheralEntry.getLocation(),
|
||||
peripheralEntry.getProcessModel()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
for (PeripheralCommAdapterPanel curPanel : customPanelList) {
|
||||
LOG.debug("Adding {} with title {} to tabbedPane.", curPanel, curPanel.getTitle());
|
||||
tabbedPane.addTab(curPanel.getTitle(), curPanel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this panel's border title.
|
||||
*
|
||||
* @param title This panel's new border title.
|
||||
*/
|
||||
private void setBorderTitle(String title) {
|
||||
requireNonNull(title, "title");
|
||||
((TitledBorder) getBorder()).setTitle(title);
|
||||
// Trigger a repaint - the title sometimes looks strange otherwise.
|
||||
repaint();
|
||||
}
|
||||
|
||||
// FORMATTER:OFF
|
||||
// CHECKSTYLE:OFF
|
||||
/**
|
||||
* This method is called from within the constructor to
|
||||
* initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is
|
||||
* always regenerated by the Form Editor.
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
noPeripheralDevicePanel = new javax.swing.JPanel();
|
||||
noPeripheralDeviceLabel = new javax.swing.JLabel();
|
||||
tabbedPane = new javax.swing.JTabbedPane();
|
||||
|
||||
noPeripheralDevicePanel.setLayout(new java.awt.BorderLayout());
|
||||
|
||||
noPeripheralDeviceLabel.setFont(noPeripheralDeviceLabel.getFont().deriveFont(noPeripheralDeviceLabel.getFont().getSize()+3f));
|
||||
noPeripheralDeviceLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
|
||||
java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("i18n/org/opentcs/kernelcontrolcenter/Bundle"); // NOI18N
|
||||
noPeripheralDeviceLabel.setText(bundle.getString("peripheralDetailPanel.label_noPeripheralDeviceAttached.text")); // NOI18N
|
||||
noPeripheralDevicePanel.add(noPeripheralDeviceLabel, java.awt.BorderLayout.CENTER);
|
||||
|
||||
setBorder(javax.swing.BorderFactory.createTitledBorder(DEFAULT_BORDER_TITLE));
|
||||
setLayout(new java.awt.BorderLayout());
|
||||
|
||||
tabbedPane.setTabLayoutPolicy(javax.swing.JTabbedPane.SCROLL_TAB_LAYOUT);
|
||||
add(tabbedPane, java.awt.BorderLayout.CENTER);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JLabel noPeripheralDeviceLabel;
|
||||
private javax.swing.JPanel noPeripheralDevicePanel;
|
||||
private javax.swing.JTabbedPane tabbedPane;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
// CHECKSTYLE:ON
|
||||
// FORMATTER:ON
|
||||
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter.peripherals;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.opentcs.kernelcontrolcenter.I18nKernelControlCenter.BUNDLE_PATH;
|
||||
import static org.opentcs.util.Assertions.checkArgument;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.ResourceBundle;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import org.opentcs.access.KernelServicePortal;
|
||||
import org.opentcs.drivers.peripherals.PeripheralCommAdapterDescription;
|
||||
import org.opentcs.drivers.peripherals.PeripheralProcessModel;
|
||||
import org.opentcs.util.CallWrapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A model for displaying a table of peripherals.
|
||||
*/
|
||||
public class PeripheralTableModel
|
||||
extends
|
||||
AbstractTableModel
|
||||
implements
|
||||
PropertyChangeListener {
|
||||
|
||||
/**
|
||||
* The index of the column showing the peripheral device's name.
|
||||
*/
|
||||
public static final int COLUMN_LOCATION = 0;
|
||||
/**
|
||||
* The index of the column showing the associated adapter.
|
||||
*/
|
||||
public static final int COLUMN_ADAPTER = 1;
|
||||
/**
|
||||
* The index of the column showing the adapter's enabled state.
|
||||
*/
|
||||
public static final int COLUMN_ENABLED = 2;
|
||||
/**
|
||||
* This class's logger.
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PeripheralTableModel.class);
|
||||
/**
|
||||
* This class's resource bundle.
|
||||
*/
|
||||
private static final ResourceBundle BUNDLE = ResourceBundle.getBundle(BUNDLE_PATH);
|
||||
/**
|
||||
* The collumn names for the table header.
|
||||
*/
|
||||
private static final String[] COLUMN_NAMES
|
||||
= new String[]{
|
||||
BUNDLE.getString("peripheralTableModel.column_location.headerText"),
|
||||
BUNDLE.getString("peripheralTableModel.column_adapter.headerText"),
|
||||
BUNDLE.getString("peripheralTableModel.column_enabled.headerText")
|
||||
};
|
||||
/**
|
||||
* The column classes.
|
||||
*/
|
||||
private static final Class<?>[] COLUMN_CLASSES
|
||||
= new Class<?>[]{
|
||||
String.class,
|
||||
PeripheralCommAdapterDescription.class,
|
||||
Boolean.class
|
||||
};
|
||||
/**
|
||||
* The service portal to use for kernel interaction.
|
||||
*/
|
||||
private final KernelServicePortal servicePortal;
|
||||
/**
|
||||
* The call wrapper to use.
|
||||
*/
|
||||
private final CallWrapper callWrapper;
|
||||
/**
|
||||
* The entries in the table.
|
||||
*/
|
||||
private final List<LocalPeripheralEntry> entries = new ArrayList<>();
|
||||
|
||||
public PeripheralTableModel(
|
||||
KernelServicePortal servicePortal,
|
||||
CallWrapper callWrapper
|
||||
) {
|
||||
this.servicePortal = requireNonNull(servicePortal, "servicePortal");
|
||||
this.callWrapper = requireNonNull(callWrapper, "callWrapper");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier for the adapter column.
|
||||
*
|
||||
* @return the idenfitier for the adapter column.
|
||||
*/
|
||||
public static String adapterColumnIdentifier() {
|
||||
return COLUMN_NAMES[COLUMN_ADAPTER];
|
||||
}
|
||||
|
||||
public void addData(LocalPeripheralEntry entry) {
|
||||
entries.add(entry);
|
||||
fireTableRowsInserted(entries.size() - 1, entries.size() - 1);
|
||||
}
|
||||
|
||||
public LocalPeripheralEntry getDataAt(int index) {
|
||||
checkArgument(
|
||||
index >= 0 && index < entries.size(),
|
||||
"index must be in 0..%d: %d",
|
||||
entries.size(),
|
||||
index
|
||||
);
|
||||
return entries.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return entries.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return COLUMN_NAMES.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
if (rowIndex >= entries.size()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
LocalPeripheralEntry entry = entries.get(rowIndex);
|
||||
|
||||
switch (columnIndex) {
|
||||
case COLUMN_LOCATION:
|
||||
return entry.getProcessModel().getLocation().getName();
|
||||
case COLUMN_ADAPTER:
|
||||
return entry.getAttachedCommAdapter();
|
||||
case COLUMN_ENABLED:
|
||||
return entry.getProcessModel().isCommAdapterEnabled();
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid column index: " + columnIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
|
||||
requireNonNull(aValue, "aValue");
|
||||
|
||||
LocalPeripheralEntry entry = entries.get(rowIndex);
|
||||
switch (columnIndex) {
|
||||
case COLUMN_LOCATION:
|
||||
break;
|
||||
case COLUMN_ADAPTER:
|
||||
break;
|
||||
case COLUMN_ENABLED:
|
||||
try {
|
||||
if ((boolean) aValue) {
|
||||
callWrapper.call(
|
||||
() -> servicePortal.getPeripheralService().enableCommAdapter(entry.getLocation())
|
||||
);
|
||||
}
|
||||
else {
|
||||
callWrapper.call(
|
||||
() -> servicePortal.getPeripheralService().disableCommAdapter(entry.getLocation())
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LOG.warn(
|
||||
"Error enabling/disabling peripheral comm adapter for {}",
|
||||
entry.getLocation().getName(),
|
||||
ex
|
||||
);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid column index: " + columnIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
||||
switch (columnIndex) {
|
||||
case COLUMN_LOCATION:
|
||||
return false;
|
||||
case COLUMN_ADAPTER:
|
||||
return true;
|
||||
case COLUMN_ENABLED:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int columnIndex) {
|
||||
return COLUMN_NAMES[columnIndex];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getColumnClass(int columnIndex) {
|
||||
return COLUMN_CLASSES[columnIndex];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (!isRelevantUpdate(evt)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LocalPeripheralEntry entry = (LocalPeripheralEntry) evt.getSource();
|
||||
int index = entries.indexOf(entry);
|
||||
SwingUtilities.invokeLater(() -> fireTableRowsUpdated(index, index));
|
||||
|
||||
}
|
||||
|
||||
private boolean isRelevantUpdate(PropertyChangeEvent evt) {
|
||||
if (!(evt.getSource() instanceof LocalPeripheralEntry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Objects.equals(
|
||||
evt.getPropertyName(),
|
||||
LocalPeripheralEntry.Attribute.ATTACHED_COMM_ADAPTER.name()
|
||||
)) {
|
||||
PeripheralCommAdapterDescription oldInfo
|
||||
= (PeripheralCommAdapterDescription) evt.getOldValue();
|
||||
PeripheralCommAdapterDescription newInfo
|
||||
= (PeripheralCommAdapterDescription) evt.getNewValue();
|
||||
return !oldInfo.equals(newInfo);
|
||||
}
|
||||
if (Objects.equals(
|
||||
evt.getPropertyName(),
|
||||
LocalPeripheralEntry.Attribute.PROCESS_MODEL.name()
|
||||
)) {
|
||||
PeripheralProcessModel oldTo = (PeripheralProcessModel) evt.getOldValue();
|
||||
PeripheralProcessModel newTo = (PeripheralProcessModel) evt.getNewValue();
|
||||
return oldTo.isCommAdapterEnabled() != newTo.isCommAdapterEnabled();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.8" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<AccessibilityProperties>
|
||||
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="peripheralsPanel.accessibleName" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</AccessibilityProperties>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_formBundle" type="java.lang.String" value="i18n/de/fraunhofer/iml/opentcs/parkplus/controlcenterpanels/peripherals/TransferCabinsPanel"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,-87,0,0,3,116"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="listPanel">
|
||||
<Properties>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
|
||||
<TitledBorder title="Peripheral devices in model"/>
|
||||
</Border>
|
||||
</Property>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[464, 2147483647]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[464, 425]"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[464, 424]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="filterPanel">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="North"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout">
|
||||
<Property name="alignment" type="int" value="0"/>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JCheckBox" name="hideDetachedLocationsCheckBox">
|
||||
<Properties>
|
||||
<Property name="selected" type="boolean" value="true"/>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="peripheralsPanel.checkBox_hideDetachedLocations.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="hideDetachedLocationsCheckBoxActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="Center"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTable" name="peripheralTable">
|
||||
<Events>
|
||||
<EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="peripheralTableMouseClicked"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="peripheralDetailsPanel">
|
||||
<Properties>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
|
||||
<TitledBorder title="Peripheral details"/>
|
||||
</Border>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[800, 23]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
@@ -0,0 +1,389 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter.peripherals;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.opentcs.kernelcontrolcenter.I18nKernelControlCenter.BUNDLE_PATH;
|
||||
import static org.opentcs.util.Assertions.checkState;
|
||||
|
||||
import jakarta.annotation.Nonnull;
|
||||
import jakarta.inject.Inject;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.ResourceBundle;
|
||||
import javax.swing.DefaultCellEditor;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.RowFilter;
|
||||
import javax.swing.table.TableRowSorter;
|
||||
import org.opentcs.access.Kernel;
|
||||
import org.opentcs.access.KernelServicePortal;
|
||||
import org.opentcs.common.peripherals.NullPeripheralCommAdapterDescription;
|
||||
import org.opentcs.components.kernelcontrolcenter.ControlCenterPanel;
|
||||
import org.opentcs.customizations.ServiceCallWrapper;
|
||||
import org.opentcs.drivers.peripherals.PeripheralCommAdapterDescription;
|
||||
import org.opentcs.drivers.peripherals.management.PeripheralAttachmentInformation;
|
||||
import org.opentcs.kernelcontrolcenter.util.SingleCellEditor;
|
||||
import org.opentcs.util.CallWrapper;
|
||||
import org.opentcs.util.gui.BoundsPopupMenuListener;
|
||||
import org.opentcs.util.gui.StringTableCellRenderer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A panel containing all locations representing peripheral devices.
|
||||
*/
|
||||
public class PeripheralsPanel
|
||||
extends
|
||||
ControlCenterPanel {
|
||||
|
||||
/**
|
||||
* This class's logger.
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PeripheralsPanel.class);
|
||||
/**
|
||||
* The service portal to use for kernel interaction.
|
||||
*/
|
||||
private final KernelServicePortal servicePortal;
|
||||
/**
|
||||
* The call wrapper to use.
|
||||
*/
|
||||
private final CallWrapper callWrapper;
|
||||
/**
|
||||
* The pool of peripheral devices.
|
||||
*/
|
||||
private final LocalPeripheralEntryPool peripheralEntryPool;
|
||||
/**
|
||||
* The details panel.
|
||||
*/
|
||||
private final PeripheralDetailPanel detailPanel;
|
||||
/**
|
||||
* The table row sorter to use.
|
||||
*/
|
||||
private TableRowSorter<PeripheralTableModel> sorter;
|
||||
/**
|
||||
* This instance's resource bundle.
|
||||
*/
|
||||
private final ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_PATH);
|
||||
/**
|
||||
* Whether this component is initialized.
|
||||
*/
|
||||
private boolean initialized;
|
||||
|
||||
/**
|
||||
* Creates new instance.
|
||||
*
|
||||
* @param servicePortal The service portal to use for kernel interaction.
|
||||
* @param callWrapper The call wrapper to use for publishing events to the kernel.
|
||||
* @param peripheralEntryPool The pool of peripheral devices.
|
||||
* @param detailPanel The details panel.
|
||||
*/
|
||||
@Inject
|
||||
@SuppressWarnings("this-escape")
|
||||
public PeripheralsPanel(
|
||||
@Nonnull
|
||||
KernelServicePortal servicePortal,
|
||||
@Nonnull
|
||||
@ServiceCallWrapper
|
||||
CallWrapper callWrapper,
|
||||
@Nonnull
|
||||
LocalPeripheralEntryPool peripheralEntryPool,
|
||||
@Nonnull
|
||||
PeripheralDetailPanel detailPanel
|
||||
) {
|
||||
this.servicePortal = requireNonNull(servicePortal, "servicePortal");
|
||||
this.callWrapper = requireNonNull(callWrapper, "callWrapper");
|
||||
this.peripheralEntryPool = requireNonNull(peripheralEntryPool, "peripheralEntryPool");
|
||||
this.detailPanel = requireNonNull(detailPanel, "detailPanel");
|
||||
|
||||
initComponents();
|
||||
|
||||
peripheralTable.setDefaultRenderer(
|
||||
PeripheralCommAdapterDescription.class,
|
||||
new CommAdapterFactoryTableCellRenderer()
|
||||
);
|
||||
|
||||
peripheralDetailsPanel.add(detailPanel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
if (isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify that the kernel is in a state in which controlling peripherals is possible.
|
||||
Kernel.State kernelState;
|
||||
try {
|
||||
kernelState = callWrapper.call(() -> servicePortal.getState());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LOG.warn("Error getting the kernel state", ex);
|
||||
return;
|
||||
}
|
||||
checkState(
|
||||
Kernel.State.OPERATING.equals(kernelState),
|
||||
"Cannot work in kernel state %s",
|
||||
kernelState
|
||||
);
|
||||
|
||||
peripheralEntryPool.initialize();
|
||||
detailPanel.initialize();
|
||||
|
||||
EventQueue.invokeLater(() -> {
|
||||
initPeripheralTable();
|
||||
});
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void terminate() {
|
||||
if (!isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
detailPanel.terminate();
|
||||
peripheralEntryPool.terminate();
|
||||
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
private void initPeripheralTable() {
|
||||
PeripheralTableModel model = new PeripheralTableModel(servicePortal, callWrapper);
|
||||
peripheralTable.setModel(model);
|
||||
peripheralTable.getColumnModel().getColumn(PeripheralTableModel.COLUMN_ADAPTER).setCellRenderer(
|
||||
new StringTableCellRenderer<PeripheralCommAdapterDescription>(
|
||||
description -> description.getDescription()
|
||||
)
|
||||
);
|
||||
sorter = new TableRowSorter<>(model);
|
||||
peripheralTable.setRowSorter(sorter);
|
||||
|
||||
peripheralEntryPool.getEntries().forEach((location, entry) -> {
|
||||
model.addData(entry);
|
||||
entry.addPropertyChangeListener(model);
|
||||
});
|
||||
|
||||
initComboBoxes();
|
||||
updateRowFilter();
|
||||
}
|
||||
|
||||
private void initComboBoxes() {
|
||||
SingleCellEditor adpaterCellEditor = new SingleCellEditor(peripheralTable);
|
||||
|
||||
int index = 0;
|
||||
for (LocalPeripheralEntry entry : peripheralEntryPool.getEntries().values()) {
|
||||
initCommAdaptersComboBox(entry, index, adpaterCellEditor);
|
||||
index++;
|
||||
}
|
||||
|
||||
peripheralTable.getColumn(PeripheralTableModel.adapterColumnIdentifier())
|
||||
.setCellEditor(adpaterCellEditor);
|
||||
}
|
||||
|
||||
private void initCommAdaptersComboBox(
|
||||
LocalPeripheralEntry peripheralEntry,
|
||||
int rowIndex,
|
||||
SingleCellEditor adapterCellEditor
|
||||
) {
|
||||
final PeripheralAdapterComboBox comboBox = new PeripheralAdapterComboBox();
|
||||
PeripheralAttachmentInformation ai;
|
||||
try {
|
||||
ai = callWrapper.call(
|
||||
() -> servicePortal.getPeripheralService().fetchAttachmentInformation(
|
||||
peripheralEntry.getLocation()
|
||||
)
|
||||
);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LOG.warn(
|
||||
"Error fetching attachment information for {}",
|
||||
peripheralEntry.getLocation().getName(),
|
||||
ex
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
for (PeripheralCommAdapterDescription factory : ai.getAvailableCommAdapters()) {
|
||||
comboBox.addItem(factory);
|
||||
}
|
||||
if (ai.getAvailableCommAdapters().isEmpty()) {
|
||||
comboBox.addItem(new NullPeripheralCommAdapterDescription());
|
||||
}
|
||||
|
||||
// Set the selection to the attached comm adapter. (The peripheral is already attached to a comm
|
||||
// adapter due to auto attachment on startup.)
|
||||
comboBox.setSelectedItem(ai.getAttachedCommAdapter());
|
||||
|
||||
comboBox.setRenderer(new AdapterFactoryCellRenderer());
|
||||
comboBox.addPopupMenuListener(new BoundsPopupMenuListener());
|
||||
comboBox.addItemListener((ItemEvent evt) -> {
|
||||
if (evt.getStateChange() == ItemEvent.DESELECTED) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we selected a comm adapter that's already attached, do nothing.
|
||||
if (Objects.equals(evt.getItem(), peripheralEntry.getAttachedCommAdapter())) {
|
||||
LOG.debug(
|
||||
"{} is already attached to: {}",
|
||||
peripheralEntry.getLocation().getName(),
|
||||
evt.getItem()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
int reply = JOptionPane.showConfirmDialog(
|
||||
null,
|
||||
bundle.getString("peripheralsPanel.optionPane_driverChangeConfirmation.message"),
|
||||
bundle.getString("peripheralsPanel.optionPane_driverChangeConfirmation.title"),
|
||||
JOptionPane.YES_NO_OPTION
|
||||
);
|
||||
if (reply == JOptionPane.NO_OPTION) {
|
||||
return;
|
||||
}
|
||||
|
||||
PeripheralCommAdapterDescription factory = comboBox.getSelectedItem();
|
||||
try {
|
||||
callWrapper.call(
|
||||
() -> servicePortal.getPeripheralService().attachCommAdapter(
|
||||
peripheralEntry.getLocation(),
|
||||
factory
|
||||
)
|
||||
);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LOG.warn(
|
||||
"Error attaching adapter {} to vehicle {}",
|
||||
factory,
|
||||
peripheralEntry.getLocation().getName(),
|
||||
ex
|
||||
);
|
||||
return;
|
||||
}
|
||||
LOG.info("Attaching comm adapter {} to {}", factory, peripheralEntry.getLocation().getName());
|
||||
});
|
||||
adapterCellEditor.setEditorAt(rowIndex, new DefaultCellEditor(comboBox));
|
||||
|
||||
peripheralEntry.addPropertyChangeListener(comboBox);
|
||||
}
|
||||
|
||||
private void updateRowFilter() {
|
||||
sorter.setRowFilter(RowFilter.andFilter(toRegexFilters()));
|
||||
}
|
||||
|
||||
private List<RowFilter<PeripheralTableModel, Integer>> toRegexFilters() {
|
||||
List<RowFilter<PeripheralTableModel, Integer>> result = new ArrayList<>();
|
||||
result.add(RowFilter.regexFilter(".*", PeripheralTableModel.COLUMN_ADAPTER));
|
||||
if (hideDetachedLocationsCheckBox.isSelected()) {
|
||||
result.add(
|
||||
RowFilter.notFilter(
|
||||
RowFilter.regexFilter(
|
||||
NullPeripheralCommAdapterDescription.class.getSimpleName(),
|
||||
PeripheralTableModel.COLUMN_ADAPTER
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// FORMATTER:OFF
|
||||
// CHECKSTYLE:OFF
|
||||
/**
|
||||
* This method is called from within the constructor to
|
||||
* initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is
|
||||
* always regenerated by the Form Editor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
listPanel = new javax.swing.JPanel();
|
||||
filterPanel = new javax.swing.JPanel();
|
||||
hideDetachedLocationsCheckBox = new javax.swing.JCheckBox();
|
||||
jScrollPane1 = new javax.swing.JScrollPane();
|
||||
peripheralTable = new javax.swing.JTable();
|
||||
peripheralDetailsPanel = new javax.swing.JPanel();
|
||||
|
||||
setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.LINE_AXIS));
|
||||
|
||||
listPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Peripheral devices in model"));
|
||||
listPanel.setMaximumSize(new java.awt.Dimension(464, 2147483647));
|
||||
listPanel.setMinimumSize(new java.awt.Dimension(464, 425));
|
||||
listPanel.setPreferredSize(new java.awt.Dimension(464, 424));
|
||||
listPanel.setLayout(new java.awt.BorderLayout());
|
||||
|
||||
filterPanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT));
|
||||
|
||||
hideDetachedLocationsCheckBox.setSelected(true);
|
||||
java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("i18n/org/opentcs/kernelcontrolcenter/Bundle"); // NOI18N
|
||||
hideDetachedLocationsCheckBox.setText(bundle.getString("peripheralsPanel.checkBox_hideDetachedLocations.text")); // NOI18N
|
||||
hideDetachedLocationsCheckBox.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
hideDetachedLocationsCheckBoxActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
filterPanel.add(hideDetachedLocationsCheckBox);
|
||||
|
||||
listPanel.add(filterPanel, java.awt.BorderLayout.NORTH);
|
||||
|
||||
peripheralTable.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||
public void mouseClicked(java.awt.event.MouseEvent evt) {
|
||||
peripheralTableMouseClicked(evt);
|
||||
}
|
||||
});
|
||||
jScrollPane1.setViewportView(peripheralTable);
|
||||
|
||||
listPanel.add(jScrollPane1, java.awt.BorderLayout.CENTER);
|
||||
|
||||
add(listPanel);
|
||||
|
||||
peripheralDetailsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Peripheral details"));
|
||||
peripheralDetailsPanel.setPreferredSize(new java.awt.Dimension(800, 23));
|
||||
peripheralDetailsPanel.setLayout(new java.awt.BorderLayout());
|
||||
add(peripheralDetailsPanel);
|
||||
|
||||
getAccessibleContext().setAccessibleName(bundle.getString("peripheralsPanel.accessibleName")); // NOI18N
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
// CHECKSTYLE:ON
|
||||
// FORMATTER:ON
|
||||
|
||||
private void peripheralTableMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_peripheralTableMouseClicked
|
||||
if (evt.getClickCount() == 2) {
|
||||
int index = peripheralTable.getSelectedRow();
|
||||
if (index >= 0) {
|
||||
PeripheralTableModel model = (PeripheralTableModel) peripheralTable.getModel();
|
||||
LocalPeripheralEntry selectedEntry
|
||||
= model.getDataAt(peripheralTable.convertRowIndexToModel(index));
|
||||
detailPanel.attachToEntry(selectedEntry);
|
||||
}
|
||||
}
|
||||
}//GEN-LAST:event_peripheralTableMouseClicked
|
||||
|
||||
private void hideDetachedLocationsCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_hideDetachedLocationsCheckBoxActionPerformed
|
||||
updateRowFilter();
|
||||
}//GEN-LAST:event_hideDetachedLocationsCheckBoxActionPerformed
|
||||
|
||||
// FORMATTER:OFF
|
||||
// CHECKSTYLE:OFF
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JPanel filterPanel;
|
||||
private javax.swing.JCheckBox hideDetachedLocationsCheckBox;
|
||||
private javax.swing.JScrollPane jScrollPane1;
|
||||
private javax.swing.JPanel listPanel;
|
||||
private javax.swing.JPanel peripheralDetailsPanel;
|
||||
private javax.swing.JTable peripheralTable;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
// CHECKSTYLE:ON
|
||||
// FORMATTER:ON
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter.util;
|
||||
|
||||
import java.util.List;
|
||||
import org.opentcs.configuration.ConfigurationEntry;
|
||||
import org.opentcs.configuration.ConfigurationPrefix;
|
||||
import org.opentcs.util.gui.dialog.ConnectionParamSet;
|
||||
|
||||
/**
|
||||
* Provides methods to configure the KernelControlCenter application.
|
||||
*/
|
||||
@ConfigurationPrefix(KernelControlCenterConfiguration.PREFIX)
|
||||
public interface KernelControlCenterConfiguration {
|
||||
|
||||
/**
|
||||
* This configuration's prefix.
|
||||
*/
|
||||
String PREFIX = "kernelcontrolcenter";
|
||||
|
||||
@ConfigurationEntry(
|
||||
type = "String",
|
||||
description = {"The kernel control center application's locale, as a BCP 47 language tag.",
|
||||
"Examples: 'en', 'de', 'zh'"},
|
||||
changesApplied = ConfigurationEntry.ChangesApplied.ON_APPLICATION_START,
|
||||
orderKey = "0_init_0"
|
||||
)
|
||||
String locale();
|
||||
|
||||
@ConfigurationEntry(
|
||||
type = "Comma-separated list of <description>\\|<hostname>\\|<port>",
|
||||
description = "Kernel connection bookmarks to be used.",
|
||||
changesApplied = ConfigurationEntry.ChangesApplied.ON_APPLICATION_START,
|
||||
orderKey = "1_connection_0"
|
||||
)
|
||||
List<ConnectionParamSet> connectionBookmarks();
|
||||
|
||||
@ConfigurationEntry(
|
||||
type = "Boolean",
|
||||
description = {"Whether to automatically connect to the kernel on startup.",
|
||||
"If 'true', the first connection bookmark will be used for the initial "
|
||||
+ "connection attempt.",
|
||||
"If 'false', a dialog will be shown to enter connection parameters."},
|
||||
changesApplied = ConfigurationEntry.ChangesApplied.ON_APPLICATION_START,
|
||||
orderKey = "1_connection_1"
|
||||
)
|
||||
boolean connectAutomaticallyOnStartup();
|
||||
|
||||
@ConfigurationEntry(
|
||||
type = "Integer",
|
||||
description = "The maximum number of characters in the logging text area.",
|
||||
changesApplied = ConfigurationEntry.ChangesApplied.INSTANTLY,
|
||||
orderKey = "9_misc_0"
|
||||
)
|
||||
int loggingAreaCapacity();
|
||||
|
||||
@ConfigurationEntry(
|
||||
type = "Boolean",
|
||||
description = "Whether to enable and show the panel for peripheral drivers.",
|
||||
changesApplied = ConfigurationEntry.ChangesApplied.ON_APPLICATION_START,
|
||||
orderKey = "9_misc_1"
|
||||
)
|
||||
boolean enablePeripheralsPanel();
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter.util;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.EventObject;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.swing.DefaultCellEditor;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.event.CellEditorListener;
|
||||
import javax.swing.table.TableCellEditor;
|
||||
|
||||
/**
|
||||
* A cell editor for maintaining different editors in one column.
|
||||
*/
|
||||
public final class SingleCellEditor
|
||||
implements
|
||||
TableCellEditor {
|
||||
|
||||
/**
|
||||
* The TableCellEditors for every cell.
|
||||
*/
|
||||
private final Map<Integer, TableCellEditor> editors;
|
||||
/**
|
||||
* The current editor.
|
||||
*/
|
||||
private TableCellEditor editor;
|
||||
/**
|
||||
* The default editor.
|
||||
*/
|
||||
private final TableCellEditor defaultEditor;
|
||||
/**
|
||||
* The table associated with the editors.
|
||||
*/
|
||||
private final JTable table;
|
||||
|
||||
/**
|
||||
* Constructs a SingleCellEditor.
|
||||
*
|
||||
* @param table The JTable associated
|
||||
*/
|
||||
public SingleCellEditor(JTable table) {
|
||||
this.table = table;
|
||||
editors = new HashMap<>();
|
||||
defaultEditor = new DefaultCellEditor(new JTextField());
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns an editor to a row.
|
||||
*
|
||||
* @param row table row
|
||||
* @param rowEditor table cell editor
|
||||
*/
|
||||
public void setEditorAt(int row, TableCellEditor rowEditor) {
|
||||
editors.put(row, rowEditor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellEditorComponent(
|
||||
JTable whichTable,
|
||||
Object value,
|
||||
boolean isSelected,
|
||||
int row,
|
||||
int column
|
||||
) {
|
||||
return editor.getTableCellEditorComponent(
|
||||
whichTable,
|
||||
value,
|
||||
isSelected,
|
||||
row,
|
||||
column
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCellEditorValue() {
|
||||
return editor.getCellEditorValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stopCellEditing() {
|
||||
return editor.stopCellEditing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelCellEditing() {
|
||||
editor.cancelCellEditing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCellEditable(EventObject anEvent) {
|
||||
if (anEvent instanceof KeyEvent) {
|
||||
return false;
|
||||
}
|
||||
selectEditor((MouseEvent) anEvent);
|
||||
return editor.isCellEditable(anEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCellEditorListener(CellEditorListener l) {
|
||||
editor.addCellEditorListener(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCellEditorListener(CellEditorListener l) {
|
||||
editor.removeCellEditorListener(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldSelectCell(EventObject anEvent) {
|
||||
selectEditor((MouseEvent) anEvent);
|
||||
return editor.shouldSelectCell(anEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current editor.
|
||||
*
|
||||
* @param e A MouseEvent
|
||||
*/
|
||||
public void selectEditor(MouseEvent e) {
|
||||
int row;
|
||||
if (e == null) {
|
||||
row = table.getSelectionModel().getAnchorSelectionIndex();
|
||||
}
|
||||
else {
|
||||
row = table.convertRowIndexToModel(table.rowAtPoint(e.getPoint()));
|
||||
}
|
||||
editor = editors.get(row);
|
||||
if (editor == null) {
|
||||
editor = defaultEditor;
|
||||
}
|
||||
table.changeSelection(row, table.getColumn("Adapter").getModelIndex(), false, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter.vehicles;
|
||||
|
||||
import java.awt.Component;
|
||||
import javax.swing.DefaultListCellRenderer;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.ListCellRenderer;
|
||||
import org.opentcs.drivers.vehicle.VehicleCommAdapterDescription;
|
||||
|
||||
/**
|
||||
* ListCellRenderer for the adapter combo box.
|
||||
*/
|
||||
final class AdapterFactoryCellRenderer
|
||||
implements
|
||||
ListCellRenderer<VehicleCommAdapterDescription> {
|
||||
|
||||
/**
|
||||
* A default renderer for creating the label.
|
||||
*/
|
||||
private final DefaultListCellRenderer defaultRenderer = new DefaultListCellRenderer();
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
AdapterFactoryCellRenderer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getListCellRendererComponent(
|
||||
JList<? extends VehicleCommAdapterDescription> list,
|
||||
VehicleCommAdapterDescription value,
|
||||
int index,
|
||||
boolean isSelected,
|
||||
boolean cellHasFocus
|
||||
) {
|
||||
JLabel label = (JLabel) defaultRenderer.getListCellRendererComponent(
|
||||
list,
|
||||
value,
|
||||
index,
|
||||
isSelected,
|
||||
cellHasFocus
|
||||
);
|
||||
if (value != null) {
|
||||
label.setText(value.getDescription());
|
||||
}
|
||||
else {
|
||||
label.setText(" ");
|
||||
}
|
||||
return label;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter.vehicles;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Objects;
|
||||
import javax.swing.JComboBox;
|
||||
import org.opentcs.drivers.vehicle.VehicleCommAdapterDescription;
|
||||
|
||||
/**
|
||||
* A wide combobox which sets the selected item when receiving an update event from a
|
||||
* {@link LocalVehicleEntry}.
|
||||
*/
|
||||
public class CommAdapterComboBox
|
||||
extends
|
||||
JComboBox<VehicleCommAdapterDescription>
|
||||
implements
|
||||
PropertyChangeListener {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public CommAdapterComboBox() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public VehicleCommAdapterDescription getSelectedItem() {
|
||||
return (VehicleCommAdapterDescription) super.getSelectedItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (!(evt.getSource() instanceof LocalVehicleEntry)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LocalVehicleEntry entry = (LocalVehicleEntry) evt.getSource();
|
||||
if (Objects.equals(entry.getAttachedCommAdapterDescription(), getModel().getSelectedItem())) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.setSelectedItem(entry.getAttachedCommAdapterDescription());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,392 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.6" maxVersion="1.6" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<NonVisualComponents>
|
||||
<Container class="javax.swing.JPopupMenu" name="logPopupMenu">
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout">
|
||||
<Property name="useNullLayout" type="boolean" value="true"/>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<MenuItem class="javax.swing.JMenuItem" name="clearMenuItem">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="detailPanel.popupMenu_messageDetails.menuItem_clear.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="clearMenuItemActionPerformed"/>
|
||||
</Events>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
</MenuItem>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPopupMenu" name="loggingTablePopupMenu">
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout">
|
||||
<Property name="useNullLayout" type="boolean" value="true"/>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Menu class="javax.swing.JMenu" name="filterMenu">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="detailPanel.popupMenu_messagesTable.subMenu_filter.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
<Property name="actionCommand" type="java.lang.String" value=" message filtering"/>
|
||||
</Properties>
|
||||
<SubComponents>
|
||||
<MenuItem class="javax.swing.JCheckBoxMenuItem" name="everythingCheckBoxMenuItem">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="detailPanel.popupMenu_messagesTable.subMenu_filter.menuItem_showAll.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="everythingCheckBoxMenuItemActionPerformed"/>
|
||||
</Events>
|
||||
</MenuItem>
|
||||
<MenuItem class="javax.swing.JCheckBoxMenuItem" name="warningsCheckBoxMenuItem">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="detailPanel.popupMenu_messagesTable.subMenu_filter.menuItem_showErrorsAndWarnings.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="warningsCheckBoxMenuItemActionPerformed"/>
|
||||
</Events>
|
||||
</MenuItem>
|
||||
<MenuItem class="javax.swing.JCheckBoxMenuItem" name="errorsCheckBoxMenuItem">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="detailPanel.popupMenu_messagesTable.subMenu_filter.menuItem_showErrors.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="errorsCheckBoxMenuItemActionPerformed"/>
|
||||
</Events>
|
||||
</MenuItem>
|
||||
</SubComponents>
|
||||
</Menu>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="noVehiclePanel">
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="noVehicleLabel">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
|
||||
<FontInfo relative="true">
|
||||
<Font component="noVehicleLabel" property="font" relativeSize="true" size="3"/>
|
||||
</FontInfo>
|
||||
</Property>
|
||||
<Property name="horizontalAlignment" type="int" value="0"/>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="detailPanel.label_noVehicleAttached.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="Center"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</NonVisualComponents>
|
||||
<Properties>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
|
||||
<TitledBorder title="<User Code>">
|
||||
<Connection PropertyName="titleX" code="DEFAULT_BORDER_TITLE" type="code"/>
|
||||
</TitledBorder>
|
||||
</Border>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="2"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,-63,0,0,2,-123"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JTabbedPane" name="tabbedPane">
|
||||
<Properties>
|
||||
<Property name="tabLayoutPolicy" type="int" value="1"/>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="Center"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="overviewTabPanel">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
|
||||
<JTabbedPaneConstraints tabName="General status">
|
||||
<Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="detailPanel.tab_generalStatus.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</JTabbedPaneConstraints>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="headPanel">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="statusPanel">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="West"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="adapterStatusPanel">
|
||||
<Properties>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
|
||||
<TitledBorder title="Adapter status">
|
||||
<ResourceString PropertyName="titleX" bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="detailPanel.panel_adapterStatus.border.title" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</TitledBorder>
|
||||
</Border>
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JCheckBox" name="chkBoxEnable">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="detailPanel.checkBox_enableAdapter.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
<Property name="enabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="chkBoxEnableActionPerformed"/>
|
||||
</Events>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="Center"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="statusFiguresPanel">
|
||||
<Properties>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
|
||||
<TitledBorder title="Vehicle status">
|
||||
<ResourceString PropertyName="titleX" bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="detailPanel.panel_vehicleStatus.border.title" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</TitledBorder>
|
||||
</Border>
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="curPosLbl">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="detailPanel.label_currentPosition.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="0" anchor="13" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="curPosTxt">
|
||||
<Properties>
|
||||
<Property name="editable" type="boolean" value="false"/>
|
||||
<Property name="columns" type="int" value="9"/>
|
||||
<Property name="horizontalAlignment" type="int" value="4"/>
|
||||
<Property name="text" type="java.lang.String" value="Point-0001"/>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="curStateLbl">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="detailPanel.label_currentState.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="3" insetsBottom="0" insetsRight="0" anchor="13" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="curStateTxt">
|
||||
<Properties>
|
||||
<Property name="editable" type="boolean" value="false"/>
|
||||
<Property name="columns" type="int" value="9"/>
|
||||
<Property name="horizontalAlignment" type="int" value="4"/>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="Vehicle.State.UNKNOWN.name()" type="code"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="1" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="3" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="fillingLbl">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="logoPanel">
|
||||
<Properties>
|
||||
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="ff" green="ff" red="ff" type="rgb"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="Center"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="logoLbl">
|
||||
<Properties>
|
||||
<Property name="horizontalAlignment" type="int" value="0"/>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/opentcs/kernelcontrolcenter/res/logos/opentcs_logo.gif"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="Center"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="logPanel">
|
||||
<Properties>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
|
||||
<TitledBorder title="Messages">
|
||||
<ResourceString PropertyName="titleX" bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="detailPanel.panel_messages.border.title" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</TitledBorder>
|
||||
</Border>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[468, 200]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="1.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="logTableScrollPane">
|
||||
<Properties>
|
||||
<Property name="componentPopupMenu" type="javax.swing.JPopupMenu" editor="org.netbeans.modules.form.ComponentChooserEditor">
|
||||
<ComponentRef name="loggingTablePopupMenu"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="Center"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTable" name="loggingTable">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.editors2.TableModelEditor">
|
||||
<Table columnCount="2" rowCount="0">
|
||||
<Column editable="false" title="Time stamp" type="java.lang.Object"/>
|
||||
<Column editable="false" title="Message" type="java.lang.Object"/>
|
||||
</Table>
|
||||
</Property>
|
||||
<Property name="componentPopupMenu" type="javax.swing.JPopupMenu" editor="org.netbeans.modules.form.ComponentChooserEditor">
|
||||
<ComponentRef name="loggingTablePopupMenu"/>
|
||||
</Property>
|
||||
<Property name="selectionModel" type="javax.swing.ListSelectionModel" editor="org.netbeans.modules.form.editors2.JTableSelectionModelEditor">
|
||||
<JTableSelectionModel selectionMode="0"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JScrollPane" name="logTextScrollPane">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="South"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTextArea" name="loggingTextArea">
|
||||
<Properties>
|
||||
<Property name="editable" type="boolean" value="false"/>
|
||||
<Property name="columns" type="int" value="20"/>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
|
||||
<Font name="Courier New" size="12" style="0"/>
|
||||
</Property>
|
||||
<Property name="lineWrap" type="boolean" value="true"/>
|
||||
<Property name="rows" type="int" value="3"/>
|
||||
<Property name="componentPopupMenu" type="javax.swing.JPopupMenu" editor="org.netbeans.modules.form.ComponentChooserEditor">
|
||||
<ComponentRef name="logPopupMenu"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
@@ -0,0 +1,736 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter.vehicles;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.FormatStyle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.border.TitledBorder;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import org.opentcs.access.KernelServicePortal;
|
||||
import org.opentcs.components.Lifecycle;
|
||||
import org.opentcs.customizations.ApplicationEventBus;
|
||||
import org.opentcs.customizations.ServiceCallWrapper;
|
||||
import org.opentcs.data.model.Vehicle;
|
||||
import org.opentcs.data.notification.UserNotification;
|
||||
import org.opentcs.drivers.vehicle.VehicleProcessModel;
|
||||
import org.opentcs.drivers.vehicle.management.ProcessModelEvent;
|
||||
import org.opentcs.drivers.vehicle.management.VehicleAttachmentEvent;
|
||||
import org.opentcs.drivers.vehicle.management.VehicleAttachmentInformation;
|
||||
import org.opentcs.drivers.vehicle.management.VehicleCommAdapterPanel;
|
||||
import org.opentcs.drivers.vehicle.management.VehicleCommAdapterPanelFactory;
|
||||
import org.opentcs.util.CallWrapper;
|
||||
import org.opentcs.util.event.EventHandler;
|
||||
import org.opentcs.util.event.EventSource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Displays information about a vehicle (VehicleModel) graphically.
|
||||
*/
|
||||
public class DetailPanel
|
||||
extends
|
||||
JPanel
|
||||
implements
|
||||
EventHandler,
|
||||
Lifecycle {
|
||||
|
||||
/**
|
||||
* This class's logger.
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DetailPanel.class);
|
||||
/**
|
||||
* A panel's default border title.
|
||||
*/
|
||||
private static final String DEFAULT_BORDER_TITLE = "";
|
||||
/**
|
||||
* A <code>DateFormat</code> instance for formatting message's time stamps.
|
||||
*/
|
||||
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter
|
||||
.ofLocalizedDateTime(FormatStyle.SHORT)
|
||||
.withLocale(Locale.getDefault())
|
||||
.withZone(ZoneId.systemDefault());
|
||||
/**
|
||||
* The adapter specific list of JPanels.
|
||||
*/
|
||||
private final List<VehicleCommAdapterPanel> customPanelList = new ArrayList<>();
|
||||
/**
|
||||
* The set of factories to create adapter specific panels.
|
||||
*/
|
||||
private final Set<VehicleCommAdapterPanelFactory> panelFactories;
|
||||
/**
|
||||
* The logging table model to use.
|
||||
*/
|
||||
private final LogTableModel loggingTableModel = new LogTableModel();
|
||||
/**
|
||||
* Where this instance registers for application events.
|
||||
*/
|
||||
private final EventSource eventSource;
|
||||
/**
|
||||
* The service portal to use for kernel interaction.
|
||||
*/
|
||||
private final KernelServicePortal servicePortal;
|
||||
/**
|
||||
* The call wrapper to use for service calls.
|
||||
*/
|
||||
private final CallWrapper callWrapper;
|
||||
/**
|
||||
* The vehicle model of the vehicle current associated with this window.
|
||||
*/
|
||||
private LocalVehicleEntry vehicleEntry;
|
||||
/**
|
||||
* The comm adapter currently attached to the vehicle (model).
|
||||
*/
|
||||
private VehicleAttachmentInformation attachmentInfo;
|
||||
/**
|
||||
* Whether this panel is initialized or not.
|
||||
*/
|
||||
private boolean initialized;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param servicePortal The service portal to use for kernel interaction.
|
||||
* @param callWrapper The call wrapper to use for service calls.
|
||||
* @param eventSource Where this instance registers for application events.
|
||||
* @param panelFactories The factories to create adapter specific panels.
|
||||
*/
|
||||
@Inject
|
||||
@SuppressWarnings("this-escape")
|
||||
public DetailPanel(
|
||||
KernelServicePortal servicePortal,
|
||||
@ServiceCallWrapper
|
||||
CallWrapper callWrapper,
|
||||
@ApplicationEventBus
|
||||
EventSource eventSource,
|
||||
Set<VehicleCommAdapterPanelFactory> panelFactories
|
||||
) {
|
||||
this.servicePortal = requireNonNull(servicePortal, "servicePortal");
|
||||
this.callWrapper = requireNonNull(callWrapper, "callWrapper");
|
||||
this.eventSource = requireNonNull(eventSource, "eventSource");
|
||||
this.panelFactories = requireNonNull(panelFactories, "panelFactories");
|
||||
|
||||
initComponents();
|
||||
|
||||
loggingTable.setModel(loggingTableModel);
|
||||
loggingTable.getColumnModel().getColumn(0).setPreferredWidth(40);
|
||||
loggingTable.getColumnModel().getColumn(1).setPreferredWidth(110);
|
||||
loggingTable.getSelectionModel().addListSelectionListener(new RowListener());
|
||||
// Make sure we start with an empty panel.
|
||||
detachFromVehicle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
if (isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (VehicleCommAdapterPanelFactory fatory : panelFactories) {
|
||||
fatory.initialize();
|
||||
}
|
||||
|
||||
eventSource.subscribe(this);
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void terminate() {
|
||||
if (!isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
detachFromVehicle();
|
||||
|
||||
eventSource.unsubscribe(this);
|
||||
|
||||
for (VehicleCommAdapterPanelFactory fatory : panelFactories) {
|
||||
fatory.terminate();
|
||||
}
|
||||
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(Object e) {
|
||||
// Ignore events if no vehicle entry is associated with this panel.
|
||||
if (vehicleEntry == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e instanceof VehicleAttachmentEvent) {
|
||||
VehicleAttachmentEvent event = (VehicleAttachmentEvent) e;
|
||||
if (Objects.equals(
|
||||
vehicleEntry.getVehicleName(),
|
||||
event.getVehicleName()
|
||||
)) {
|
||||
updateFromVehicleEntry(event);
|
||||
}
|
||||
}
|
||||
if (e instanceof ProcessModelEvent) {
|
||||
ProcessModelEvent event = (ProcessModelEvent) e;
|
||||
if (Objects.equals(
|
||||
vehicleEntry.getVehicleName(),
|
||||
event.getUpdatedProcessModel().getName()
|
||||
)) {
|
||||
updateFromVehicleProcessModel(event);
|
||||
|
||||
// Forward event to the comm adapter panels
|
||||
customPanelList.forEach(
|
||||
panel -> panel.processModelChange(
|
||||
event.getAttributeChanged(),
|
||||
event.getUpdatedProcessModel()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches this panel to a vehicle.
|
||||
*
|
||||
* @param newVehicleEntry The vehicle entry to attach to.
|
||||
*/
|
||||
void attachToVehicle(LocalVehicleEntry newVehicleEntry) {
|
||||
requireNonNull(newVehicleEntry, "newVehicleEntry");
|
||||
|
||||
// Clean up first - but only if we're not reattaching the vehicle model which is already
|
||||
// attached to this panel.
|
||||
if (vehicleEntry != newVehicleEntry) {
|
||||
detachFromVehicle();
|
||||
}
|
||||
vehicleEntry = newVehicleEntry;
|
||||
|
||||
setBorderTitle(vehicleEntry.getVehicleName());
|
||||
|
||||
// Ensure the tabbed pane containing vehicle information is shown.
|
||||
removeAll();
|
||||
add(tabbedPane);
|
||||
|
||||
// Init vehicle status.
|
||||
loggingTableModel.clear();
|
||||
for (UserNotification notification : vehicleEntry.getProcessModel().getNotifications()) {
|
||||
loggingTableModel.addRow(notification);
|
||||
}
|
||||
initVehicleEntryAttributes();
|
||||
|
||||
// Update panel contents.
|
||||
validate();
|
||||
tabbedPane.setSelectedIndex(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches this panel from a vehicle (if it is currently attached to any).
|
||||
*/
|
||||
private void detachFromVehicle() {
|
||||
if (vehicleEntry != null) {
|
||||
// Remove all custom panels and let the comm adapter know we don't need them any more.
|
||||
removeCustomPanels();
|
||||
if (attachmentInfo != null) {
|
||||
attachmentInfo = null;
|
||||
}
|
||||
customPanelList.clear();
|
||||
vehicleEntry = null;
|
||||
}
|
||||
// Clear the log message table.
|
||||
loggingTableModel.clear();
|
||||
setBorderTitle(DEFAULT_BORDER_TITLE);
|
||||
// Remove the contents of this panel.
|
||||
removeAll();
|
||||
add(noVehiclePanel);
|
||||
// Update panel contents.
|
||||
validate();
|
||||
}
|
||||
|
||||
private void initVehicleEntryAttributes() {
|
||||
updateCommAdapter(vehicleEntry.getAttachmentInformation());
|
||||
updateCommAdapterEnabled(vehicleEntry.getProcessModel().isCommAdapterEnabled());
|
||||
updateVehiclePosition(vehicleEntry.getProcessModel().getPosition());
|
||||
updateVehicleState(vehicleEntry.getProcessModel().getState());
|
||||
}
|
||||
|
||||
private void updateFromVehicleEntry(VehicleAttachmentEvent evt) {
|
||||
updateCommAdapter(evt.getAttachmentInformation());
|
||||
}
|
||||
|
||||
private void updateFromVehicleProcessModel(ProcessModelEvent evt) {
|
||||
if (Objects.equals(
|
||||
evt.getAttributeChanged(),
|
||||
VehicleProcessModel.Attribute.COMM_ADAPTER_ENABLED.name()
|
||||
)) {
|
||||
updateCommAdapterEnabled(evt.getUpdatedProcessModel().isCommAdapterEnabled());
|
||||
}
|
||||
else if (Objects.equals(
|
||||
evt.getAttributeChanged(),
|
||||
VehicleProcessModel.Attribute.POSITION.name()
|
||||
)) {
|
||||
updateVehiclePosition(evt.getUpdatedProcessModel().getPosition());
|
||||
}
|
||||
else if (Objects.equals(
|
||||
evt.getAttributeChanged(),
|
||||
VehicleProcessModel.Attribute.STATE.name()
|
||||
)) {
|
||||
updateVehicleState(evt.getUpdatedProcessModel().getState());
|
||||
}
|
||||
else if (Objects.equals(
|
||||
evt.getAttributeChanged(),
|
||||
VehicleProcessModel.Attribute.USER_NOTIFICATION.name()
|
||||
)) {
|
||||
updateUserNotification(evt.getUpdatedProcessModel().getNotifications());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCommAdapter(VehicleAttachmentInformation newAttachmentInfo) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
// If there was a comm adapter and it changed, we need to clean up a few things first.
|
||||
if (attachmentInfo != null) {
|
||||
// Detach all custom panels of the old comm adapter.
|
||||
removeCustomPanels();
|
||||
customPanelList.clear();
|
||||
}
|
||||
// Update the comm adapter reference.
|
||||
attachmentInfo = newAttachmentInfo;
|
||||
// If we have a new comm adapter, set up a few things.
|
||||
if (attachmentInfo != null) {
|
||||
// Update the custom panels displayed.
|
||||
updateCustomPanels();
|
||||
}
|
||||
chkBoxEnable.setEnabled(attachmentInfo != null);
|
||||
chkBoxEnable.setSelected(
|
||||
attachmentInfo != null
|
||||
&& vehicleEntry.getProcessModel().isCommAdapterEnabled()
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private void updateCommAdapterEnabled(boolean enabled) {
|
||||
SwingUtilities.invokeLater(() -> chkBoxEnable.setSelected(enabled));
|
||||
}
|
||||
|
||||
private void updateVehiclePosition(String position) {
|
||||
SwingUtilities.invokeLater(() -> curPosTxt.setText(position));
|
||||
}
|
||||
|
||||
private void updateVehicleState(Vehicle.State state) {
|
||||
SwingUtilities.invokeLater(() -> curStateTxt.setText(state.toString()));
|
||||
}
|
||||
|
||||
private void updateUserNotification(Queue<UserNotification> notifications) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
loggingTableModel.clear();
|
||||
notifications.forEach(notification -> loggingTableModel.addRow(notification));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the list of custom panels in the tabbed pane.
|
||||
*/
|
||||
private void updateCustomPanels() {
|
||||
for (VehicleCommAdapterPanel curPanel : customPanelList) {
|
||||
LOG.debug("Removing {} from tabbedPane.", curPanel);
|
||||
tabbedPane.remove(curPanel);
|
||||
}
|
||||
customPanelList.clear();
|
||||
if (attachmentInfo != null) {
|
||||
for (VehicleCommAdapterPanelFactory panelFactory : panelFactories) {
|
||||
customPanelList.addAll(
|
||||
panelFactory.getPanelsFor(
|
||||
vehicleEntry.getAttachedCommAdapterDescription(),
|
||||
vehicleEntry.getAttachmentInformation().getVehicleReference(),
|
||||
vehicleEntry.getProcessModel()
|
||||
)
|
||||
);
|
||||
for (VehicleCommAdapterPanel curPanel : customPanelList) {
|
||||
LOG.debug("Adding {} with title {} to tabbedPane.", curPanel, curPanel.getTitle());
|
||||
tabbedPane.addTab(curPanel.getTitle(), curPanel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the custom panels from this panel's tabbed pane.
|
||||
*/
|
||||
private void removeCustomPanels() {
|
||||
LOG.debug("Setting selected component of tabbedPane to overviewTabPanel.");
|
||||
tabbedPane.setSelectedComponent(overviewTabPanel);
|
||||
for (VehicleCommAdapterPanel panel : customPanelList) {
|
||||
LOG.debug("Removing {} from tabbedPane.", panel);
|
||||
tabbedPane.remove(panel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this panel's border title.
|
||||
*
|
||||
* @param newTitle This panel's new border title.
|
||||
*/
|
||||
private void setBorderTitle(String newTitle) {
|
||||
requireNonNull(newTitle, "newTitle");
|
||||
((TitledBorder) getBorder()).setTitle(newTitle);
|
||||
// Trigger a repaint - the title sometimes looks strange otherwise.
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method appends the selected notification to the text area in the log tab.
|
||||
*
|
||||
* @param row The selected row in the table.
|
||||
*/
|
||||
private void outputLogNotification(int row) {
|
||||
UserNotification message = loggingTableModel.getRow(row);
|
||||
String timestamp = DATE_FORMAT.format(message.getTimestamp());
|
||||
String output = timestamp + " (" + message.getLevel() + "):\n" + message.getText();
|
||||
loggingTextArea.setText(output);
|
||||
}
|
||||
|
||||
// FORMATTER:OFF
|
||||
// CHECKSTYLE:OFF
|
||||
/**
|
||||
* This method is called from within the constructor to
|
||||
* initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is
|
||||
* always regenerated by the Form Editor.
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
java.awt.GridBagConstraints gridBagConstraints;
|
||||
|
||||
logPopupMenu = new javax.swing.JPopupMenu();
|
||||
clearMenuItem = new javax.swing.JMenuItem();
|
||||
loggingTablePopupMenu = new javax.swing.JPopupMenu();
|
||||
filterMenu = new javax.swing.JMenu();
|
||||
everythingCheckBoxMenuItem = new javax.swing.JCheckBoxMenuItem();
|
||||
warningsCheckBoxMenuItem = new javax.swing.JCheckBoxMenuItem();
|
||||
errorsCheckBoxMenuItem = new javax.swing.JCheckBoxMenuItem();
|
||||
noVehiclePanel = new javax.swing.JPanel();
|
||||
noVehicleLabel = new javax.swing.JLabel();
|
||||
tabbedPane = new javax.swing.JTabbedPane();
|
||||
overviewTabPanel = new javax.swing.JPanel();
|
||||
headPanel = new javax.swing.JPanel();
|
||||
statusPanel = new javax.swing.JPanel();
|
||||
adapterStatusPanel = new javax.swing.JPanel();
|
||||
chkBoxEnable = new javax.swing.JCheckBox();
|
||||
statusFiguresPanel = new javax.swing.JPanel();
|
||||
curPosLbl = new javax.swing.JLabel();
|
||||
curPosTxt = new javax.swing.JTextField();
|
||||
curStateLbl = new javax.swing.JLabel();
|
||||
curStateTxt = new javax.swing.JTextField();
|
||||
fillingLbl = new javax.swing.JLabel();
|
||||
logoPanel = new javax.swing.JPanel();
|
||||
logoLbl = new javax.swing.JLabel();
|
||||
logPanel = new javax.swing.JPanel();
|
||||
logTableScrollPane = new javax.swing.JScrollPane();
|
||||
loggingTable = new javax.swing.JTable();
|
||||
logTextScrollPane = new javax.swing.JScrollPane();
|
||||
loggingTextArea = new javax.swing.JTextArea();
|
||||
|
||||
java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("i18n/org/opentcs/kernelcontrolcenter/Bundle"); // NOI18N
|
||||
clearMenuItem.setText(bundle.getString("detailPanel.popupMenu_messageDetails.menuItem_clear.text")); // NOI18N
|
||||
clearMenuItem.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
clearMenuItemActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
logPopupMenu.add(clearMenuItem);
|
||||
|
||||
filterMenu.setText(bundle.getString("detailPanel.popupMenu_messagesTable.subMenu_filter.text")); // NOI18N
|
||||
filterMenu.setActionCommand(" message filtering");
|
||||
|
||||
everythingCheckBoxMenuItem.setText(bundle.getString("detailPanel.popupMenu_messagesTable.subMenu_filter.menuItem_showAll.text")); // NOI18N
|
||||
everythingCheckBoxMenuItem.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
everythingCheckBoxMenuItemActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
filterMenu.add(everythingCheckBoxMenuItem);
|
||||
|
||||
warningsCheckBoxMenuItem.setText(bundle.getString("detailPanel.popupMenu_messagesTable.subMenu_filter.menuItem_showErrorsAndWarnings.text")); // NOI18N
|
||||
warningsCheckBoxMenuItem.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
warningsCheckBoxMenuItemActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
filterMenu.add(warningsCheckBoxMenuItem);
|
||||
|
||||
errorsCheckBoxMenuItem.setText(bundle.getString("detailPanel.popupMenu_messagesTable.subMenu_filter.menuItem_showErrors.text")); // NOI18N
|
||||
errorsCheckBoxMenuItem.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
errorsCheckBoxMenuItemActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
filterMenu.add(errorsCheckBoxMenuItem);
|
||||
|
||||
loggingTablePopupMenu.add(filterMenu);
|
||||
|
||||
noVehiclePanel.setLayout(new java.awt.BorderLayout());
|
||||
|
||||
noVehicleLabel.setFont(noVehicleLabel.getFont().deriveFont(noVehicleLabel.getFont().getSize()+3f));
|
||||
noVehicleLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
|
||||
noVehicleLabel.setText(bundle.getString("detailPanel.label_noVehicleAttached.text")); // NOI18N
|
||||
noVehiclePanel.add(noVehicleLabel, java.awt.BorderLayout.CENTER);
|
||||
|
||||
setBorder(javax.swing.BorderFactory.createTitledBorder(DEFAULT_BORDER_TITLE));
|
||||
setLayout(new java.awt.BorderLayout());
|
||||
|
||||
tabbedPane.setTabLayoutPolicy(javax.swing.JTabbedPane.SCROLL_TAB_LAYOUT);
|
||||
|
||||
overviewTabPanel.setLayout(new java.awt.GridBagLayout());
|
||||
|
||||
headPanel.setLayout(new java.awt.BorderLayout());
|
||||
|
||||
statusPanel.setLayout(new javax.swing.BoxLayout(statusPanel, javax.swing.BoxLayout.LINE_AXIS));
|
||||
|
||||
adapterStatusPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(bundle.getString("detailPanel.panel_adapterStatus.border.title"))); // NOI18N
|
||||
adapterStatusPanel.setLayout(new java.awt.BorderLayout());
|
||||
|
||||
chkBoxEnable.setText(bundle.getString("detailPanel.checkBox_enableAdapter.text")); // NOI18N
|
||||
chkBoxEnable.setEnabled(false);
|
||||
chkBoxEnable.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
chkBoxEnableActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
adapterStatusPanel.add(chkBoxEnable, java.awt.BorderLayout.CENTER);
|
||||
|
||||
statusPanel.add(adapterStatusPanel);
|
||||
|
||||
statusFiguresPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(bundle.getString("detailPanel.panel_vehicleStatus.border.title"))); // NOI18N
|
||||
statusFiguresPanel.setLayout(new java.awt.GridBagLayout());
|
||||
|
||||
curPosLbl.setText(bundle.getString("detailPanel.label_currentPosition.text")); // NOI18N
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
|
||||
gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 0);
|
||||
statusFiguresPanel.add(curPosLbl, gridBagConstraints);
|
||||
|
||||
curPosTxt.setEditable(false);
|
||||
curPosTxt.setColumns(9);
|
||||
curPosTxt.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
|
||||
curPosTxt.setText("Point-0001");
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 0);
|
||||
statusFiguresPanel.add(curPosTxt, gridBagConstraints);
|
||||
|
||||
curStateLbl.setText(bundle.getString("detailPanel.label_currentState.text")); // NOI18N
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 1;
|
||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
|
||||
gridBagConstraints.insets = new java.awt.Insets(3, 3, 0, 0);
|
||||
statusFiguresPanel.add(curStateLbl, gridBagConstraints);
|
||||
|
||||
curStateTxt.setEditable(false);
|
||||
curStateTxt.setColumns(9);
|
||||
curStateTxt.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
|
||||
curStateTxt.setText(Vehicle.State.UNKNOWN.name());
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 1;
|
||||
gridBagConstraints.gridy = 1;
|
||||
gridBagConstraints.insets = new java.awt.Insets(3, 3, 0, 0);
|
||||
statusFiguresPanel.add(curStateTxt, gridBagConstraints);
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.weightx = 1.0;
|
||||
statusFiguresPanel.add(fillingLbl, gridBagConstraints);
|
||||
|
||||
statusPanel.add(statusFiguresPanel);
|
||||
|
||||
headPanel.add(statusPanel, java.awt.BorderLayout.WEST);
|
||||
|
||||
logoPanel.setBackground(new java.awt.Color(255, 255, 255));
|
||||
logoPanel.setLayout(new java.awt.BorderLayout());
|
||||
|
||||
logoLbl.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
|
||||
logoLbl.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/opentcs/kernelcontrolcenter/res/logos/opentcs_logo.gif"))); // NOI18N
|
||||
logoPanel.add(logoLbl, java.awt.BorderLayout.CENTER);
|
||||
|
||||
headPanel.add(logoPanel, java.awt.BorderLayout.CENTER);
|
||||
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||
overviewTabPanel.add(headPanel, gridBagConstraints);
|
||||
|
||||
logPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(bundle.getString("detailPanel.panel_messages.border.title"))); // NOI18N
|
||||
logPanel.setPreferredSize(new java.awt.Dimension(468, 200));
|
||||
logPanel.setLayout(new java.awt.BorderLayout());
|
||||
|
||||
logTableScrollPane.setComponentPopupMenu(loggingTablePopupMenu);
|
||||
|
||||
loggingTable.setModel(new javax.swing.table.DefaultTableModel(
|
||||
new Object [][] {
|
||||
|
||||
},
|
||||
new String [] {
|
||||
"Time stamp", "Message"
|
||||
}
|
||||
) {
|
||||
boolean[] canEdit = new boolean [] {
|
||||
false, false
|
||||
};
|
||||
|
||||
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
||||
return canEdit [columnIndex];
|
||||
}
|
||||
});
|
||||
loggingTable.setComponentPopupMenu(loggingTablePopupMenu);
|
||||
loggingTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
|
||||
logTableScrollPane.setViewportView(loggingTable);
|
||||
|
||||
logPanel.add(logTableScrollPane, java.awt.BorderLayout.CENTER);
|
||||
|
||||
loggingTextArea.setEditable(false);
|
||||
loggingTextArea.setColumns(20);
|
||||
loggingTextArea.setFont(new java.awt.Font("Courier New", 0, 12)); // NOI18N
|
||||
loggingTextArea.setLineWrap(true);
|
||||
loggingTextArea.setRows(3);
|
||||
loggingTextArea.setComponentPopupMenu(logPopupMenu);
|
||||
logTextScrollPane.setViewportView(loggingTextArea);
|
||||
|
||||
logPanel.add(logTextScrollPane, java.awt.BorderLayout.SOUTH);
|
||||
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 1;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
|
||||
gridBagConstraints.weightx = 1.0;
|
||||
gridBagConstraints.weighty = 1.0;
|
||||
overviewTabPanel.add(logPanel, gridBagConstraints);
|
||||
|
||||
tabbedPane.addTab(bundle.getString("detailPanel.tab_generalStatus.text"), overviewTabPanel); // NOI18N
|
||||
|
||||
add(tabbedPane, java.awt.BorderLayout.CENTER);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
// CHECKSTYLE:ON
|
||||
// FORMATTER:ON
|
||||
|
||||
private void warningsCheckBoxMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_warningsCheckBoxMenuItemActionPerformed
|
||||
loggingTableModel.filterMessages(
|
||||
(notification) -> notification.getLevel().equals(UserNotification.Level.IMPORTANT)
|
||||
|| notification.getLevel().equals(UserNotification.Level.NOTEWORTHY)
|
||||
);
|
||||
warningsCheckBoxMenuItem.setSelected(true);
|
||||
errorsCheckBoxMenuItem.setSelected(false);
|
||||
everythingCheckBoxMenuItem.setSelected(false);
|
||||
}//GEN-LAST:event_warningsCheckBoxMenuItemActionPerformed
|
||||
|
||||
private void everythingCheckBoxMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_everythingCheckBoxMenuItemActionPerformed
|
||||
loggingTableModel.filterMessages((notification) -> true);
|
||||
everythingCheckBoxMenuItem.setSelected(true);
|
||||
errorsCheckBoxMenuItem.setSelected(false);
|
||||
warningsCheckBoxMenuItem.setSelected(false);
|
||||
}//GEN-LAST:event_everythingCheckBoxMenuItemActionPerformed
|
||||
|
||||
private void errorsCheckBoxMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_errorsCheckBoxMenuItemActionPerformed
|
||||
loggingTableModel.filterMessages(
|
||||
(notification) -> notification.getLevel().equals(UserNotification.Level.IMPORTANT)
|
||||
);
|
||||
errorsCheckBoxMenuItem.setSelected(true);
|
||||
everythingCheckBoxMenuItem.setSelected(false);
|
||||
warningsCheckBoxMenuItem.setSelected(false);
|
||||
}//GEN-LAST:event_errorsCheckBoxMenuItemActionPerformed
|
||||
|
||||
private void clearMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clearMenuItemActionPerformed
|
||||
loggingTextArea.setText("");
|
||||
}//GEN-LAST:event_clearMenuItemActionPerformed
|
||||
|
||||
private void chkBoxEnableActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chkBoxEnableActionPerformed
|
||||
try {
|
||||
if (chkBoxEnable.isSelected()) {
|
||||
callWrapper.call(
|
||||
() -> servicePortal.getVehicleService()
|
||||
.enableCommAdapter(vehicleEntry.getAttachmentInformation().getVehicleReference())
|
||||
);
|
||||
}
|
||||
else {
|
||||
callWrapper.call(
|
||||
() -> servicePortal.getVehicleService()
|
||||
.disableCommAdapter(vehicleEntry.getAttachmentInformation().getVehicleReference())
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LOG.warn("Error enabling/disabling comm adapter for {}", vehicleEntry.getVehicleName(), ex);
|
||||
}
|
||||
}//GEN-LAST:event_chkBoxEnableActionPerformed
|
||||
|
||||
// FORMATTER:OFF
|
||||
// CHECKSTYLE:OFF
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JPanel adapterStatusPanel;
|
||||
private javax.swing.JCheckBox chkBoxEnable;
|
||||
private javax.swing.JMenuItem clearMenuItem;
|
||||
private javax.swing.JLabel curPosLbl;
|
||||
private javax.swing.JTextField curPosTxt;
|
||||
private javax.swing.JLabel curStateLbl;
|
||||
private javax.swing.JTextField curStateTxt;
|
||||
private javax.swing.JCheckBoxMenuItem errorsCheckBoxMenuItem;
|
||||
private javax.swing.JCheckBoxMenuItem everythingCheckBoxMenuItem;
|
||||
private javax.swing.JLabel fillingLbl;
|
||||
private javax.swing.JMenu filterMenu;
|
||||
private javax.swing.JPanel headPanel;
|
||||
private javax.swing.JPanel logPanel;
|
||||
private javax.swing.JPopupMenu logPopupMenu;
|
||||
private javax.swing.JScrollPane logTableScrollPane;
|
||||
private javax.swing.JScrollPane logTextScrollPane;
|
||||
private javax.swing.JTable loggingTable;
|
||||
private javax.swing.JPopupMenu loggingTablePopupMenu;
|
||||
private javax.swing.JTextArea loggingTextArea;
|
||||
private javax.swing.JLabel logoLbl;
|
||||
private javax.swing.JPanel logoPanel;
|
||||
private javax.swing.JLabel noVehicleLabel;
|
||||
private javax.swing.JPanel noVehiclePanel;
|
||||
private javax.swing.JPanel overviewTabPanel;
|
||||
private javax.swing.JPanel statusFiguresPanel;
|
||||
private javax.swing.JPanel statusPanel;
|
||||
private javax.swing.JTabbedPane tabbedPane;
|
||||
private javax.swing.JCheckBoxMenuItem warningsCheckBoxMenuItem;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
// CHECKSTYLE:ON
|
||||
// FORMATTER:ON
|
||||
|
||||
/**
|
||||
* A <code>ListSelectionListener</code> for handling the logging table selection events.
|
||||
*/
|
||||
private final class RowListener
|
||||
implements
|
||||
ListSelectionListener {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
private RowListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent event) {
|
||||
if (event.getValueIsAdjusting()) {
|
||||
return;
|
||||
}
|
||||
if (loggingTable.getSelectedRow() >= 0) {
|
||||
outputLogNotification(loggingTable.getSelectedRow());
|
||||
}
|
||||
else {
|
||||
loggingTextArea.setText("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.3" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<NonVisualComponents>
|
||||
<Container class="javax.swing.JPopupMenu" name="vehicleListPopupMenu">
|
||||
<Events>
|
||||
<EventHandler event="popupMenuWillBecomeVisible" listener="javax.swing.event.PopupMenuListener" parameters="javax.swing.event.PopupMenuEvent" handler="vehicleListPopupMenuPopupMenuWillBecomeVisible"/>
|
||||
</Events>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout">
|
||||
<Property name="useNullLayout" type="boolean" value="true"/>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Menu class="javax.swing.JMenu" name="driverMenu">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="driverGui.popupMenu_vehicles.subMenu_driver.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="menuSelected" listener="javax.swing.event.MenuListener" parameters="javax.swing.event.MenuEvent" handler="driverMenuMenuSelected"/>
|
||||
</Events>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
<SubComponents>
|
||||
<MenuItem class="javax.swing.JMenuItem" name="noDriversMenuItem">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="No drivers available."/>
|
||||
<Property name="enabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
</MenuItem>
|
||||
</SubComponents>
|
||||
</Menu>
|
||||
<Component class="javax.swing.JSeparator" name="jSeparator1">
|
||||
</Component>
|
||||
<MenuItem class="javax.swing.JMenuItem" name="enableAllMenuItem">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="driverGui.popupMenu_vehicles.menuItem_enableAll.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="enableAllMenuItemActionPerformed"/>
|
||||
</Events>
|
||||
</MenuItem>
|
||||
<MenuItem class="javax.swing.JMenuItem" name="enableAllSelectedMenuItem">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="driverGui.popupMenu_vehicles.menuItem_enableSelected.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="enableAllSelectedMenuItemActionPerformed"/>
|
||||
</Events>
|
||||
</MenuItem>
|
||||
<Component class="javax.swing.JSeparator" name="jSeparator4">
|
||||
</Component>
|
||||
<MenuItem class="javax.swing.JMenuItem" name="disableAllMenuItem">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="driverGui.popupMenu_vehicles.menuItem_disableAll.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="disableAllMenuItemActionPerformed"/>
|
||||
</Events>
|
||||
</MenuItem>
|
||||
<MenuItem class="javax.swing.JMenuItem" name="disableAllSelectedMenuItem">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="driverGui.popupMenu_vehicles.menuItem_disableSelected.text" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="disableAllSelectedMenuItemActionPerformed"/>
|
||||
</Events>
|
||||
</MenuItem>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</NonVisualComponents>
|
||||
<AccessibilityProperties>
|
||||
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="driverGui.accessibleName" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</AccessibilityProperties>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,2,-104,0,0,4,99"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
|
||||
<Property name="axis" type="int" value="0"/>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="listDisplayPanel">
|
||||
<Properties>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
|
||||
<TitledBorder title="Vehicles in model">
|
||||
<ResourceString PropertyName="titleX" bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="driverGui.panel_vehicles.border.title" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</TitledBorder>
|
||||
</Border>
|
||||
</Property>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[464, 2147483647]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[464, 425]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="Center"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTable" name="vehicleTable">
|
||||
<Properties>
|
||||
<Property name="componentPopupMenu" type="javax.swing.JPopupMenu" editor="org.netbeans.modules.form.ComponentChooserEditor">
|
||||
<ComponentRef name="vehicleListPopupMenu"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="vehicleTableMouseClicked"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="vehicleDetailPanel">
|
||||
<Properties>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
|
||||
<TitledBorder title="Vehicle details">
|
||||
<ResourceString PropertyName="titleX" bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="driverGui.panel_vehicleDetails.border.title" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</TitledBorder>
|
||||
</Border>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[800, 23]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AccessibilityProperties>
|
||||
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="i18n/org/opentcs/kernelcontrolcenter/Bundle.properties" key="driverGui.panel_vehicleDetails.accessibleName" replaceFormat="java.util.ResourceBundle.getBundle("{bundleNameSlashes}").getString("{key}")"/>
|
||||
</Property>
|
||||
</AccessibilityProperties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
@@ -0,0 +1,682 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter.vehicles;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.opentcs.kernelcontrolcenter.I18nKernelControlCenter.BUNDLE_PATH;
|
||||
import static org.opentcs.util.Assertions.checkState;
|
||||
|
||||
import jakarta.annotation.Nonnull;
|
||||
import jakarta.inject.Inject;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.DefaultCellEditor;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JOptionPane;
|
||||
import org.opentcs.access.Kernel;
|
||||
import org.opentcs.access.KernelServicePortal;
|
||||
import org.opentcs.components.kernelcontrolcenter.ControlCenterPanel;
|
||||
import org.opentcs.customizations.ServiceCallWrapper;
|
||||
import org.opentcs.data.model.Point;
|
||||
import org.opentcs.drivers.vehicle.VehicleCommAdapterDescription;
|
||||
import org.opentcs.drivers.vehicle.commands.InitPositionCommand;
|
||||
import org.opentcs.drivers.vehicle.management.VehicleAttachmentInformation;
|
||||
import org.opentcs.drivers.vehicle.management.VehicleProcessModelTO;
|
||||
import org.opentcs.kernelcontrolcenter.util.SingleCellEditor;
|
||||
import org.opentcs.util.CallWrapper;
|
||||
import org.opentcs.util.Comparators;
|
||||
import org.opentcs.util.gui.BoundsPopupMenuListener;
|
||||
import org.opentcs.util.gui.StringListCellRenderer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A panel containing all vehicles and detailed information.
|
||||
*/
|
||||
public class DriverGUI
|
||||
extends
|
||||
ControlCenterPanel {
|
||||
|
||||
/**
|
||||
* This class's Logger.
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DriverGUI.class);
|
||||
/**
|
||||
* This instance's resource bundle.
|
||||
*/
|
||||
private final ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_PATH);
|
||||
/**
|
||||
* The service portal to use for kernel interaction.
|
||||
*/
|
||||
private final KernelServicePortal servicePortal;
|
||||
/**
|
||||
* The call wrapper to use for service calls.
|
||||
*/
|
||||
private final CallWrapper callWrapper;
|
||||
/**
|
||||
* The pool of vehicle entries.
|
||||
*/
|
||||
private final LocalVehicleEntryPool vehicleEntryPool;
|
||||
/**
|
||||
* The detail panel to dispay when selecting a vehicle.
|
||||
*/
|
||||
private final DetailPanel detailPanel;
|
||||
/**
|
||||
* Whether this panel is initialized or not.
|
||||
*/
|
||||
private boolean initialized;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param servicePortal The service portal to use for kernel interaction.
|
||||
* @param callWrapper The call wrapper to use for service calls.
|
||||
* @param vehicleEntryPool The pool of vehicle entries.
|
||||
* @param detailPanel The detail panel to display.
|
||||
*/
|
||||
@Inject
|
||||
@SuppressWarnings("this-escape")
|
||||
public DriverGUI(
|
||||
@Nonnull
|
||||
KernelServicePortal servicePortal,
|
||||
@Nonnull
|
||||
@ServiceCallWrapper
|
||||
CallWrapper callWrapper,
|
||||
@Nonnull
|
||||
LocalVehicleEntryPool vehicleEntryPool,
|
||||
@Nonnull
|
||||
DetailPanel detailPanel
|
||||
) {
|
||||
this.servicePortal = requireNonNull(servicePortal, "servicePortal");
|
||||
this.callWrapper = requireNonNull(callWrapper, "callWrapper");
|
||||
this.vehicleEntryPool = requireNonNull(vehicleEntryPool, "vehicleEntryPool");
|
||||
this.detailPanel = requireNonNull(detailPanel, "detailPanel");
|
||||
|
||||
initComponents();
|
||||
|
||||
vehicleTable.setDefaultRenderer(
|
||||
VehicleCommAdapterDescription.class,
|
||||
new VehicleCommAdapterFactoryTableCellRenderer()
|
||||
);
|
||||
vehicleDetailPanel.add(detailPanel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
if (initialized) {
|
||||
LOG.debug("Already initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify that the kernel is in a state in which controlling vehicles is possible.
|
||||
Kernel.State kernelState;
|
||||
try {
|
||||
kernelState = callWrapper.call(() -> servicePortal.getState());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LOG.warn("Error getting the kernel state", ex);
|
||||
return;
|
||||
}
|
||||
checkState(
|
||||
Kernel.State.OPERATING.equals(kernelState),
|
||||
"Cannot work in kernel state %s",
|
||||
kernelState
|
||||
);
|
||||
|
||||
vehicleEntryPool.initialize();
|
||||
detailPanel.initialize();
|
||||
|
||||
EventQueue.invokeLater(() -> {
|
||||
initVehicleList();
|
||||
});
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void terminate() {
|
||||
if (!initialized) {
|
||||
LOG.debug("Not initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
detailPanel.terminate();
|
||||
vehicleEntryPool.terminate();
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
private void initVehicleList() {
|
||||
vehicleTable.setModel(new VehicleTableModel(servicePortal.getVehicleService(), callWrapper));
|
||||
VehicleTableModel model = (VehicleTableModel) vehicleTable.getModel();
|
||||
|
||||
vehicleEntryPool.getEntries().forEach((vehicleName, entry) -> {
|
||||
model.addData(entry);
|
||||
entry.addPropertyChangeListener(model);
|
||||
});
|
||||
|
||||
vehicleTable.getComponentPopupMenu().setEnabled(!model.getVehicleEntries().isEmpty());
|
||||
|
||||
initAdapterComboBoxes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the combo boxes with available adapters for every vehicle.
|
||||
*/
|
||||
private void initAdapterComboBoxes() {
|
||||
SingleCellEditor adapterCellEditor = new SingleCellEditor(vehicleTable);
|
||||
SingleCellEditor pointsCellEditor = new SingleCellEditor(vehicleTable);
|
||||
|
||||
int index = 0;
|
||||
for (LocalVehicleEntry entry : vehicleEntryPool.getEntries().values()) {
|
||||
initCommAdaptersComboBox(entry, index, adapterCellEditor);
|
||||
initPointsComboBox(index, pointsCellEditor);
|
||||
index++;
|
||||
}
|
||||
|
||||
vehicleTable.getColumn(VehicleTableModel.adapterColumnIdentifier())
|
||||
.setCellEditor(adapterCellEditor);
|
||||
vehicleTable.getColumn(VehicleTableModel.positionColumnIdentifier())
|
||||
.setCellEditor(pointsCellEditor);
|
||||
}
|
||||
|
||||
private void initCommAdaptersComboBox(
|
||||
LocalVehicleEntry vehicleEntry,
|
||||
int rowIndex,
|
||||
SingleCellEditor adapterCellEditor
|
||||
) {
|
||||
final CommAdapterComboBox comboBox = new CommAdapterComboBox();
|
||||
VehicleAttachmentInformation ai;
|
||||
try {
|
||||
ai = callWrapper.call(
|
||||
() -> servicePortal.getVehicleService().fetchAttachmentInformation(
|
||||
vehicleEntry.getAttachmentInformation().getVehicleReference()
|
||||
)
|
||||
);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LOG.warn("Error fetching attachment information for {}", vehicleEntry.getVehicleName(), ex);
|
||||
return;
|
||||
}
|
||||
ai.getAvailableCommAdapters().forEach(factory -> comboBox.addItem(factory));
|
||||
|
||||
// Set the selection to the attached comm adapter, (The vehicle is already attached to a comm
|
||||
// adapter due to auto attachment on startup.)
|
||||
comboBox.setSelectedItem(vehicleEntry.getAttachmentInformation().getAttachedCommAdapter());
|
||||
|
||||
comboBox.setRenderer(new AdapterFactoryCellRenderer());
|
||||
comboBox.addPopupMenuListener(new BoundsPopupMenuListener());
|
||||
comboBox.addItemListener((ItemEvent evt) -> {
|
||||
if (evt.getStateChange() == ItemEvent.DESELECTED) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we selected a comm adapter that's already attached, do nothing.
|
||||
if (Objects.equals(evt.getItem(), vehicleEntry.getAttachedCommAdapterDescription())) {
|
||||
LOG.debug("{} is already attached to: {}", vehicleEntry.getVehicleName(), evt.getItem());
|
||||
return;
|
||||
}
|
||||
|
||||
int reply = JOptionPane.showConfirmDialog(
|
||||
null,
|
||||
bundle.getString("driverGui.optionPane_driverChangeConfirmation.message"),
|
||||
bundle.getString("driverGui.optionPane_driverChangeConfirmation.title"),
|
||||
JOptionPane.YES_NO_OPTION
|
||||
);
|
||||
if (reply == JOptionPane.NO_OPTION) {
|
||||
return;
|
||||
}
|
||||
|
||||
VehicleCommAdapterDescription factory = comboBox.getSelectedItem();
|
||||
try {
|
||||
callWrapper.call(
|
||||
() -> servicePortal.getVehicleService().attachCommAdapter(
|
||||
vehicleEntry.getAttachmentInformation().getVehicleReference(), factory
|
||||
)
|
||||
);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LOG.warn(
|
||||
"Error attaching adapter {} to vehicle {}",
|
||||
factory,
|
||||
vehicleEntry.getVehicleName(),
|
||||
ex
|
||||
);
|
||||
return;
|
||||
}
|
||||
LOG.info("Attaching comm adapter {} to {}", factory, vehicleEntry.getVehicleName());
|
||||
});
|
||||
adapterCellEditor.setEditorAt(rowIndex, new DefaultCellEditor(comboBox));
|
||||
|
||||
vehicleEntry.addPropertyChangeListener(comboBox);
|
||||
}
|
||||
|
||||
/**
|
||||
* If a loopback adapter was chosen, this method initializes the combo boxes with positions the
|
||||
* user can set the vehicle to.
|
||||
*
|
||||
* @param rowIndex An index indicating which row this combo box belongs to
|
||||
* @param pointsCellEditor The <code>SingleCellEditor</code> containing the combo boxes.
|
||||
*/
|
||||
private void initPointsComboBox(int rowIndex, SingleCellEditor pointsCellEditor) {
|
||||
final JComboBox<Point> pointComboBox = new JComboBox<>();
|
||||
|
||||
Set<Point> points;
|
||||
try {
|
||||
points = callWrapper.call(() -> servicePortal.getVehicleService().fetchObjects(Point.class));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LOG.warn("Error fetching points", ex);
|
||||
return;
|
||||
}
|
||||
|
||||
points.stream().sorted(Comparators.objectsByName())
|
||||
.forEach(point -> pointComboBox.addItem(point));
|
||||
pointComboBox.setSelectedIndex(-1);
|
||||
pointComboBox.setRenderer(new StringListCellRenderer<>(x -> x == null ? "" : x.getName()));
|
||||
|
||||
pointComboBox.addItemListener((ItemEvent e) -> {
|
||||
try {
|
||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||
Point newPoint = (Point) e.getItem();
|
||||
LocalVehicleEntry vehicleEntry = vehicleEntryPool.getEntryFor(getSelectedVehicleName());
|
||||
if (vehicleEntry.getAttachedCommAdapterDescription().isSimVehicleCommAdapter()) {
|
||||
callWrapper.call(
|
||||
() -> servicePortal.getVehicleService().sendCommAdapterCommand(
|
||||
vehicleEntry.getAttachmentInformation().getVehicleReference(),
|
||||
new InitPositionCommand(newPoint.getName())
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
LOG.debug(
|
||||
"Vehicle {}: Not a simulation adapter -> not setting initial position.",
|
||||
vehicleEntry.getVehicleName()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LOG.warn("Error sending init position command", ex);
|
||||
}
|
||||
});
|
||||
pointsCellEditor.setEditorAt(rowIndex, new DefaultCellEditor(pointComboBox));
|
||||
}
|
||||
|
||||
private void enableAllCommAdapters() {
|
||||
enableCommAdapters(vehicleEntryPool.getEntries().values());
|
||||
}
|
||||
|
||||
private void enableSelectedCommAdapters() {
|
||||
enableCommAdapters(getSelectedVehicleEntries());
|
||||
}
|
||||
|
||||
private void enableCommAdapters(Collection<LocalVehicleEntry> selectedEntries) {
|
||||
Collection<LocalVehicleEntry> entries = selectedEntries.stream()
|
||||
.filter(entry -> !entry.getProcessModel().isCommAdapterEnabled())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
try {
|
||||
for (LocalVehicleEntry entry : entries) {
|
||||
callWrapper.call(
|
||||
() -> servicePortal.getVehicleService().enableCommAdapter(
|
||||
entry.getAttachmentInformation().getVehicleReference()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LOG.warn("Error enabling comm adapter, canceling", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void disableAllCommAdapters() {
|
||||
disableCommAdapters(vehicleEntryPool.getEntries().values());
|
||||
}
|
||||
|
||||
private void disableSelectedCommAdapters() {
|
||||
disableCommAdapters(getSelectedVehicleEntries());
|
||||
}
|
||||
|
||||
private void disableCommAdapters(Collection<LocalVehicleEntry> selectedEntries) {
|
||||
Collection<LocalVehicleEntry> entries = selectedEntries.stream()
|
||||
.filter(entry -> entry.getProcessModel().isCommAdapterEnabled())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
try {
|
||||
for (LocalVehicleEntry entry : entries) {
|
||||
callWrapper.call(
|
||||
() -> servicePortal.getVehicleService().disableCommAdapter(
|
||||
entry.getAttachmentInformation().getVehicleReference()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LOG.warn("Error disabling comm adapter, canceling", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private String getSelectedVehicleName() {
|
||||
VehicleTableModel model = (VehicleTableModel) vehicleTable.getModel();
|
||||
return model.getDataAt(vehicleTable.getSelectedRow()).getVehicleName();
|
||||
}
|
||||
|
||||
private List<String> getSelectedVehicleNames() {
|
||||
List<String> selectedVehicleNames = new ArrayList<>();
|
||||
VehicleTableModel model = (VehicleTableModel) vehicleTable.getModel();
|
||||
for (int selectedRow : vehicleTable.getSelectedRows()) {
|
||||
String selectedVehicleName = model.getDataAt(selectedRow).getVehicleName();
|
||||
selectedVehicleNames.add(selectedVehicleName);
|
||||
}
|
||||
return selectedVehicleNames;
|
||||
}
|
||||
|
||||
private List<LocalVehicleEntry> getSelectedVehicleEntries() {
|
||||
List<LocalVehicleEntry> selectedEntries = new ArrayList<>();
|
||||
for (String selectedVehicleName : getSelectedVehicleNames()) {
|
||||
selectedEntries.add(vehicleEntryPool.getEntryFor(selectedVehicleName));
|
||||
}
|
||||
return selectedEntries;
|
||||
}
|
||||
|
||||
private void createDriverMenu() {
|
||||
driverMenu.removeAll();
|
||||
|
||||
// Collect all available comm adapters/factories
|
||||
Set<VehicleCommAdapterDescription> availableDescriptions = new HashSet<>();
|
||||
vehicleEntryPool.getEntries().forEach((vehicleName, entry) -> {
|
||||
availableDescriptions.addAll(entry.getAttachmentInformation().getAvailableCommAdapters());
|
||||
});
|
||||
|
||||
for (VehicleCommAdapterDescription description : availableDescriptions) {
|
||||
// If there's one vehicle not supported by this factory the selection can't be attached to it
|
||||
boolean factorySupportsSelectedVehicles = getSelectedVehicleEntries().stream()
|
||||
.map(entry -> entry.getAttachmentInformation().getAvailableCommAdapters())
|
||||
.allMatch(descriptions -> !Collections.disjoint(descriptions, availableDescriptions));
|
||||
|
||||
List<String> vehiclesToAttach = new ArrayList<>();
|
||||
if (factorySupportsSelectedVehicles) {
|
||||
vehiclesToAttach = getSelectedVehicleNames();
|
||||
}
|
||||
|
||||
Action action = new AttachCommAdapterAction(vehiclesToAttach, description);
|
||||
JMenuItem menuItem = driverMenu.add(action);
|
||||
menuItem.setEnabled(!vehiclesToAttach.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
private void createPopupMenu() {
|
||||
// Find out how many vehicles (don't) have a driver attached.
|
||||
StatesCounts stateCounts = getCommAdapterStateCountsFor(vehicleEntryPool.getEntries().values());
|
||||
enableAllMenuItem.setEnabled(stateCounts.disabledCount > 0);
|
||||
disableAllMenuItem.setEnabled(stateCounts.enabledCount > 0);
|
||||
|
||||
// Now do the same for those that are selected.
|
||||
stateCounts = getCommAdapterStateCountsFor(getSelectedVehicleEntries());
|
||||
enableAllSelectedMenuItem.setEnabled(stateCounts.disabledCount > 0);
|
||||
disableAllSelectedMenuItem.setEnabled(stateCounts.enabledCount > 0);
|
||||
}
|
||||
|
||||
private StatesCounts getCommAdapterStateCountsFor(Collection<LocalVehicleEntry> entries) {
|
||||
StatesCounts stateCounts = new StatesCounts();
|
||||
for (LocalVehicleEntry entry : entries) {
|
||||
VehicleProcessModelTO processModel = entry.getProcessModel();
|
||||
if (processModel.isCommAdapterEnabled()) {
|
||||
stateCounts.enabledCount++;
|
||||
}
|
||||
else {
|
||||
stateCounts.disabledCount++;
|
||||
}
|
||||
}
|
||||
return stateCounts;
|
||||
}
|
||||
|
||||
// FORMATTER:OFF
|
||||
// CHECKSTYLE:OFF
|
||||
/**
|
||||
* This method is called from within the constructor to
|
||||
* initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is
|
||||
* always regenerated by the Form Editor.
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
vehicleListPopupMenu = new javax.swing.JPopupMenu();
|
||||
driverMenu = new javax.swing.JMenu();
|
||||
noDriversMenuItem = new javax.swing.JMenuItem();
|
||||
jSeparator1 = new javax.swing.JSeparator();
|
||||
enableAllMenuItem = new javax.swing.JMenuItem();
|
||||
enableAllSelectedMenuItem = new javax.swing.JMenuItem();
|
||||
jSeparator4 = new javax.swing.JSeparator();
|
||||
disableAllMenuItem = new javax.swing.JMenuItem();
|
||||
disableAllSelectedMenuItem = new javax.swing.JMenuItem();
|
||||
listDisplayPanel = new javax.swing.JPanel();
|
||||
jScrollPane1 = new javax.swing.JScrollPane();
|
||||
vehicleTable = new javax.swing.JTable();
|
||||
vehicleDetailPanel = new javax.swing.JPanel();
|
||||
|
||||
vehicleListPopupMenu.addPopupMenuListener(new javax.swing.event.PopupMenuListener() {
|
||||
public void popupMenuCanceled(javax.swing.event.PopupMenuEvent evt) {
|
||||
}
|
||||
public void popupMenuWillBecomeInvisible(javax.swing.event.PopupMenuEvent evt) {
|
||||
}
|
||||
public void popupMenuWillBecomeVisible(javax.swing.event.PopupMenuEvent evt) {
|
||||
vehicleListPopupMenuPopupMenuWillBecomeVisible(evt);
|
||||
}
|
||||
});
|
||||
|
||||
java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("i18n/org/opentcs/kernelcontrolcenter/Bundle"); // NOI18N
|
||||
driverMenu.setText(bundle.getString("driverGui.popupMenu_vehicles.subMenu_driver.text")); // NOI18N
|
||||
driverMenu.addMenuListener(new javax.swing.event.MenuListener() {
|
||||
public void menuCanceled(javax.swing.event.MenuEvent evt) {
|
||||
}
|
||||
public void menuDeselected(javax.swing.event.MenuEvent evt) {
|
||||
}
|
||||
public void menuSelected(javax.swing.event.MenuEvent evt) {
|
||||
driverMenuMenuSelected(evt);
|
||||
}
|
||||
});
|
||||
|
||||
noDriversMenuItem.setText("No drivers available.");
|
||||
noDriversMenuItem.setEnabled(false);
|
||||
driverMenu.add(noDriversMenuItem);
|
||||
|
||||
vehicleListPopupMenu.add(driverMenu);
|
||||
vehicleListPopupMenu.add(jSeparator1);
|
||||
|
||||
enableAllMenuItem.setText(bundle.getString("driverGui.popupMenu_vehicles.menuItem_enableAll.text")); // NOI18N
|
||||
enableAllMenuItem.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
enableAllMenuItemActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
vehicleListPopupMenu.add(enableAllMenuItem);
|
||||
|
||||
enableAllSelectedMenuItem.setText(bundle.getString("driverGui.popupMenu_vehicles.menuItem_enableSelected.text")); // NOI18N
|
||||
enableAllSelectedMenuItem.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
enableAllSelectedMenuItemActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
vehicleListPopupMenu.add(enableAllSelectedMenuItem);
|
||||
vehicleListPopupMenu.add(jSeparator4);
|
||||
|
||||
disableAllMenuItem.setText(bundle.getString("driverGui.popupMenu_vehicles.menuItem_disableAll.text")); // NOI18N
|
||||
disableAllMenuItem.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
disableAllMenuItemActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
vehicleListPopupMenu.add(disableAllMenuItem);
|
||||
|
||||
disableAllSelectedMenuItem.setText(bundle.getString("driverGui.popupMenu_vehicles.menuItem_disableSelected.text")); // NOI18N
|
||||
disableAllSelectedMenuItem.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
disableAllSelectedMenuItemActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
vehicleListPopupMenu.add(disableAllSelectedMenuItem);
|
||||
|
||||
setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.X_AXIS));
|
||||
|
||||
listDisplayPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(bundle.getString("driverGui.panel_vehicles.border.title"))); // NOI18N
|
||||
listDisplayPanel.setMaximumSize(new java.awt.Dimension(464, 2147483647));
|
||||
listDisplayPanel.setMinimumSize(new java.awt.Dimension(464, 425));
|
||||
listDisplayPanel.setLayout(new java.awt.BorderLayout());
|
||||
|
||||
vehicleTable.setComponentPopupMenu(vehicleListPopupMenu);
|
||||
vehicleTable.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||
public void mouseClicked(java.awt.event.MouseEvent evt) {
|
||||
vehicleTableMouseClicked(evt);
|
||||
}
|
||||
});
|
||||
jScrollPane1.setViewportView(vehicleTable);
|
||||
|
||||
listDisplayPanel.add(jScrollPane1, java.awt.BorderLayout.CENTER);
|
||||
|
||||
add(listDisplayPanel);
|
||||
|
||||
vehicleDetailPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(bundle.getString("driverGui.panel_vehicleDetails.border.title"))); // NOI18N
|
||||
vehicleDetailPanel.setPreferredSize(new java.awt.Dimension(800, 23));
|
||||
vehicleDetailPanel.setLayout(new java.awt.BorderLayout());
|
||||
add(vehicleDetailPanel);
|
||||
vehicleDetailPanel.getAccessibleContext().setAccessibleName(bundle.getString("driverGui.panel_vehicleDetails.accessibleName")); // NOI18N
|
||||
|
||||
getAccessibleContext().setAccessibleName(bundle.getString("driverGui.accessibleName")); // NOI18N
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
// CHECKSTYLE:ON
|
||||
// FORMATTER:ON
|
||||
|
||||
private void driverMenuMenuSelected(javax.swing.event.MenuEvent evt) {//GEN-FIRST:event_driverMenuMenuSelected
|
||||
createDriverMenu();
|
||||
}//GEN-LAST:event_driverMenuMenuSelected
|
||||
|
||||
private void enableAllMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_enableAllMenuItemActionPerformed
|
||||
enableAllCommAdapters();
|
||||
}//GEN-LAST:event_enableAllMenuItemActionPerformed
|
||||
|
||||
private void enableAllSelectedMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_enableAllSelectedMenuItemActionPerformed
|
||||
enableSelectedCommAdapters();
|
||||
}//GEN-LAST:event_enableAllSelectedMenuItemActionPerformed
|
||||
|
||||
private void disableAllMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_disableAllMenuItemActionPerformed
|
||||
disableAllCommAdapters();
|
||||
}//GEN-LAST:event_disableAllMenuItemActionPerformed
|
||||
|
||||
private void disableAllSelectedMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_disableAllSelectedMenuItemActionPerformed
|
||||
disableSelectedCommAdapters();
|
||||
}//GEN-LAST:event_disableAllSelectedMenuItemActionPerformed
|
||||
|
||||
private void vehicleListPopupMenuPopupMenuWillBecomeVisible(
|
||||
javax.swing.event.PopupMenuEvent evt
|
||||
) {//GEN-FIRST:event_vehicleListPopupMenuPopupMenuWillBecomeVisible
|
||||
createPopupMenu();
|
||||
}//GEN-LAST:event_vehicleListPopupMenuPopupMenuWillBecomeVisible
|
||||
|
||||
private void vehicleTableMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_vehicleTableMouseClicked
|
||||
if (evt.getClickCount() == 2) {
|
||||
int index = vehicleTable.getSelectedRow();
|
||||
if (index >= 0) {
|
||||
VehicleTableModel model = (VehicleTableModel) vehicleTable.getModel();
|
||||
LocalVehicleEntry clickedEntry = model.getDataAt(index);
|
||||
DetailPanel detailPanel = (DetailPanel) vehicleDetailPanel.getComponent(0);
|
||||
detailPanel.attachToVehicle(clickedEntry);
|
||||
}
|
||||
}
|
||||
}//GEN-LAST:event_vehicleTableMouseClicked
|
||||
|
||||
// FORMATTER:OFF
|
||||
// CHECKSTYLE:OFF
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JMenuItem disableAllMenuItem;
|
||||
private javax.swing.JMenuItem disableAllSelectedMenuItem;
|
||||
private javax.swing.JMenu driverMenu;
|
||||
private javax.swing.JMenuItem enableAllMenuItem;
|
||||
private javax.swing.JMenuItem enableAllSelectedMenuItem;
|
||||
private javax.swing.JScrollPane jScrollPane1;
|
||||
private javax.swing.JSeparator jSeparator1;
|
||||
private javax.swing.JSeparator jSeparator4;
|
||||
private javax.swing.JPanel listDisplayPanel;
|
||||
private javax.swing.JMenuItem noDriversMenuItem;
|
||||
private javax.swing.JPanel vehicleDetailPanel;
|
||||
private javax.swing.JPopupMenu vehicleListPopupMenu;
|
||||
private javax.swing.JTable vehicleTable;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
// CHECKSTYLE:ON
|
||||
// FORMATTER:ON
|
||||
|
||||
/**
|
||||
* Attaches adapters produced by a given factory to a set of vehicles when performed.
|
||||
*/
|
||||
private class AttachCommAdapterAction
|
||||
extends
|
||||
AbstractAction {
|
||||
|
||||
/**
|
||||
* The affected vehicles' entries.
|
||||
*/
|
||||
private final List<String> vehicleNames;
|
||||
/**
|
||||
* The factory providing the communication adapter.
|
||||
*/
|
||||
private final VehicleCommAdapterDescription commAdapterDescription;
|
||||
|
||||
/**
|
||||
* Creates a new AttachCommAdapterAction.
|
||||
*
|
||||
* @param vehicleNames The affected vehicles' entries.
|
||||
* @param commAdapterDescription The factory providing the communication adapter.
|
||||
*/
|
||||
private AttachCommAdapterAction(
|
||||
List<String> vehicleNames,
|
||||
VehicleCommAdapterDescription commAdapterDescription
|
||||
) {
|
||||
super(commAdapterDescription.getDescription());
|
||||
this.vehicleNames = requireNonNull(vehicleNames, "vehicleNames");
|
||||
this.commAdapterDescription = requireNonNull(commAdapterDescription, "factory");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
for (String vehicleName : vehicleNames) {
|
||||
servicePortal.getVehicleService().attachCommAdapter(
|
||||
vehicleEntryPool.getEntryFor(vehicleName)
|
||||
.getAttachmentInformation()
|
||||
.getVehicleReference(),
|
||||
commAdapterDescription
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class StatesCounts {
|
||||
|
||||
private int enabledCount;
|
||||
private int disabledCount;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
StatesCounts() {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter.vehicles;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import jakarta.annotation.Nonnull;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyChangeSupport;
|
||||
import org.opentcs.drivers.vehicle.VehicleCommAdapterDescription;
|
||||
import org.opentcs.drivers.vehicle.management.VehicleAttachmentInformation;
|
||||
import org.opentcs.drivers.vehicle.management.VehicleProcessModelTO;
|
||||
|
||||
/**
|
||||
* An entry for a vehicle present in kernel with detailed information about its attachment state
|
||||
* and latest process model.
|
||||
*/
|
||||
public class LocalVehicleEntry {
|
||||
|
||||
/**
|
||||
* Used for implementing property change events.
|
||||
*/
|
||||
@SuppressWarnings("this-escape")
|
||||
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
|
||||
/**
|
||||
* Detailed information about the attachment state.
|
||||
*/
|
||||
private VehicleAttachmentInformation attachmentInformation;
|
||||
/**
|
||||
* The current process model to this entry.
|
||||
*/
|
||||
private VehicleProcessModelTO processModel;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param attachmentInformation Detailed information about the attachment state.
|
||||
* @param processModel The current process model to this entry.
|
||||
*/
|
||||
public LocalVehicleEntry(
|
||||
VehicleAttachmentInformation attachmentInformation,
|
||||
VehicleProcessModelTO processModel
|
||||
) {
|
||||
this.attachmentInformation = requireNonNull(attachmentInformation, "attachmentInformation");
|
||||
this.processModel = requireNonNull(processModel, "processModel");
|
||||
}
|
||||
|
||||
public void addPropertyChangeListener(PropertyChangeListener listener) {
|
||||
pcs.addPropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
public void removePropertyChangeListener(PropertyChangeListener listener) {
|
||||
pcs.removePropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public VehicleAttachmentInformation getAttachmentInformation() {
|
||||
return attachmentInformation;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public VehicleProcessModelTO getProcessModel() {
|
||||
return processModel;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public String getVehicleName() {
|
||||
return attachmentInformation.getVehicleReference().getName();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public VehicleCommAdapterDescription getAttachedCommAdapterDescription() {
|
||||
return attachmentInformation.getAttachedCommAdapter();
|
||||
}
|
||||
|
||||
public void setAttachmentInformation(
|
||||
@Nonnull
|
||||
VehicleAttachmentInformation attachmentInformation
|
||||
) {
|
||||
VehicleAttachmentInformation oldAttachmentInformation = this.attachmentInformation;
|
||||
this.attachmentInformation = requireNonNull(attachmentInformation, "attachmentInformation");
|
||||
|
||||
pcs.firePropertyChange(
|
||||
Attribute.ATTACHMENT_INFORMATION.name(),
|
||||
oldAttachmentInformation,
|
||||
attachmentInformation
|
||||
);
|
||||
}
|
||||
|
||||
public void setProcessModel(
|
||||
@Nonnull
|
||||
VehicleProcessModelTO processModel
|
||||
) {
|
||||
VehicleProcessModelTO oldProcessModel = this.processModel;
|
||||
this.processModel = requireNonNull(processModel, "processModel");
|
||||
|
||||
pcs.firePropertyChange(
|
||||
Attribute.PROCESS_MODEL.name(),
|
||||
oldProcessModel,
|
||||
processModel
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum elements used as notification arguments to specify which argument changed.
|
||||
*/
|
||||
public enum Attribute {
|
||||
/**
|
||||
* Indicates a change of the process model reference.
|
||||
*/
|
||||
PROCESS_MODEL,
|
||||
/**
|
||||
* Indicates a change of the attachment information reference.
|
||||
*/
|
||||
ATTACHMENT_INFORMATION
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter.vehicles;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import jakarta.annotation.Nonnull;
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import org.opentcs.access.KernelServicePortal;
|
||||
import org.opentcs.components.Lifecycle;
|
||||
import org.opentcs.customizations.ApplicationEventBus;
|
||||
import org.opentcs.customizations.ServiceCallWrapper;
|
||||
import org.opentcs.data.model.Vehicle;
|
||||
import org.opentcs.drivers.vehicle.management.ProcessModelEvent;
|
||||
import org.opentcs.drivers.vehicle.management.VehicleAttachmentEvent;
|
||||
import org.opentcs.drivers.vehicle.management.VehicleAttachmentInformation;
|
||||
import org.opentcs.drivers.vehicle.management.VehicleProcessModelTO;
|
||||
import org.opentcs.util.CallWrapper;
|
||||
import org.opentcs.util.event.EventHandler;
|
||||
import org.opentcs.util.event.EventSource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Provides a pool of {@link LocalVehicleEntry}s with an entry for every {@link Vehicle} object in
|
||||
* the kernel.
|
||||
*/
|
||||
public class LocalVehicleEntryPool
|
||||
implements
|
||||
EventHandler,
|
||||
Lifecycle {
|
||||
|
||||
/**
|
||||
* This class's logger.
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(LocalVehicleEntryPool.class);
|
||||
/**
|
||||
* The service portal to use for kernel interactions.
|
||||
*/
|
||||
private final KernelServicePortal servicePortal;
|
||||
/**
|
||||
* The call wrapper to use for service calls.
|
||||
*/
|
||||
private final CallWrapper callWrapper;
|
||||
/**
|
||||
* Where this instance registers for application events.
|
||||
*/
|
||||
private final EventSource eventSource;
|
||||
/**
|
||||
* The entries of this pool.
|
||||
*/
|
||||
private final Map<String, LocalVehicleEntry> entries = new TreeMap<>();
|
||||
/**
|
||||
* Whether the pool is initialized or not.
|
||||
*/
|
||||
private boolean initialized;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param servicePortal The service portal to use for kernel interactions.
|
||||
* @param callWrapper The call wrapper to use for service calls.
|
||||
* @param eventSource Where this instance registers for application events.
|
||||
*/
|
||||
@Inject
|
||||
public LocalVehicleEntryPool(
|
||||
KernelServicePortal servicePortal,
|
||||
@ServiceCallWrapper
|
||||
CallWrapper callWrapper,
|
||||
@ApplicationEventBus
|
||||
EventSource eventSource
|
||||
) {
|
||||
this.servicePortal = requireNonNull(servicePortal, "servicePortal");
|
||||
this.callWrapper = requireNonNull(callWrapper, "callWrapper");
|
||||
this.eventSource = requireNonNull(eventSource, "eventSource");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
if (isInitialized()) {
|
||||
LOG.debug("Already initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
eventSource.subscribe(this);
|
||||
|
||||
try {
|
||||
|
||||
Set<Vehicle> vehicles
|
||||
= callWrapper.call(() -> servicePortal.getVehicleService().fetchObjects(Vehicle.class));
|
||||
for (Vehicle vehicle : vehicles) {
|
||||
VehicleAttachmentInformation ai = callWrapper.call(() -> {
|
||||
return servicePortal.getVehicleService()
|
||||
.fetchAttachmentInformation(vehicle.getReference());
|
||||
});
|
||||
VehicleProcessModelTO processModel = callWrapper.call(() -> {
|
||||
return servicePortal.getVehicleService().fetchProcessModel(vehicle.getReference());
|
||||
});
|
||||
LocalVehicleEntry entry = new LocalVehicleEntry(ai, processModel);
|
||||
entries.put(vehicle.getName(), entry);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LOG.warn("Error initializing local vehicle entry pool", ex);
|
||||
entries.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.debug("Initialized vehicle entry pool: {}", entries);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void terminate() {
|
||||
if (!isInitialized()) {
|
||||
LOG.debug("Not initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
eventSource.unsubscribe(this);
|
||||
|
||||
entries.clear();
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(Object event) {
|
||||
if (event instanceof ProcessModelEvent) {
|
||||
ProcessModelEvent e = (ProcessModelEvent) event;
|
||||
LocalVehicleEntry entry = getEntryFor(e.getUpdatedProcessModel().getName());
|
||||
if (entry == null) {
|
||||
return;
|
||||
}
|
||||
entry.setProcessModel(e.getUpdatedProcessModel());
|
||||
}
|
||||
else if (event instanceof VehicleAttachmentEvent) {
|
||||
VehicleAttachmentEvent e = (VehicleAttachmentEvent) event;
|
||||
LocalVehicleEntry entry = getEntryFor(e.getVehicleName());
|
||||
if (entry == null) {
|
||||
return;
|
||||
}
|
||||
entry.setAttachmentInformation(e.getAttachmentInformation());
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Map<String, LocalVehicleEntry> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public LocalVehicleEntry getEntryFor(String vehicleName) {
|
||||
return vehicleName == null ? null : entries.get(vehicleName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter.vehicles;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.opentcs.kernelcontrolcenter.I18nKernelControlCenter.BUNDLE_PATH;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.FormatStyle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import org.opentcs.data.notification.UserNotification;
|
||||
|
||||
/**
|
||||
* A table model for holding {@link UserNotification} instances in rows.
|
||||
*/
|
||||
final class LogTableModel
|
||||
extends
|
||||
AbstractTableModel {
|
||||
|
||||
/**
|
||||
* The column names.
|
||||
*/
|
||||
private static final String[] COLUMN_NAMES
|
||||
= new String[]{
|
||||
ResourceBundle.getBundle(BUNDLE_PATH)
|
||||
.getString("logTableModel.column_timeStamp.headerText"),
|
||||
ResourceBundle.getBundle(BUNDLE_PATH)
|
||||
.getString("logTableModel.column_message.headerText")
|
||||
};
|
||||
/**
|
||||
* The column classes.
|
||||
*/
|
||||
private static final Class<?>[] COLUMN_CLASSES
|
||||
= new Class<?>[]{
|
||||
String.class,
|
||||
String.class
|
||||
};
|
||||
/**
|
||||
* A {@link DateFormat} instance for formatting notifications' time stamps.
|
||||
*/
|
||||
private final DateTimeFormatter dateFormat = DateTimeFormatter
|
||||
.ofLocalizedDateTime(FormatStyle.SHORT)
|
||||
.withLocale(Locale.getDefault())
|
||||
.withZone(ZoneId.systemDefault());
|
||||
/**
|
||||
* The buffer for holding the model data.
|
||||
*/
|
||||
private final Set<UserNotification> values
|
||||
= new TreeSet<>((n1, n2) -> n1.getTimestamp().compareTo(n2.getTimestamp()));
|
||||
/**
|
||||
* The actual data displayed in the table.
|
||||
*/
|
||||
private List<UserNotification> filteredValues = new ArrayList<>();
|
||||
/**
|
||||
* The predicate used for filtering table rows.
|
||||
*/
|
||||
private Predicate<UserNotification> filterPredicate = (notification) -> true;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
LogTableModel() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int row, int column) {
|
||||
if (row < 0 || row >= filteredValues.size()) {
|
||||
return null;
|
||||
}
|
||||
switch (column) {
|
||||
case 0:
|
||||
return dateFormat.format(filteredValues.get(row).getTimestamp());
|
||||
case 1:
|
||||
return filteredValues.get(row).getText();
|
||||
default:
|
||||
return new IllegalArgumentException("Column out of bounds.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a row to the model, containing the given notification.
|
||||
*
|
||||
* @param notification The notification to be added in a new row.
|
||||
*/
|
||||
public void addRow(UserNotification notification) {
|
||||
requireNonNull(notification, "notification");
|
||||
|
||||
values.add(notification);
|
||||
updateFilteredValues();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given notification from the internal data set and from the current data model.
|
||||
*
|
||||
* @param notification The notification to be removed.
|
||||
*/
|
||||
public void removeRow(UserNotification notification) {
|
||||
if (values.contains(notification)) {
|
||||
values.remove(notification);
|
||||
updateFilteredValues();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all notifications from the model.
|
||||
*/
|
||||
public void clear() {
|
||||
if (!values.isEmpty()) {
|
||||
values.clear();
|
||||
updateFilteredValues();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getColumnClass(int columnIndex) {
|
||||
return COLUMN_CLASSES[columnIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the notification object representing the indexed row.
|
||||
*
|
||||
* @param row The row for which to fetch the notification object.
|
||||
* @return The notification object representing the indexed row.
|
||||
*/
|
||||
public UserNotification getRow(int row) {
|
||||
return filteredValues.get(row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the notifications and shows only errors and warnings.
|
||||
*
|
||||
* @param predicate The predicate used for filtering table rows.
|
||||
*/
|
||||
public void filterMessages(Predicate<UserNotification> predicate) {
|
||||
this.filterPredicate = requireNonNull(predicate, "predicate");
|
||||
|
||||
updateFilteredValues();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return filteredValues.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return COLUMN_NAMES.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int columnIndex) {
|
||||
try {
|
||||
return COLUMN_NAMES[columnIndex];
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException exc) {
|
||||
return "ERROR";
|
||||
}
|
||||
}
|
||||
|
||||
private void updateFilteredValues() {
|
||||
filteredValues = values.stream().filter(filterPredicate).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter.vehicles;
|
||||
|
||||
import java.awt.Component;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import org.opentcs.drivers.vehicle.VehicleCommAdapterDescription;
|
||||
|
||||
/**
|
||||
* A {@link TableCellRenderer} for {@link VehicleCommAdapterDescription} instances.
|
||||
* This class provides a representation of any VehicleCommAdapterDescription instance by writing
|
||||
* its actual description on a JLabel.
|
||||
*/
|
||||
class VehicleCommAdapterFactoryTableCellRenderer
|
||||
extends
|
||||
DefaultTableCellRenderer {
|
||||
|
||||
VehicleCommAdapterFactoryTableCellRenderer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(
|
||||
JTable table,
|
||||
Object value,
|
||||
boolean isSelected,
|
||||
boolean hasFocus,
|
||||
int row,
|
||||
int column
|
||||
)
|
||||
throws IllegalArgumentException {
|
||||
|
||||
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||
|
||||
if (value == null) {
|
||||
setText("");
|
||||
}
|
||||
else if (value instanceof VehicleCommAdapterDescription) {
|
||||
setText(((VehicleCommAdapterDescription) value).getDescription());
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("value");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter.vehicles;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.opentcs.kernelcontrolcenter.I18nKernelControlCenter.BUNDLE_PATH;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.ResourceBundle;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import org.opentcs.components.kernel.services.VehicleService;
|
||||
import org.opentcs.drivers.vehicle.VehicleCommAdapterDescription;
|
||||
import org.opentcs.drivers.vehicle.management.VehicleAttachmentInformation;
|
||||
import org.opentcs.drivers.vehicle.management.VehicleProcessModelTO;
|
||||
import org.opentcs.util.CallWrapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A model for displaying a list/table of vehicles in the kernel GUI.
|
||||
*/
|
||||
public class VehicleTableModel
|
||||
extends
|
||||
AbstractTableModel
|
||||
implements
|
||||
PropertyChangeListener {
|
||||
|
||||
/**
|
||||
* This class's logger.
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(VehicleTableModel.class);
|
||||
/**
|
||||
* This class's resource bundle.
|
||||
*/
|
||||
private static final ResourceBundle BUNDLE = ResourceBundle.getBundle(BUNDLE_PATH);
|
||||
/**
|
||||
* The column names.
|
||||
*/
|
||||
private static final String[] COLUMN_NAMES
|
||||
= new String[]{
|
||||
BUNDLE.getString("vehicleTableModel.column_vehicle.headerText"),
|
||||
BUNDLE.getString("vehicleTableModel.column_state.headerText"),
|
||||
BUNDLE.getString("vehicleTableModel.column_adapter.headerText"),
|
||||
BUNDLE.getString("vehicleTableModel.column_enabled.headerText"),
|
||||
BUNDLE.getString("vehicleTableModel.column_position.headerText")
|
||||
};
|
||||
/**
|
||||
* The column classes.
|
||||
*/
|
||||
private static final Class<?>[] COLUMN_CLASSES
|
||||
= new Class<?>[]{
|
||||
String.class,
|
||||
String.class,
|
||||
VehicleCommAdapterDescription.class,
|
||||
Boolean.class,
|
||||
String.class
|
||||
};
|
||||
/**
|
||||
* The index of the column showing the vehicle name.
|
||||
*/
|
||||
private static final int VEHICLE_COLUMN = 0;
|
||||
/**
|
||||
* The index of the column showing the vehicle state.
|
||||
*/
|
||||
private static final int STATE_COLUMNN = 1;
|
||||
/**
|
||||
* The index of the column showing the associated adapter.
|
||||
*/
|
||||
private static final int ADAPTER_COLUMN = 2;
|
||||
/**
|
||||
* The index of the column showing the adapter's enabled state.
|
||||
*/
|
||||
private static final int ENABLED_COLUMN = 3;
|
||||
/**
|
||||
* The index of the column showing the vehicle's current position.
|
||||
*/
|
||||
private static final int POSITION_COLUMN = 4;
|
||||
/**
|
||||
* The identifier for the adapter column.
|
||||
*/
|
||||
private static final String ADAPTER_COLUMN_IDENTIFIER = COLUMN_NAMES[ADAPTER_COLUMN];
|
||||
/**
|
||||
* The identifier for the position column.
|
||||
*/
|
||||
private static final String POSITION_COLUMN_IDENTIFIER = COLUMN_NAMES[POSITION_COLUMN];
|
||||
/**
|
||||
* The vehicles we're controlling.
|
||||
*/
|
||||
private final List<LocalVehicleEntry> entries = new ArrayList<>();
|
||||
/**
|
||||
* The vehicle service used for interactions.
|
||||
*/
|
||||
private final VehicleService vehicleService;
|
||||
/**
|
||||
* The call wrapper to use for service calls.
|
||||
*/
|
||||
private final CallWrapper callWrapper;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param vehicleService The vehicle service used for interactions.
|
||||
* @param callWrapper The call wrapper to use for service calls.
|
||||
*/
|
||||
public VehicleTableModel(
|
||||
VehicleService vehicleService,
|
||||
CallWrapper callWrapper
|
||||
) {
|
||||
this.vehicleService = requireNonNull(vehicleService, "vehicleService");
|
||||
this.callWrapper = requireNonNull(callWrapper, "callWrapper");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier for the adapter column.
|
||||
*
|
||||
* @return The identifier for the adapter column.
|
||||
*/
|
||||
public static String adapterColumnIdentifier() {
|
||||
return ADAPTER_COLUMN_IDENTIFIER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier for the position column.
|
||||
*
|
||||
* @return The identifier for the position column.
|
||||
*/
|
||||
public static String positionColumnIdentifier() {
|
||||
return POSITION_COLUMN_IDENTIFIER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new entry to this model.
|
||||
*
|
||||
* @param newEntry The new entry.
|
||||
*/
|
||||
public void addData(LocalVehicleEntry newEntry) {
|
||||
entries.add(newEntry);
|
||||
fireTableRowsInserted(entries.size(), entries.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the vehicle entry at the given row.
|
||||
*
|
||||
* @param row The row.
|
||||
* @return The entry at the given row.
|
||||
*/
|
||||
public LocalVehicleEntry getDataAt(int row) {
|
||||
if (row >= 0) {
|
||||
return entries.get(row);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return entries.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return COLUMN_NAMES.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
|
||||
LocalVehicleEntry entry = entries.get(rowIndex);
|
||||
switch (columnIndex) {
|
||||
case ADAPTER_COLUMN:
|
||||
break;
|
||||
case ENABLED_COLUMN:
|
||||
setEnabledState((boolean) aValue, entry);
|
||||
break;
|
||||
case POSITION_COLUMN:
|
||||
break;
|
||||
default:
|
||||
LOG.warn("Unhandled column index: {}", columnIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
if (rowIndex >= entries.size()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
LocalVehicleEntry entry = entries.get(rowIndex);
|
||||
|
||||
switch (columnIndex) {
|
||||
case VEHICLE_COLUMN:
|
||||
return entry.getVehicleName();
|
||||
case STATE_COLUMNN:
|
||||
return getVehicleState(entry);
|
||||
case ADAPTER_COLUMN:
|
||||
return entry.getAttachmentInformation().getAttachedCommAdapter();
|
||||
case ENABLED_COLUMN:
|
||||
return entry.getProcessModel().isCommAdapterEnabled();
|
||||
case POSITION_COLUMN:
|
||||
return entry.getProcessModel().getPosition();
|
||||
default:
|
||||
LOG.warn("Unhandled column index: {}", columnIndex);
|
||||
return "Invalid column index " + columnIndex;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int columnIndex) {
|
||||
try {
|
||||
return COLUMN_NAMES[columnIndex];
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException exc) {
|
||||
LOG.warn("Invalid columnIndex", exc);
|
||||
return "Invalid column index " + columnIndex;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getColumnClass(int columnIndex) {
|
||||
return COLUMN_CLASSES[columnIndex];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
||||
switch (columnIndex) {
|
||||
case ADAPTER_COLUMN:
|
||||
return true;
|
||||
case ENABLED_COLUMN:
|
||||
return true;
|
||||
case POSITION_COLUMN:
|
||||
LocalVehicleEntry entry = entries.get(rowIndex);
|
||||
return entry.getAttachedCommAdapterDescription().isSimVehicleCommAdapter()
|
||||
&& entry.getProcessModel().isCommAdapterEnabled();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list containing the vehicle models associated with this model.
|
||||
*
|
||||
* @return A list containing the vehicle models associated with this model.
|
||||
*/
|
||||
public List<LocalVehicleEntry> getVehicleEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (!(evt.getSource() instanceof LocalVehicleEntry)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isRelevantUpdate(evt)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LocalVehicleEntry entry = (LocalVehicleEntry) evt.getSource();
|
||||
for (int index = 0; index < entries.size(); index++) {
|
||||
if (entry == entries.get(index)) {
|
||||
int myIndex = index;
|
||||
SwingUtilities.invokeLater(() -> fireTableRowsUpdated(myIndex, myIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setEnabledState(boolean enabled, LocalVehicleEntry entry) {
|
||||
try {
|
||||
if (enabled) {
|
||||
callWrapper.call(
|
||||
() -> vehicleService.enableCommAdapter(
|
||||
entry.getAttachmentInformation().getVehicleReference()
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
callWrapper.call(
|
||||
() -> vehicleService.disableCommAdapter(
|
||||
entry.getAttachmentInformation().getVehicleReference()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LOG.warn("Error enabling/disabling comm adapter for {}", entry.getVehicleName(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private String getVehicleState(LocalVehicleEntry entry) {
|
||||
return entry.getProcessModel().getState().name();
|
||||
}
|
||||
|
||||
private boolean isRelevantUpdate(PropertyChangeEvent evt) {
|
||||
if (Objects.equals(
|
||||
evt.getPropertyName(),
|
||||
LocalVehicleEntry.Attribute.ATTACHMENT_INFORMATION.name()
|
||||
)) {
|
||||
VehicleAttachmentInformation oldInfo = (VehicleAttachmentInformation) evt.getOldValue();
|
||||
VehicleAttachmentInformation newInfo = (VehicleAttachmentInformation) evt.getNewValue();
|
||||
return !oldInfo.getAttachedCommAdapter().equals(newInfo.getAttachedCommAdapter());
|
||||
}
|
||||
if (Objects.equals(
|
||||
evt.getPropertyName(),
|
||||
LocalVehicleEntry.Attribute.PROCESS_MODEL.name()
|
||||
)) {
|
||||
VehicleProcessModelTO oldTo = (VehicleProcessModelTO) evt.getOldValue();
|
||||
VehicleProcessModelTO newTo = (VehicleProcessModelTO) evt.getNewValue();
|
||||
return oldTo.isCommAdapterEnabled() != newTo.isCommAdapterEnabled()
|
||||
|| oldTo.getState() != newTo.getState()
|
||||
|| !Objects.equals(oldTo.getPosition(), newTo.getPosition());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
/**
|
||||
* Classes of the kernel control center concerning vehicles/vehicle drivers.
|
||||
* The GUI parts of the driver framework.
|
||||
*/
|
||||
package org.opentcs.kernelcontrolcenter.vehicles;
|
||||
10
opentcs-kernelcontrolcenter/src/main/resources/REUSE.toml
Normal file
10
opentcs-kernelcontrolcenter/src/main/resources/REUSE.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
# SPDX-FileCopyrightText: The openTCS Authors
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
version = 1
|
||||
|
||||
[[annotations]]
|
||||
path = ["**/*.gif", "**/*.jpg", "**/*.png", "**/*.svg"]
|
||||
precedence = "closest"
|
||||
SPDX-FileCopyrightText = "The openTCS Authors"
|
||||
SPDX-License-Identifier = "CC-BY-4.0"
|
||||
@@ -0,0 +1,60 @@
|
||||
# SPDX-FileCopyrightText: The openTCS Authors
|
||||
# SPDX-License-Identifier: CC-BY-4.0
|
||||
|
||||
aboutDialog.button_close.text=Close
|
||||
aboutDialog.label_baselineVersion.text=Baseline version:
|
||||
aboutDialog.label_customVersion.text=Custom version:
|
||||
aboutDialog.label_email.text=E-mail:
|
||||
aboutDialog.label_fraunhoferIml.text=<html><b>Maintainer:<br>Fraunhofer Institute for<br>Material Flow and Logistics (IML)</b></html>
|
||||
aboutDialog.label_homepage.text=Home page:
|
||||
aboutDialog.title=About openTCS
|
||||
defaultServiceCallWrapper.optionPane_retryConfirmation.message=Do you want to retry the last service call?
|
||||
defaultServiceCallWrapper.optionPane_retryConfirmation.title=Retry service call?
|
||||
detailPanel.checkBox_enableAdapter.text=Enable adapter
|
||||
detailPanel.label_currentPosition.text=Current position:
|
||||
detailPanel.label_currentState.text=Current state:
|
||||
detailPanel.label_noVehicleAttached.text=<html>No vehicle attached to this panel.<br>Doubleclick on a vehicle to display it here.</html>
|
||||
detailPanel.panel_adapterStatus.border.title=Adapter status
|
||||
detailPanel.panel_messages.border.title=Messages
|
||||
detailPanel.panel_vehicleStatus.border.title=Vehicle status
|
||||
detailPanel.popupMenu_messageDetails.menuItem_clear.text=Clear log message text
|
||||
detailPanel.popupMenu_messagesTable.subMenu_filter.menuItem_showAll.text=Show all messages
|
||||
detailPanel.popupMenu_messagesTable.subMenu_filter.menuItem_showErrors.text=Show errors only
|
||||
detailPanel.popupMenu_messagesTable.subMenu_filter.menuItem_showErrorsAndWarnings.text=Show errors and warnings only
|
||||
detailPanel.popupMenu_messagesTable.subMenu_filter.text=Filter messages...
|
||||
detailPanel.tab_generalStatus.text=General status
|
||||
driverGui.accessibleName=Vehicle driver
|
||||
driverGui.optionPane_driverChangeConfirmation.message=<html>Do you really want to change the associated driver?<br>(This will reset the vehicle's resource allocations.)</html>
|
||||
driverGui.optionPane_driverChangeConfirmation.title=Really change the driver?
|
||||
driverGui.panel_vehicleDetails.accessibleName=Vehicle details
|
||||
driverGui.panel_vehicleDetails.border.title=Vehicle details
|
||||
driverGui.panel_vehicles.border.title=Vehicles in model
|
||||
driverGui.popupMenu_vehicles.menuItem_disableAll.text=Disable all
|
||||
driverGui.popupMenu_vehicles.menuItem_disableSelected.text=Disable selected
|
||||
driverGui.popupMenu_vehicles.menuItem_enableAll.text=Enable all
|
||||
driverGui.popupMenu_vehicles.menuItem_enableSelected.text=Enable selected
|
||||
driverGui.popupMenu_vehicles.subMenu_driver.text=Driver
|
||||
kernelControlCenter.checkBox_autoScroll.text=Enable automatic scrolling (i.e. always show youngest messages)
|
||||
kernelControlCenter.menu_about.text=About openTCS
|
||||
kernelControlCenter.menu_help.text=Help
|
||||
kernelControlCenter.menu_kernel.menuItem_connect.text=Connect to kernel
|
||||
kernelControlCenter.menu_kernel.menuItem_disconnect.text=Disconnect from kernel
|
||||
kernelControlCenter.menu_kernel.menuItem_exit.text=Exit
|
||||
kernelControlCenter.tab_logging.title=Logging
|
||||
kernelControlCenter.title.connectedTo=Connected to:
|
||||
kernelControlCenter.title=Kernel Control Center
|
||||
logTableModel.column_message.headerText=Message
|
||||
logTableModel.column_timeStamp.headerText=Time stamp
|
||||
peripheralDetailPanel.label_noPeripheralDeviceAttached.text=<html>No peripheral device attached to this panel.<br>Doubleclick on a peripheral device to display it here.</html>
|
||||
peripheralTableModel.column_adapter.headerText=Adapter
|
||||
peripheralTableModel.column_enabled.headerText=Enabled?
|
||||
peripheralTableModel.column_location.headerText=Location
|
||||
peripheralsPanel.accessibleName=Peripheral driver
|
||||
peripheralsPanel.checkBox_hideDetachedLocations.text=Hide detached locations
|
||||
peripheralsPanel.optionPane_driverChangeConfirmation.message=Do you really want to change the associated driver?
|
||||
peripheralsPanel.optionPane_driverChangeConfirmation.title=Really change the driver?
|
||||
vehicleTableModel.column_adapter.headerText=Adapter
|
||||
vehicleTableModel.column_enabled.headerText=Enabled?
|
||||
vehicleTableModel.column_position.headerText=Position
|
||||
vehicleTableModel.column_state.headerText=State
|
||||
vehicleTableModel.column_vehicle.headerText=Vehicle
|
||||
@@ -0,0 +1,59 @@
|
||||
# SPDX-FileCopyrightText: The openTCS Authors
|
||||
# SPDX-License-Identifier: CC-BY-4.0
|
||||
|
||||
aboutDialog.button_close.text=Schlie\u00dfen
|
||||
aboutDialog.label_baselineVersion.text=Baseline-Version:
|
||||
aboutDialog.label_customVersion.text=Custom-Version:
|
||||
aboutDialog.label_email.text=E-Mail:
|
||||
aboutDialog.label_fraunhoferIml.text=<html><b>Maintainer:<br>Fraunhofer-Institut f\u00fcr<br>Materialfluss und Logistik (IML)</b></html>
|
||||
aboutDialog.label_homepage.text=Homepage:
|
||||
aboutDialog.title=\u00dcber openTCS
|
||||
defaultServiceCallWrapper.optionPane_retryConfirmation.message=Wollen Sie den letzten Serviceaufruf erneut versuchen?
|
||||
defaultServiceCallWrapper.optionPane_retryConfirmation.title=Erneuter Serviceaufruf?
|
||||
detailPanel.checkBox_enableAdapter.text=Adapter einschalten
|
||||
detailPanel.label_currentPosition.text=Aktuelle Position:
|
||||
detailPanel.label_currentState.text=Aktueller Zustand:
|
||||
detailPanel.label_noVehicleAttached.text=<html>Kein Fahrzeug in diesem Fenster.<br>Klicken Sie ein Fahrzeug doppelt an, um es hier anzuzeigen.</html>
|
||||
detailPanel.panel_adapterStatus.border.title=Adapterzustand
|
||||
detailPanel.panel_messages.border.title=Nachrichten
|
||||
detailPanel.panel_vehicleStatus.border.title=Fahrzeugzustand
|
||||
detailPanel.popupMenu_messageDetails.menuItem_clear.text=Nachrichtentext l\u00f6schen
|
||||
detailPanel.popupMenu_messagesTable.subMenu_filter.menuItem_showAll.text=Alle Nachrichten anzeigen
|
||||
detailPanel.popupMenu_messagesTable.subMenu_filter.menuItem_showErrors.text=Nur Fehler anzeigen
|
||||
detailPanel.popupMenu_messagesTable.subMenu_filter.menuItem_showErrorsAndWarnings.text=Nur Fehler und Warnungen anzeigen
|
||||
detailPanel.popupMenu_messagesTable.subMenu_filter.text=Nachrichten filtern...
|
||||
detailPanel.tab_generalStatus.text=Zustand allgemein
|
||||
driverGui.accessibleName=Fahrzeugtreiber
|
||||
driverGui.optionPane_driverChangeConfirmation.message=<html>Wollen Sie wirklich den zugewiesenen Treiber \u00e4ndern?<br>(Dies wird die Ressourcenzuweisungen des Fahrzeugs zur\u00fccksetzen.)</html>
|
||||
driverGui.optionPane_driverChangeConfirmation.title=Wirklich den Treiber \u00e4ndern?
|
||||
driverGui.panel_vehicleDetails.border.title=Detailansicht
|
||||
driverGui.panel_vehicles.border.title=Modellansicht
|
||||
driverGui.popupMenu_vehicles.menuItem_disableAll.text=Alle Treiber ausschalten
|
||||
driverGui.popupMenu_vehicles.menuItem_disableSelected.text=Ausgew\u00e4hlte Treiber ausschalten
|
||||
driverGui.popupMenu_vehicles.menuItem_enableAll.text=Alle Treiber einschalten
|
||||
driverGui.popupMenu_vehicles.menuItem_enableSelected.text=Ausgew\u00e4hlte Treiber einschalten
|
||||
driverGui.popupMenu_vehicles.subMenu_driver.text=Treiber
|
||||
kernelControlCenter.checkBox_autoScroll.text=Automatischen Bildlauf aktivieren (d.h. immer die j\u00fcngste Nachricht anzeigen)
|
||||
kernelControlCenter.menu_about.text=\u00dcber openTCS
|
||||
kernelControlCenter.menu_help.text=Hilfe
|
||||
kernelControlCenter.menu_kernel.menuItem_connect.text=Mit Kernel verbinden
|
||||
kernelControlCenter.menu_kernel.menuItem_disconnect.text=Von Kernel trennen
|
||||
kernelControlCenter.menu_kernel.menuItem_exit.text=Beenden
|
||||
kernelControlCenter.tab_logging.title=Protokollierung
|
||||
kernelControlCenter.title.connectedTo=Verbunden mit:
|
||||
kernelControlCenter.title=Kernel-Kontrollzentrum
|
||||
logTableModel.column_message.headerText=Nachricht
|
||||
logTableModel.column_timeStamp.headerText=Zeitstempel
|
||||
peripheralDetailPanel.label_noPeripheralDeviceAttached.text=<html>Kein Peripherieger\u00e4t in diesem Fenster.<br>Klicken Sie eine Station doppelt an, um sie hier anzuzeigen.</html>
|
||||
peripheralTableModel.column_adapter.headerText=Adapter
|
||||
peripheralTableModel.column_enabled.headerText=Aktiviert?
|
||||
peripheralTableModel.column_location.headerText=Station
|
||||
peripheralsPanel.accessibleName=Peripherietreiber
|
||||
peripheralsPanel.checkBox_hideDetachedLocations.text=Nicht zugewiesene Stationen ausblenden
|
||||
peripheralsPanel.optionPane_driverChangeConfirmation.message=Wollen Sie wirklich den zugewiesenen Treiber \u00e4ndern?
|
||||
peripheralsPanel.optionPane_driverChangeConfirmation.title=Wirklich den Treiber \u00e4ndern?
|
||||
vehicleTableModel.column_adapter.headerText=Adapter
|
||||
vehicleTableModel.column_enabled.headerText=Aktiviert?
|
||||
vehicleTableModel.column_position.headerText=Position
|
||||
vehicleTableModel.column_state.headerText=Zustand
|
||||
vehicleTableModel.column_vehicle.headerText=Fahrzeug
|
||||
@@ -0,0 +1,15 @@
|
||||
# SPDX-FileCopyrightText: The openTCS Authors
|
||||
# SPDX-License-Identifier: CC-BY-4.0
|
||||
|
||||
# This file contains default configuration values and should not be modified.
|
||||
# To adjust the application configuration, override values in a separate file.
|
||||
|
||||
kernelcontrolcenter.locale = en
|
||||
kernelcontrolcenter.connectionBookmarks = Localhost|localhost|1099
|
||||
kernelcontrolcenter.connectAutomaticallyOnStartup = true
|
||||
kernelcontrolcenter.loggingAreaCapacity = 3000
|
||||
kernelcontrolcenter.enablePeripheralsPanel = true
|
||||
|
||||
ssl.enable = false
|
||||
ssl.truststoreFile = ./config/truststore.p12
|
||||
ssl.truststorePassword = password
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,106 @@
|
||||
// SPDX-FileCopyrightText: The openTCS Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
package org.opentcs.kernelcontrolcenter;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.opentcs.common.PortalManager;
|
||||
import org.opentcs.kernelcontrolcenter.exchange.KernelEventFetcher;
|
||||
import org.opentcs.kernelcontrolcenter.util.KernelControlCenterConfiguration;
|
||||
import org.opentcs.util.event.EventBus;
|
||||
|
||||
/**
|
||||
* Test cases for the {@link KernelControlCenterApplication}.
|
||||
*/
|
||||
class KernelControlCenterApplicationTest {
|
||||
|
||||
/**
|
||||
* The class to test.
|
||||
*/
|
||||
private KernelControlCenterApplication application;
|
||||
|
||||
/**
|
||||
* The event bus for online events.
|
||||
*/
|
||||
private EventBus eventBus;
|
||||
|
||||
/**
|
||||
* The portal manager to establish kernel connections.
|
||||
*/
|
||||
private PortalManager portalManager;
|
||||
|
||||
/**
|
||||
* The control center gui.
|
||||
*/
|
||||
private KernelControlCenter controlCenter;
|
||||
|
||||
/**
|
||||
* The configuration of the control center.
|
||||
*/
|
||||
private KernelControlCenterConfiguration configuration;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
eventBus = mock(EventBus.class);
|
||||
portalManager = mock(PortalManager.class);
|
||||
configuration = mock(KernelControlCenterConfiguration.class);
|
||||
controlCenter = mock(KernelControlCenter.class);
|
||||
application = spy(
|
||||
new KernelControlCenterApplication(
|
||||
mock(KernelEventFetcher.class),
|
||||
controlCenter,
|
||||
portalManager,
|
||||
eventBus,
|
||||
configuration
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void onlyInitializeOnce() {
|
||||
when(configuration.connectAutomaticallyOnStartup()).thenReturn(true);
|
||||
application.initialize();
|
||||
application.initialize();
|
||||
|
||||
assertTrue(application.isInitialized());
|
||||
verify(controlCenter, times(1)).initialize();
|
||||
verify(portalManager, times(1)).connect(any());
|
||||
verify(application, times(1)).online(anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
void onlyTerminateOnce() {
|
||||
when(configuration.connectAutomaticallyOnStartup()).thenReturn(false);
|
||||
application.initialize();
|
||||
application.terminate();
|
||||
application.terminate();
|
||||
|
||||
assertFalse(application.isInitialized());
|
||||
verify(controlCenter, times(1)).terminate();
|
||||
verify(application, times(1)).offline();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldOnlyConnectOnce() {
|
||||
//When trying to connect, return the value that indicates a successful connection
|
||||
when(portalManager.connect(any())).thenReturn(true);
|
||||
application.initialize();
|
||||
application.online(true);
|
||||
application.online(true);
|
||||
|
||||
assertTrue(application.isInitialized());
|
||||
assertTrue(application.isOnline());
|
||||
verify(controlCenter, times(1)).initialize();
|
||||
verify(portalManager, times(1)).connect(any());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user