org.bidib.wizard.mvc.booster.controller.BoosterTableController Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bidibwizard-client Show documentation
Show all versions of bidibwizard-client Show documentation
jBiDiB BiDiB Wizard Client Application POM
package org.bidib.wizard.mvc.booster.controller;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.function.Supplier;
import javax.swing.Timer;
import org.apache.commons.collections4.CollectionUtils;
import org.bidib.jbidibc.messages.enums.CommandStationState;
import org.bidib.jbidibc.messages.utils.NodeUtils;
import org.bidib.jbidibc.messages.utils.ThreadFactoryBuilder;
import org.bidib.wizard.api.model.NodeInterface;
import org.bidib.wizard.api.model.NodeListProvider;
import org.bidib.wizard.api.model.NodeProvider;
import org.bidib.wizard.api.model.event.NodeStatusEvent.StatusIdentifier;
import org.bidib.wizard.api.model.listener.DefaultNodeListListener;
import org.bidib.wizard.api.model.listener.NodeListListener;
import org.bidib.wizard.api.service.console.ConsoleService;
import org.bidib.wizard.api.service.node.BoosterService;
import org.bidib.wizard.api.service.node.CommandStationService;
import org.bidib.wizard.client.common.uils.SwingUtils;
import org.bidib.wizard.client.common.view.DockKeys;
import org.bidib.wizard.client.common.view.DockUtils;
import org.bidib.wizard.client.common.view.statusbar.StatusBar;
import org.bidib.wizard.common.labels.WizardLabelWrapper;
import org.bidib.wizard.core.model.connection.ConnectionRegistry;
import org.bidib.wizard.core.service.ConnectionService;
import org.bidib.wizard.model.status.BoosterStatus;
import org.bidib.wizard.model.status.CommandStationStatus;
import org.bidib.wizard.mvc.booster.controller.listener.BoosterTableControllerListener;
import org.bidib.wizard.mvc.booster.model.BoosterModel;
import org.bidib.wizard.mvc.booster.model.BoosterTableModel;
import org.bidib.wizard.mvc.booster.view.BoosterTableView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.vlsolutions.swing.docking.Dockable;
import com.vlsolutions.swing.docking.DockableState;
import com.vlsolutions.swing.docking.DockingConstants;
import com.vlsolutions.swing.docking.DockingDesktop;
import com.vlsolutions.swing.docking.DockingUtilities;
import com.vlsolutions.swing.docking.RelativeDockablePosition;
import com.vlsolutions.swing.docking.TabbedDockableContainer;
import com.vlsolutions.swing.docking.event.DockableStateChangeEvent;
import com.vlsolutions.swing.docking.event.DockableStateChangeListener;
public class BoosterTableController implements BoosterTableControllerListener {
private static final Logger LOGGER = LoggerFactory.getLogger(BoosterTableController.class);
private BoosterTableView boosterTableView;
private BoosterTableModel boosterTableModel;
private Timer boosterCurrentTimer;
private static final long CURRENT_UPDATE_TIMEOUT = 3000;
@Autowired
private ConnectionService connectionService;
@Autowired
private BoosterService boosterService;
@Autowired
private CommandStationService commandStationService;
@Autowired
private StatusBar statusBar;
@Autowired
private WizardLabelWrapper wizardLabelWrapper;
@Autowired
private ConsoleService consoleService;
private final DockingDesktop desktop;
private DockableStateChangeListener dockableStateChangeListener;
private final Supplier nodeProviderSupplier;
private final NodeListProvider nodeListProvider;
protected final ScheduledExecutorService serviceWorker;
public BoosterTableController(final DockingDesktop desktop, final Supplier nodeProviderSupplier,
final NodeListProvider nodeListProvider) {
this.desktop = desktop;
this.nodeProviderSupplier = nodeProviderSupplier;
this.nodeListProvider = nodeListProvider;
final ThreadFactory namedThreadFactory =
new ThreadFactoryBuilder().setNameFormat("boosterServiceWorkers-thread-%d").build();
serviceWorker = Executors.newScheduledThreadPool(2, namedThreadFactory);
}
public void start() {
// check if the booster table view is already opened
String searchKey = DockKeys.BOOSTER_TABLE_VIEW;
LOGGER.info("Search for view with key: {}", searchKey);
Dockable view = desktop.getContext().getDockableByKey(searchKey);
if (view != null) {
LOGGER.info("Select the existing booster table view.");
DockUtils.selectWindow(view);
return;
}
createDockable();
}
public Dockable createDockable() {
LOGGER.info("Create new BoosterTableView.");
if (boosterTableView != null) {
LOGGER.info("Select the existing booster table view.");
DockUtils.selectWindow(boosterTableView);
return boosterTableView;
}
boosterTableModel =
new BoosterTableModel((text, duration) -> statusBar.setStatusText(text, duration), consoleService);
boosterTableView = new BoosterTableView(this, boosterTableModel);
// add the log panel next to the console panel
DockableState[] dockables = desktop.getDockables();
LOGGER.info("Current dockables: {}", new Object[] { dockables });
if (desktop.getDockables().length > 1) {
DockableState consoleView = null;
// search the console view
for (DockableState dockable : dockables) {
if (DockKeys.DOCKKEY_CONSOLE_VIEW.equals(dockable.getDockable().getDockKey())) {
LOGGER.info("Found the console view dockable.");
consoleView = dockable;
break;
}
else if (DockKeys.DOCKKEY_DEBUG_CONSOLE_VIEW.equals(dockable.getDockable().getDockKey())) {
LOGGER.info("Found the debug console view dockable.");
consoleView = dockable;
break;
}
}
Dockable dock = desktop.getDockables()[1].getDockable();
if (consoleView != null) {
LOGGER.info("Add the booster table view to the console view panel.");
dock = consoleView.getDockable();
int order = 0;
LOGGER.info("Add new booster table at order: {}", order);
desktop.createTab(dock, boosterTableView, order, true);
TabbedDockableContainer baseTab = DockingUtilities.findTabbedDockableContainer(dock);
baseTab.removeDockable(boosterTableView);
baseTab.addDockable(boosterTableView, order);
baseTab.setSelectedDockable(boosterTableView);
}
else if (desktop.getDockables().length > 1) {
desktop.split(dock, boosterTableView, DockingConstants.SPLIT_BOTTOM);
desktop.setDockableHeight(boosterTableView, 0.2d);
}
else {
desktop.split(dock, boosterTableView, DockingConstants.SPLIT_RIGHT);
}
}
else {
desktop.addDockable(boosterTableView, RelativeDockablePosition.RIGHT);
}
// create the nodeList listener
final NodeListListener nodeListListener = new DefaultNodeListListener() {
@Override
public void listNodeAdded(final NodeInterface node) {
LOGGER.info("The nodelist has a new node: {}", node);
nodeNew(node);
}
@Override
public void listNodeRemoved(final NodeInterface node) {
LOGGER.info("The nodelist has a node removed: {}", node);
nodeLost(node);
}
};
// register as nodeList listener at the main model
this.nodeListProvider.addNodeListListener(nodeListListener);
try {
connectionService.subscribeConnectionStatusChanges(connectionInfo -> {
if (connectionInfo.getConnectionId().equals(ConnectionRegistry.CONNECTION_ID_MAIN)) {
LOGGER.info("Current state: {}", connectionInfo.getConnectionState());
switch (connectionInfo.getConnectionState().getActualPhase()) {
case CONNECTED:
LOGGER.info("The communication was opened.");
break;
case DISCONNECTED:
LOGGER.info("The communication was closed.");
List boosters = new LinkedList<>(boosterTableModel.getBoosters());
for (BoosterModel booster : boosters) {
SwingUtils.executeInEDT(() -> boosterTableModel.removeBooster(booster.getBooster()));
}
break;
default:
break;
}
}
}, error -> {
LOGGER.warn("The connection status change caused an error.", error);
});
final NodeProvider nodeProvider = this.nodeProviderSupplier.get();
if (nodeProvider != null) {
Collection nodes = nodeProvider.getNodes();
if (CollectionUtils.isNotEmpty(nodes)) {
for (NodeInterface node : nodes) {
LOGGER.info("Initially add node.");
nodeNew(node);
}
}
}
}
catch (Exception ex) {
LOGGER.warn("Register controller as connection status listener failed.", ex);
}
this.dockableStateChangeListener = new DockableStateChangeListener() {
@Override
public void dockableStateChanged(DockableStateChangeEvent event) {
if (event.getNewState().getDockable().equals(boosterTableView) && event.getNewState().isClosed()) {
LOGGER.info("BoosterTableView was closed, free resources.");
try {
desktop.removeDockableStateChangeListener(dockableStateChangeListener);
}
catch (Exception ex) {
LOGGER
.warn("Remove dockableStateChangeListener from desktop failed: "
+ dockableStateChangeListener, ex);
}
finally {
dockableStateChangeListener = null;
}
try {
// remove node listener from communication factory
if (nodeListListener != null) {
BoosterTableController.this.nodeListProvider.removeNodeListListener(nodeListListener);
}
}
catch (Exception ex) {
LOGGER.warn("Unregister controller as node listener failed.", ex);
}
// stop the booster current timer
if (boosterCurrentTimer != null) {
LOGGER.info("Stop the booster current timer.");
boosterCurrentTimer.stop();
boosterCurrentTimer = null;
}
// release the view instance
boosterTableView = null;
}
}
};
desktop.addDockableStateChangeListener(this.dockableStateChangeListener);
try {
// start the booster current timer
boosterCurrentTimer = new Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
LOGGER.trace("The booster current timer has elapsed.");
long now = System.currentTimeMillis();
try {
// trigger the model
List boosters = boosterTableModel.getBoosters();
for (BoosterModel boosterModel : boosters) {
Integer current = boosterModel.getCurrent();
if (current != null && current > 0
&& boosterModel.getLastCurrentUpdate() < (now - CURRENT_UPDATE_TIMEOUT)) {
// the current value is outdated -> clear the value
LOGGER
.info("the current value is outdated -> clear the value, booster: {}",
boosterModel);
boosterTableModel.setBoosterCurrent(boosterModel.getBooster(), null, null);
}
}
}
catch (Exception ex) {
LOGGER.warn("Clear the outdated booster current value failed.", ex);
}
}
});
boosterCurrentTimer.setCoalesce(true);
boosterCurrentTimer.start();
}
catch (Exception ex) {
LOGGER.warn("Start the booster current timer failed.", ex);
}
return boosterTableView;
}
private void nodeLost(final NodeInterface node) {
if (NodeUtils.hasBoosterFunctions(node.getUniqueId())
|| NodeUtils.hasCommandStationFunctions(node.getUniqueId())) {
LOGGER.info("Remove booster from model: {}", node);
SwingUtils.executeInEDT(() -> boosterTableModel.removeBooster(node));
}
}
private void nodeNew(final NodeInterface node) {
if (NodeUtils.hasBoosterFunctions(node.getUniqueId())
|| NodeUtils.hasCommandStationFunctions(node.getUniqueId())) {
LOGGER.info("New booster in system detected: {}", node);
SwingUtils.executeInEDT(() -> internalNewNode(node));
}
}
private void internalNewNode(final NodeInterface node) {
LOGGER.info("Add new booster to table: {}", node);
boosterTableModel.addBooster(node, this.wizardLabelWrapper);
// if the booster is not loaded we do not query the max current
if (StatusIdentifier.InitialLoadPending == node.getNodeLoadStatusIdentifier()) {
LOGGER
.info("The initial load of the booster node is still pending. Skip fetch booster state for node: {}",
node);
return;
}
fetchBoosterState(node);
}
private void fetchBoosterState(final NodeInterface node) {
LOGGER.info("Fetch the booster state.");
// get the command station state
try {
if (NodeUtils.hasBoosterFunctions(node.getUniqueId())) {
// trigger fetch the max current value
boosterTableModel.triggerFetchBoosterMaxCurrent(node);
// trigger the booster state
serviceWorker
.submit(() -> boosterService
.queryBoosterState(ConnectionRegistry.CONNECTION_ID_MAIN, node.getBoosterNode()));
}
if (NodeUtils.hasCommandStationFunctions(node.getUniqueId()) && node.getCommandStationNode() != null) {
// trigger the command station state
serviceWorker
.submit(() -> commandStationService
.queryCommandStationState(ConnectionRegistry.CONNECTION_ID_MAIN, node.getCommandStationNode()));
}
}
catch (Exception ex) {
LOGGER.warn("Get the booster and command station state failed.", ex);
// TODO set an error flag or something in the node
}
}
@Override
public void setBoosterState(final NodeInterface node, final BoosterStatus boosterStatus) {
LOGGER.info("Set the booster status, node: {}, boosterStatus: {}", node, boosterStatus);
try {
if (node.getBoosterNode() != null) {
this.boosterService
.setBoosterState(ConnectionRegistry.CONNECTION_ID_MAIN, node.getBoosterNode(), boosterStatus);
}
else {
this.boosterService
.setBoosterState(ConnectionRegistry.CONNECTION_ID_MAIN, node.getInterfaceNode(), boosterStatus);
}
}
catch (Exception ex) {
LOGGER.warn("Set the booster status failed.", ex);
// TODO set an error flag or something in the node
}
}
@Override
public void setCommandStationState(NodeInterface node, CommandStationState csStatus) {
LOGGER.info("Set the command station status, node: {}, csStatus: {}", node, csStatus);
try {
this.commandStationService
.setCommandStationState(ConnectionRegistry.CONNECTION_ID_MAIN, node.getCommandStationNode(),
CommandStationStatus.valueOf(csStatus));
}
catch (Exception ex) {
LOGGER.warn("Set the command station status failed.", ex);
// TODO set an error flag or something in the node
}
}
}