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

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,2 @@
# SPDX-FileCopyrightText: The openTCS Authors
# SPDX-License-Identifier: CC-BY-4.0

View 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

View File

@@ -0,0 +1,2 @@
# SPDX-FileCopyrightText: The openTCS Authors
# SPDX-License-Identifier: CC-BY-4.0

View 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

View 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

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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[]
}

View File

@@ -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()
);
}
}

View File

@@ -0,0 +1,5 @@
# SPDX-FileCopyrightText: The openTCS Authors
# SPDX-License-Identifier: MIT
org.opentcs.kernelcontrolcenter.DefaultKernelControlCenterExtensionsModule
org.opentcs.kernelcontrolcenter.LoopbackCommAdapterPanelsModule

View File

@@ -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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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() + &quot; &quot; + 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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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>

View File

@@ -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
}

View File

@@ -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);
}
}
});
}
}

View File

@@ -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);
}

View File

@@ -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";
}

View File

@@ -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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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>

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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
}
}

View File

@@ -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());
}
}

View File

@@ -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());
}
}

View File

@@ -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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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="&lt;User Code&gt;">
<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>

View File

@@ -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
}

View File

@@ -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;
}
}

View File

@@ -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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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>

View File

@@ -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
}

View File

@@ -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();
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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="&lt;User Code&gt;">
<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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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>

View File

@@ -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("");
}
}
}
}

View File

@@ -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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</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(&quot;{bundleNameSlashes}&quot;).getString(&quot;{key}&quot;)"/>
</Property>
</AccessibilityProperties>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
</Container>
</SubComponents>
</Form>

View File

@@ -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() {
}
}
}

View File

@@ -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
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;

View 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"

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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());
}
}