org.bidib.wizard.mvc.pom.controller.PomProgrammerController 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.pom.controller;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.JFrame;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.collections4.Predicate;
import org.apache.commons.lang3.SystemUtils;
import org.bidib.jbidibc.messages.AddressData;
import org.bidib.jbidibc.messages.PomAddressData;
import org.bidib.jbidibc.messages.enums.AddressTypeEnum;
import org.bidib.jbidibc.messages.enums.CommandStationPom;
import org.bidib.jbidibc.messages.enums.CommandStationState;
import org.bidib.jbidibc.messages.enums.PomAcknowledge;
import org.bidib.jbidibc.messages.enums.PomAddressTypeEnum;
import org.bidib.jbidibc.messages.enums.PomOperation;
import org.bidib.jbidibc.messages.enums.PomProgState;
import org.bidib.jbidibc.messages.utils.ByteUtils;
import org.bidib.jbidibc.messages.utils.NodeUtils;
import org.bidib.wizard.api.locale.Resources;
import org.bidib.wizard.api.model.BoosterNodeInterface;
import org.bidib.wizard.api.model.CommandStationNodeInterface;
import org.bidib.wizard.api.model.NodeInterface;
import org.bidib.wizard.api.model.connection.AbstractMessageEvent;
import org.bidib.wizard.api.model.connection.event.CommandStationPomAcknowledgeMessageEvent;
import org.bidib.wizard.api.model.connection.event.CommandStationStateMessageEvent;
import org.bidib.wizard.api.model.connection.event.OccupancyCvMessageEvent;
import org.bidib.wizard.api.service.node.BoosterService;
import org.bidib.wizard.api.service.node.CommandStationService;
import org.bidib.wizard.client.common.view.WindowUtils;
import org.bidib.wizard.core.model.connection.ConnectionRegistry;
import org.bidib.wizard.core.model.connection.MessageAdapter;
import org.bidib.wizard.core.model.connection.MessageEventConsumer;
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.common.DialogRegistry;
import org.bidib.wizard.mvc.common.view.RegisteredDialog;
import org.bidib.wizard.mvc.pom.controller.listener.PomProgrammerControllerListener;
import org.bidib.wizard.mvc.pom.model.CurrentAddressBeanModel;
import org.bidib.wizard.mvc.pom.model.PomProgrammerModel;
import org.bidib.wizard.mvc.pom.view.OperationAbortedException;
import org.bidib.wizard.mvc.pom.view.PomProgrammerView;
import org.bidib.wizard.mvc.pom.view.listener.PomProgrammerViewListener;
import org.oxbow.swingbits.dialog.task.CommandLink;
import org.oxbow.swingbits.dialog.task.TaskDialogs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.vlsolutions.swing.docking.DockingDesktop;
public class PomProgrammerController {
private static final Logger LOGGER = LoggerFactory.getLogger(PomProgrammerController.class);
private final Collection listeners =
new LinkedList();
private final JFrame parent;
private final CommandStationNodeInterface node;
private final int x;
private final int y;
private final PomProgrammerModel model = new PomProgrammerModel();
private PomProgrammerView view;
@Autowired
private ConnectionService connectionService;
@Autowired
private BoosterService boosterService;
@Autowired
private CommandStationService commandStationService;
private MessageAdapter messageAdapter;
private static AtomicBoolean singleton = new AtomicBoolean();
private final DialogRegistry dialogRegistry;
public PomProgrammerController(final CommandStationNodeInterface node, final DialogRegistry dialogRegistry,
JFrame parent, int x, int y) {
this.parent = parent;
this.dialogRegistry = dialogRegistry;
this.node = node;
this.x = x;
this.y = y;
}
public static boolean isOpened() {
return singleton.get();
}
private void setOpened(boolean opened) {
singleton.set(opened);
}
public void addPomProgrammerControllerListener(PomProgrammerControllerListener l) {
listeners.add(l);
}
private void fireClose() {
for (PomProgrammerControllerListener l : listeners) {
l.close();
}
// reset the opened flag
setOpened(false);
}
private void fireSendRequest(PomAddressData decoderAddress, PomOperation operation, int cvNumber, int cvValue) {
LOGGER
.info("Send CV request, decoder addr: {}, operation: {}, cvNumber: {}, value: {}", decoderAddress,
operation, cvNumber, cvValue);
CommandStationPom opCode = CommandStationPom.valueOf(ByteUtils.getLowByte(operation.getType()));
// clear the stored cv value in the programmer model
model.clearCvValue();
LOGGER.info("Prepared opCode: {}", opCode);
for (PomProgrammerControllerListener l : listeners) {
l.sendRequest(node, decoderAddress, opCode, cvNumber, cvValue);
}
}
public void start(final DockingDesktop desktop, final AddressData initialAddress) {
this.messageAdapter = new MessageAdapter(connectionService) {
@Override
protected void prepareMessageMap(
Map, MessageEventConsumer> messageActionMap) {
LOGGER.info("Prepare the message map.");
messageActionMap.put(CommandStationStateMessageEvent.class, (evt, node) -> {
CommandStationStateMessageEvent event = (CommandStationStateMessageEvent) evt;
CommandStationState commandStationState = event.getCommandStationState();
byte[] address = event.getAddress();
LOGGER
.info("The command station state has changed, address: {}, state: {}", address,
commandStationState);
if (Arrays.equals(node.getNode().getAddr(), address)) {
LOGGER.info("The state of the selected command station node has changed.");
model.setCommandStationState(commandStationState);
}
else {
LOGGER.warn("Another command station has changed the state.");
}
});
messageActionMap.put(CommandStationPomAcknowledgeMessageEvent.class, (evt, node) -> {
CommandStationPomAcknowledgeMessageEvent event = (CommandStationPomAcknowledgeMessageEvent) evt;
byte[] address = event.getAddress();
PomAddressData decoderAddress = event.getAddressData();
PomAcknowledge pomAcknowledge = event.getPomAcknowledge();
LOGGER
.info("POM ackn was received, node addr: {}, decoder address: {}, pomAcknowledge: {}", address,
decoderAddress, pomAcknowledge);
// TODO
});
messageActionMap.put(OccupancyCvMessageEvent.class, (evt, node) -> {
OccupancyCvMessageEvent event = (OccupancyCvMessageEvent) evt;
byte[] address = event.getAddress();
PomAddressData decoderAddress = event.getAddressData();
int cvNumber = event.getCvNumber();
int cvData = event.getCvValue();
LOGGER
.info("CV was received, node addr: {}, decoder address: {}, cvNumber: {}, cvData: {}", address,
decoderAddress, cvNumber, cvData);
updatePomProgState(PomProgState.POM_PROG_OKAY, decoderAddress, cvNumber, cvData);
});
}
@Override
protected void onDisconnect() {
if (view != null) {
view.close();
view = null;
}
super.onDisconnect();
}
};
messageAdapter.setNode(node.getNode());
messageAdapter.start();
// create the view
final CurrentAddressBeanModel currentAddressBeanModel = new CurrentAddressBeanModel();
if (initialAddress != null) {
LOGGER.info("Set the provided initial address: {}", initialAddress);
currentAddressBeanModel.setDccAddress(initialAddress.getAddress());
AddressTypeEnum addressTypeEnum = initialAddress.getType();
PomAddressTypeEnum addressType = null;
switch (addressTypeEnum) {
case ACCESSORY:
addressType = PomAddressTypeEnum.ACCESSORY;
break;
case EXTENDED_ACCESSORY:
addressType = PomAddressTypeEnum.EXTENDED_ACCESSORY;
break;
default:
addressType = PomAddressTypeEnum.LOCOMOTIVE;
break;
}
currentAddressBeanModel.setAddressType(addressType);
}
// check if a loco dialog with the same address exists
// List dialogRegistry =
// DefaultApplicationContext.getInstance().get(DefaultApplicationContext.KEY_DIALOGREGISTRY, List.class);
if (currentAddressBeanModel.getDccAddress() != null) {
if (CollectionUtils.isNotEmpty(dialogRegistry.getDialogRegistry())) {
String searchKey = PomProgrammerView.prepareKey(currentAddressBeanModel.getDccAddress());
RegisteredDialog existingDialog =
IterableUtils.find(dialogRegistry.getDialogRegistry(), new Predicate() {
@Override
public boolean evaluate(RegisteredDialog dialog) {
return searchKey.equals(dialog.getKey());
}
});
if (existingDialog != null) {
LOGGER.info("Found existing dialog: {}", existingDialog);
try {
if (SystemUtils.IS_OS_WINDOWS) {
WindowUtils.bringWindowToFront(existingDialog.getWindow());
}
else {
existingDialog.getWindow().toFront();
}
return;
}
catch (Exception ex) {
LOGGER.warn("Bring the existing dialog to front failed.");
}
}
else {
LOGGER.info("No existing dialog found.");
}
}
}
view = new PomProgrammerView(model, currentAddressBeanModel);
view.addPomProgrammerViewListener(new PomProgrammerViewListener() {
@Override
public void close() {
// compDispMessages.dispose();
messageAdapter.dispose();
RegisteredDialog registeredDialog = view;
// unregister the dialog in the dialog registry
unregisterView(registeredDialog);
fireClose();
}
@Override
public void sendRequest(PomAddressData decoderAddress, PomOperation operation, int cvNumber, int cvValue) {
fireSendRequest(decoderAddress, operation, cvNumber, cvValue);
}
@Override
public boolean sendCommandStationStateRequest(boolean activate) {
LOGGER.info("Set the command station to active mode: {}", activate);
// check if the command station is running and start command station if not in running mode
final CommandStationStatus commandStationState =
commandStationService.queryCommandStationStateBlocking(ConnectionRegistry.CONNECTION_ID_MAIN, node);
// query the boosters in the system
List boostersToStart = new LinkedList<>();
for (BoosterNodeInterface node : boosterService
.getBoosterNodes(ConnectionRegistry.CONNECTION_ID_MAIN)) {
LOGGER.info("+++ Query the booster state for node: {}", node);
BoosterStatus boosterState = node.getBoosterStatus();
LOGGER.info("+++ The current boosterState: {}", boosterState);
// if a booster has no power or is off because of short detected we must show an error
// message
if (boosterState == null) {
// TODO the booster state was not delivered -> show an error
}
else if (BoosterStatus.isOffState(boosterState)) {
LOGGER.info("The current booster is off: {}", node);
boostersToStart.add(node);
}
}
//
if (CommandStationStatus.isOffState(commandStationState)
|| CollectionUtils.isNotEmpty(boostersToStart)) {
// ask the user if he wants to activate the command station
List commandLinks = new LinkedList<>();
commandLinks
.add(new CommandLink(
Resources.getString(PomProgrammerController.class, "activate_booster_and_commandstation"),
Resources
.getString(PomProgrammerController.class, "activate_booster_and_commandstation.text")));
commandLinks
.add(new CommandLink(
Resources
.getString(PomProgrammerController.class, "do_not_activate_booster_and_commandstation"),
Resources
.getString(PomProgrammerController.class,
"do_not_activate_booster_and_commandstation.text")));
if (SystemUtils.IS_OS_MAC_OSX) {
commandLinks
.add(
new CommandLink(Resources.getString(PomProgrammerController.class, "cancel_pom_dialog"),
Resources.getString(PomProgrammerController.class, "cancel_pom_dialog.text")));
}
int choice =
TaskDialogs
.build(parent, Resources.getString(PomProgrammerController.class, "message-warn"),
Resources.getString(PomProgrammerController.class, "message"))
.title(Resources.getString(PomProgrammerController.class, "title")).choice(0, commandLinks);
LOGGER.info("User selected option: {}", choice);
switch (choice) {
case -1:
LOGGER.info("User cancelled pomConfirmDialog.");
throw new OperationAbortedException("User cancelled pomConfirmDialog.");
case 0:
if (NodeUtils.hasBoosterFunctions(node.getNode().getUniqueId())) {
LOGGER.info("Activate the booster!");
boosterService
.setBoosterState(ConnectionRegistry.CONNECTION_ID_MAIN,
node.getNode().getBoosterNode(), BoosterStatus.ON);
}
else {
LOGGER.info("The command station has no booster!");
}
LOGGER.info("Activate the command station!");
commandStationService
.setCommandStationState(ConnectionRegistry.CONNECTION_ID_MAIN, node,
CommandStationStatus.GO_IGN_WD);
for (BoosterNodeInterface booster : boostersToStart) {
LOGGER.info("Start the booster: {}", booster);
boosterService
.setBoosterState(ConnectionRegistry.CONNECTION_ID_MAIN,
booster.getNode().getBoosterNode(), BoosterStatus.ON);
}
break;
case 1:
break;
case 2:
LOGGER.info("User cancelled pomConfirmDialog.");
throw new OperationAbortedException("User cancelled pomConfirmDialog.");
default:
break;
}
return true;
}
else {
LOGGER.info("Set the command station state: {}", commandStationState);
model
.setCommandStationState(
commandStationState != null ? commandStationState.getCommandStationState() : null);
}
return false;
}
});
LOGGER.info("Initialize the view.");
setOpened(true);
view.showDialog(parent, x, y);
// Register the loco dialog
LOGGER.info("Register the LocoDialog in the dialog registry: {}", view);
dialogRegistry.getDialogRegistry().add(view);
LOGGER.info("The POM programmer dialog is now displayed.");
}
private void updatePomProgState(
PomProgState pomProgState, PomAddressData decoderAddress, int cvNumber, int cvValue) {
model.updatePomProgResult(pomProgState, decoderAddress, cvNumber, cvValue);
}
private void unregisterView(RegisteredDialog view) {
if (view != null) {
try {
if (CollectionUtils.isNotEmpty(dialogRegistry.getDialogRegistry())) {
String searchKey = view.getKey();
RegisteredDialog existingDialog =
IterableUtils.find(dialogRegistry.getDialogRegistry(), new Predicate() {
@Override
public boolean evaluate(RegisteredDialog dialog) {
return searchKey.equals(dialog.getKey());
}
});
if (existingDialog != null) {
LOGGER.info("Found existing dialog to unregister: {}", existingDialog);
dialogRegistry.getDialogRegistry().remove(existingDialog);
LOGGER.info("Registry after remove: {}", dialogRegistry);
}
}
}
catch (Exception ex) {
LOGGER.warn("Unregister view failed: {}", view, ex);
}
}
else {
LOGGER.info("No view available to unregister.");
}
}
}