org.bidib.wizard.mvc.position.controller.FeedbackPositionController 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.position.controller;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
import javax.annotation.PostConstruct;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.bidib.jbidibc.core.schema.bidibbase.BaseLabel;
import org.bidib.jbidibc.core.schema.bidiblabels.NodeLabels;
import org.bidib.jbidibc.messages.enums.LcOutputType;
import org.bidib.jbidibc.messages.enums.PortConfigKeys;
import org.bidib.jbidibc.messages.utils.ProductUtils;
import org.bidib.wizard.api.locale.Resources;
import org.bidib.wizard.api.model.NodeInterface;
import org.bidib.wizard.api.model.NodeProvider;
import org.bidib.wizard.api.model.PositionAddressData;
import org.bidib.wizard.api.model.PositionFeedbackPort;
import org.bidib.wizard.api.model.RfBasisNode;
import org.bidib.wizard.api.model.connection.AbstractMessageEvent;
import org.bidib.wizard.api.model.connection.BidibConnection;
import org.bidib.wizard.api.model.connection.event.OccupancyPositionMessageEvent;
import org.bidib.wizard.api.model.listener.DefaultNodeListListener;
import org.bidib.wizard.api.model.listener.NodeListListener;
import org.bidib.wizard.client.common.model.listener.PortModelListener;
import org.bidib.wizard.client.common.view.DockKeys;
import org.bidib.wizard.client.common.view.DockUtils;
import org.bidib.wizard.common.exception.ConnectionException;
import org.bidib.wizard.common.labels.BidibLabelUtils;
import org.bidib.wizard.common.labels.LabelsChangedEvent;
import org.bidib.wizard.common.labels.WizardLabelFactory;
import org.bidib.wizard.common.labels.WizardLabelWrapper;
import org.bidib.wizard.core.model.connection.ConnectionRegistry;
import org.bidib.wizard.core.model.connection.MessageEventHandler;
import org.bidib.wizard.core.service.ConnectionService;
import org.bidib.wizard.mvc.main.controller.MainControllerInterface;
import org.bidib.wizard.mvc.main.view.exchange.NodeExchangeHelper;
import org.bidib.wizard.mvc.position.model.FeedbackPositionModel;
import org.bidib.wizard.mvc.position.model.FeedbackPositionTableModel;
import org.bidib.wizard.mvc.position.model.listener.DefaultFeedbackPositionListener;
import org.bidib.wizard.mvc.position.model.listener.FeedbackPositionListener;
import org.bidib.wizard.mvc.position.view.FeedbackPositionView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import com.vlsolutions.swing.docking.Dockable;
import com.vlsolutions.swing.docking.DockingDesktop;
import com.vlsolutions.swing.docking.RelativeDockablePosition;
import com.vlsolutions.swing.docking.event.DockableStateChangeEvent;
import com.vlsolutions.swing.docking.event.DockableStateChangeListener;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
public class FeedbackPositionController {
private static final Logger LOGGER = LoggerFactory.getLogger(FeedbackPositionController.class);
private FeedbackPositionView feedbackPositionView;
private FeedbackPositionModel feedbackPositionModel;
private FeedbackPositionListener feedbackPositionListener;
@Autowired
private ConnectionService connectionService;
@Autowired
private WizardLabelWrapper wizardLabelWrapper;
private final DockingDesktop desktop;
private DockableStateChangeListener dockableStateChangeListener;
private final MainControllerInterface mainController;
private final Supplier nodeProviderSupplier;
private CompositeDisposable compDispMessages;
public FeedbackPositionController(final DockingDesktop desktop, final Supplier nodeProviderSupplier,
final MainControllerInterface mainController) {
LOGGER.info("Create new instance of FeedbackPositionController.");
this.desktop = desktop;
this.nodeProviderSupplier = nodeProviderSupplier;
this.mainController = mainController;
}
@PostConstruct
public void initialize() {
feedbackPositionModel = new FeedbackPositionModel((positionId) -> {
final NodeLabels nodeLabels = getNodeLabels();
BaseLabel label = BidibLabelUtils.getFeedbackPositionLabel(nodeLabels, positionId);
return label;
});
prepareMessageMap();
// create the nodeList listener
final NodeListListener nodeListListener = new DefaultNodeListListener() {
@Override
public void listNodeAdded(NodeInterface node) {
LOGGER.info("The nodelist has a new node: {}", node);
nodeNew(node);
}
@Override
public void listNodeRemoved(NodeInterface node) {
LOGGER.info("The nodelist has a node removed: {}", node);
nodeLost(node);
}
};
// register as nodeList listener at the main controller
mainController.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.");
this.compDispMessages = new CompositeDisposable();
registerForMessages();
break;
case DISCONNECTED:
LOGGER.info("The communication was closed.");
if (compDispMessages != null) {
compDispMessages.dispose();
compDispMessages = null;
}
break;
default:
break;
}
}
}, error -> {
LOGGER.warn("The connection status change caused an error.", error);
});
}
catch (Exception ex) {
LOGGER.warn("Register controller as connection status listener failed.", ex);
}
try {
LOGGER.info("Check if the connection is connected already.");
boolean isConnected = connectionService.isConnected(ConnectionRegistry.CONNECTION_ID_MAIN);
if (isConnected) {
LOGGER.info("The connection is connected. Register for messages.");
if (this.compDispMessages == null) {
this.compDispMessages = new CompositeDisposable();
}
registerForMessages();
}
}
catch (Exception ex) {
LOGGER.warn("Register for messages failed.", ex);
}
final NodeProvider nodeProvider = this.nodeProviderSupplier.get();
if (nodeProvider != null) {
// initialize the node list
for (NodeInterface node : nodeProvider.getNodes()) {
nodeNew(node);
}
}
}
public void start() {
// check if the booster table view is already opened
String searchKey = DockKeys.FEEDBACK_POSITION_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;
}
LOGGER.info("Create new FeedbackPositionTableView.");
final FeedbackPositionTableModel tableModel = new FeedbackPositionTableModel();
feedbackPositionView = new FeedbackPositionView(this, feedbackPositionModel, tableModel);
// add the feedback position panel to the right of the node tree
if (desktop.getDockables().length > 1) {
Dockable dock = desktop.getDockables()[1].getDockable();
desktop.createTab(dock, feedbackPositionView, 1, true);
}
else {
desktop.addDockable(feedbackPositionView, RelativeDockablePosition.RIGHT);
}
// add the feedback port listener
feedbackPositionListener = new DefaultFeedbackPositionListener() {
@Override
public void addressesChanged(PositionFeedbackPort port, Collection addresses) {
LOGGER.info("The addresses have changed, port: {}, addresses: {}", port, addresses);
// feedbackPortTableModel.updatePort(port);
}
@Override
public void labelChanged(PositionFeedbackPort port, String label) {
LOGGER.info("The label has changed, port: {}, label: {}", port, label);
// TODO this is never called
port.setLabel(label);
final NodeInterface node = getSelectedNode();
if (node != null) {
// update the stored labels in the single base or the master base
try {
NodeLabels nodeLabels = getNodeLabels();
BidibLabelUtils.replaceFeedbackPositionLabel(nodeLabels, port.getId(), port.getLabel());
saveLabels();
}
catch (Exception ex) {
LOGGER.warn("Save feedback position labels failed.", ex);
String labelPath = ex.getMessage();
JOptionPane
.showMessageDialog(JOptionPane.getFrameForComponent(null),
Resources
.getString(NodeExchangeHelper.class, "labelfileerror.message",
new Object[] { labelPath }),
Resources.getString(NodeExchangeHelper.class, "labelfileerror.title"),
JOptionPane.ERROR_MESSAGE);
throw new RuntimeException(ex);
}
}
else {
LOGGER.warn("No node selected, labels are not stored.");
}
}
};
this.feedbackPositionModel.addFeedbackPortListener(feedbackPositionListener);
tableModel.setPortListener(new PortModelListener() {
@Override
public void labelChanged(PositionFeedbackPort port, String label) {
LOGGER.info("The label has changed, port: {}, label: {}", port, label);
final NodeInterface node = getSelectedNode();
if (node != null) {
// update the stored labels in the single base or the master base
try {
NodeLabels nodeLabels = getNodeLabels();
BidibLabelUtils.replaceFeedbackPositionLabel(nodeLabels, port.getId(), port.getLabel());
saveLabels();
}
catch (Exception ex) {
LOGGER.warn("Save feedback position labels failed.", ex);
String labelPath = ex.getMessage();
JOptionPane
.showMessageDialog(JOptionPane.getFrameForComponent(null),
Resources
.getString(NodeExchangeHelper.class, "labelfileerror.message",
new Object[] { labelPath }),
Resources.getString(NodeExchangeHelper.class, "labelfileerror.title"),
JOptionPane.ERROR_MESSAGE);
throw new RuntimeException(ex);
}
}
else {
LOGGER.warn("No node selected, labels are not stored.");
}
}
@Override
public void configChanged(PositionFeedbackPort port, PortConfigKeys... portConfigKeys) {
}
@Override
public void changePortType(LcOutputType portType, PositionFeedbackPort port) {
}
});
this.dockableStateChangeListener = new DockableStateChangeListener() {
@Override
public void dockableStateChanged(DockableStateChangeEvent event) {
if (event.getNewState().getDockable().equals(feedbackPositionView) && event.getNewState().isClosed()) {
LOGGER.info("The FeedbackPositionView was closed, free resources.");
try {
desktop.removeDockableStateChangeListener(dockableStateChangeListener);
}
catch (Exception ex) {
LOGGER
.warn("Remove dockableStateChangeListener from desktop failed: "
+ dockableStateChangeListener, ex);
}
finally {
dockableStateChangeListener = null;
}
try {
feedbackPositionView.cleanup();
}
catch (Exception ex) {
LOGGER.warn("Unregister controller as node listener failed.", ex);
}
}
}
};
desktop.addDockableStateChangeListener(this.dockableStateChangeListener);
}
private void registerForMessages() {
LOGGER.info("Register for messages from the connection.");
// register for messages from the connection
try {
BidibConnection connection = connectionService.find(ConnectionRegistry.CONNECTION_ID_MAIN);
Disposable dispMessages = connection.getSubjectMessages().subscribe(msg -> {
handleBidibMessageEvent(msg);
});
compDispMessages.add(dispMessages);
}
catch (ConnectionException ex) {
LOGGER.warn("No connection found, register on messages is skipped.", ex);
}
}
private final Map, MessageEventHandler> messageActionMap =
new HashMap<>();
private void prepareMessageMap() {
LOGGER.info("Prepare the message map.");
// add message events for occupancy position
messageActionMap.put(OccupancyPositionMessageEvent.class, evt -> {
OccupancyPositionMessageEvent event = (OccupancyPositionMessageEvent) evt;
LOGGER.info("Process the event: {}", event);
switch (event.getLocationType()) {
case LOCATOR_BADGE:
SwingUtilities.invokeLater(() -> {
LOGGER.info("Update the position: {}", event);
feedbackPositionModel
.addPosition(event.getAddress(), event.getDecoderAddress(), event.getLocationType(),
event.getLocationAddress(), null);
});
break;
case RFID_READOUT:
case AXLE_COUNTER:
SwingUtilities.invokeLater(() -> {
LOGGER.info("Update the position: {}", event);
feedbackPositionModel
.addPosition(event.getAddress(), event.getDecoderAddress(), event.getLocationType(),
event.getLocationAddress(), event.getExtendedData());
});
break;
default:
LOGGER.debug("Do not show position feedback with type: {}", event.getLocationType());
break;
}
});
}
private void handleBidibMessageEvent(AbstractMessageEvent event) {
LOGGER.debug("Handle the message event: {}", event);
// let the action update the cached value in the node
try {
MessageEventHandler action = messageActionMap.get(event.getClass());
if (action != null) {
action.handleMessageEvent(event);
}
else {
LOGGER.debug("No message event action configured for event: {}", event);
}
}
catch (Exception ex) {
LOGGER.warn("Execute the message event action failed, event: {}", event, ex);
}
}
protected void nodeLost(final NodeInterface node) {
}
private final Object rfBasisNodesLock = new Object();
protected void nodeNew(final NodeInterface node) {
if (ProductUtils.isRFBasisNode(node.getUniqueId())) {
synchronized (rfBasisNodesLock) {
LOGGER.info("Register new RF-Basis Node: {}", node);
RfBasisNode rfBasisNode = new RfBasisNode(node);
feedbackPositionModel.addRfBasisNode(rfBasisNode);
}
}
}
/**
* @return the selected node
*/
public NodeInterface getSelectedNode() {
synchronized (rfBasisNodesLock) {
RfBasisNode rfBasisNode =
// always return the single base or the master base
feedbackPositionModel
.getRfBasisNodes().stream()
.filter(rf -> (rf.getNode().getBaseNumber() == 0 || rf.getNode().getBaseNumber() == 1)).findFirst()
.orElse(null);
if (rfBasisNode != null) {
return rfBasisNode.getNode();
}
}
return null;
}
private NodeLabels getNodeLabels() {
final WizardLabelFactory wizardLabelFactory = wizardLabelWrapper.getWizardLabelFactory();
NodeLabels nodeLabels = wizardLabelFactory.loadLabels(getSelectedNode().getUniqueId());
return nodeLabels;
}
private void saveLabels() {
try {
long uniqueId = getSelectedNode().getUniqueId();
wizardLabelWrapper.saveNodeLabels(uniqueId);
}
catch (Exception e) {
LOGGER.warn("Save accessory labels failed.", e);
throw new RuntimeException(e);
}
}
@EventListener(LabelsChangedEvent.class)
public void labelsChangedEvent(LabelsChangedEvent labelsChangedEvent) {
LOGGER.info("The labels have changed, node: {}", labelsChangedEvent);
if (this.feedbackPositionView != null) {
SwingUtilities.invokeLater(() -> this.feedbackPositionView.refreshView());
}
}
}