
org.bidib.wizard.mvc.pomupdate.controller.PomUpdateController Maven / Gradle / Ivy
package org.bidib.wizard.mvc.pomupdate.controller;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.bidib.jbidibc.core.AddressData;
import org.bidib.jbidibc.core.BidibLibrary;
import org.bidib.jbidibc.core.DefaultMessageListener;
import org.bidib.jbidibc.core.Feature;
import org.bidib.jbidibc.core.MessageListener;
import org.bidib.jbidibc.core.enumeration.AddressTypeEnum;
import org.bidib.jbidibc.core.enumeration.CommandStationPom;
import org.bidib.jbidibc.core.enumeration.PomAcknowledge;
import org.bidib.jbidibc.core.message.CommandStationPomMessage;
import org.bidib.jbidibc.core.utils.ByteUtils;
import org.bidib.wizard.comm.Communication;
import org.bidib.wizard.comm.CommunicationFactory;
import org.bidib.wizard.locale.Resources;
import org.bidib.wizard.mvc.common.view.DockKeys;
import org.bidib.wizard.mvc.main.controller.MainControllerInterface;
import org.bidib.wizard.mvc.main.model.MainModel;
import org.bidib.wizard.mvc.main.model.Node;
import org.bidib.wizard.mvc.main.model.listener.NodeListListener;
import org.bidib.wizard.mvc.pomupdate.model.Decoder;
import org.bidib.wizard.mvc.pomupdate.model.PomUpdateModel;
import org.bidib.wizard.mvc.pomupdate.view.PomUpdateView;
import org.bidib.wizard.mvc.pomupdate.view.listener.DecoderInfoStatusListener;
import org.bidib.wizard.mvc.pomupdate.view.listener.PomUpdatePerformStatusListener;
import org.bidib.wizard.mvc.pomupdate.view.listener.PomUpdateStatusListener;
import org.bidib.wizard.mvc.pomupdate.view.listener.PomUpdateViewListener;
import org.bidib.wizard.mvc.preferences.model.Preferences;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
public class PomUpdateController implements NodeListListener {
private static final Logger LOGGER = LoggerFactory.getLogger(PomUpdateController.class);
private static final int MIN_POM_REPEAT = 6;
private final DockingDesktop desktop;
private final Node node;
private final MainModel mainModel;
private PomUpdateModel pomUpdateModel;
private PomUpdateView pomUpdateView;
private MessageListener messageListener;
private MainControllerInterface mainController;
private Node commandStationNode;
private int pomRepeat = 1;
protected final ScheduledExecutorService updateWorker = Executors.newScheduledThreadPool(1);
public PomUpdateController(final MainModel mainModel, final DockingDesktop desktop, final Node node) {
this.mainModel = mainModel;
this.desktop = desktop;
this.node = node;
}
public void start(final MainControllerInterface mainController) {
this.mainController = mainController;
// check if the pom update view is already opened
String searchKey = DockKeys.POM_UPDATE_VIEW;
LOGGER.info("Search for view with key: {}", searchKey);
Dockable view = desktop.getContext().getDockableByKey(searchKey);
if (view != null) {
LOGGER.info("Select the existing pom update view instead of open a new one.");
selectWindow(view);
return;
}
LOGGER.info("Create new PomUpdateView.");
pomUpdateModel = new PomUpdateModel();
pomUpdateView = new PomUpdateView(desktop, pomUpdateModel);
DockableState[] dockables = desktop.getDockables();
LOGGER.info("Current dockables: {}", new Object[] { dockables });
if (dockables.length > 1) {
DockableState tabPanelNodeDetails = null;
// search the node details tab panel
for (DockableState dockable : dockables) {
if (DockKeys.DOCKKEY_TAB_PANEL.equals(dockable.getDockable().getDockKey())) {
LOGGER.info("Found the tab panel dockable.");
tabPanelNodeDetails = dockable;
break;
}
}
Dockable dock = desktop.getDockables()[1].getDockable();
if (tabPanelNodeDetails != null) {
LOGGER.info("Add the pom update view next to the node details panel.");
dock = tabPanelNodeDetails.getDockable();
TabbedDockableContainer container = DockingUtilities.findTabbedDockableContainer(dock);
int order = 0;
if (container != null) {
order = container.getTabCount();
}
LOGGER.info("Add new pomUpdateView at order: {}", order);
desktop.createTab(dock, pomUpdateView, order, true);
}
else {
desktop.split(dock, pomUpdateView, DockingConstants.SPLIT_RIGHT);
}
}
else {
desktop.addDockable(pomUpdateView, RelativeDockablePosition.RIGHT);
}
final Communication communication = CommunicationFactory.getInstance();
pomUpdateView.addPomUpdateViewListener(new PomUpdateViewListener() {
@Override
public void close() {
if (messageListener != null) {
LOGGER.info("Remove the message listener.");
communication.removeMessageListener(messageListener);
messageListener = null;
}
// fireClose();
}
@Override
public void prepareUpdate(
final Map> prepareUpdateMap,
final PomUpdateStatusListener statusListener) {
LOGGER.info("Prepare the POM update for decoders.");
final Node commandStationNode = getCommandStationNode(communication);
if (commandStationNode == null) {
LOGGER.warn("No command station node available! Operation aborted!");
// show dialog
JOptionPane.showMessageDialog(null,
Resources.getString(PomUpdateController.class, "no_commandstation_available"));
return;
}
Runnable runnable = new Runnable() {
public void run() {
for (Entry> entry : prepareUpdateMap.entrySet()) {
Decoder decoder = entry.getKey();
LOGGER.debug("Reset the update prepare status for decoder: {}", decoder);
int progress = 0;
statusListener.updateStatus(decoder, progress);
}
for (Entry> entry : prepareUpdateMap.entrySet()) {
Decoder decoder = entry.getKey();
LOGGER.info("Prepare the decoder: {}", decoder);
int progress = 0;
statusListener.updateStatus(decoder, progress);
List pomMessages = entry.getValue();
for (CommandStationPomMessage pomMessage : pomMessages) {
PomAcknowledge ack =
communication.sendCvPomRequest(commandStationNode.getNode(),
pomMessage.getDecoderAddress(),
CommandStationPom.valueOf(ByteUtils.getLowByte(pomMessage.getOpCode())),
pomMessage.getCvNumber(), pomMessage.getCvValue());
LOGGER.info("Send POM was acknowledgded: {}", ack);
progress += 8;
statusListener.updateStatus(decoder, progress);
}
progress = 100;
statusListener.updateStatus(decoder, progress);
}
}
};
// Start the prepare update process
updateWorker.execute(runnable);
}
@Override
public void performUpdate(
final List updateMessages, final PomUpdatePerformStatusListener statusListener) {
statusListener.updateStatus(0);
LOGGER.info("Perform the POM update for decoders.");
final Node commandStationNode = getCommandStationNode(communication);
if (commandStationNode == null) {
LOGGER.warn("No command station node available! Operation aborted!");
// show dialog
JOptionPane.showMessageDialog(null,
Resources.getString(PomUpdateController.class, "no_commandstation_available"));
return;
}
final int initialDelay = Preferences.getInstance().getPomUpdateInitialDelay();
final int delayBetweenPackets = Preferences.getInstance().getPomUpdateDelayBetweenPackets();
// int resendPacketCount = Preferences.getInstance().getPomUpdateResendPacketCount();
// calculate how often the POM must be sent to make sure 5-6 repeats are sent
final int resendPomPacketCount = PomUpdateUtils.calculateResendPacketCount(pomRepeat, MIN_POM_REPEAT);
LOGGER.info(
"POM update, initialDelay: {}, delayBetweenPackets: {}, resendPacketCount: {}, pomRepeat: {}",
initialDelay, delayBetweenPackets, resendPomPacketCount, pomRepeat);
Runnable runnable = new Runnable() {
public void run() {
final int totalPackets = updateMessages.size();
LOGGER.info("Start sending update packets: {}", totalPackets);
int progress = 0;
int currentPacket = 0;
for (CommandStationPomMessage pomMessage : updateMessages) {
for (int currentResendCounter = 0; currentResendCounter < resendPomPacketCount; currentResendCounter++) {
PomAcknowledge ack =
communication.sendCvPomRequest(commandStationNode.getNode(),
pomMessage.getDecoderAddress(),
CommandStationPom.valueOf(ByteUtils.getLowByte(pomMessage.getOpCode())),
pomMessage.getCvNumber(), pomMessage.getCvValue());
LOGGER.info("Send POM was acknowledgded: {}, currentResendCounter: {}", ack,
currentResendCounter);
}
currentPacket++;
progress = (currentPacket * 100) / totalPackets;
statusListener.updateStatus(progress);
// wait some time after send packet
if (currentPacket == 1) {
LOGGER.info("Wait {}ms after send initiating packet.", initialDelay);
try {
Thread.sleep(initialDelay);
}
catch (InterruptedException ex) {
LOGGER.warn("Wait {}ms after send initiating packet failed.", initialDelay, ex);
}
}
else {
LOGGER.info("Wait {}ms after send pom packet.", delayBetweenPackets);
try {
Thread.sleep(delayBetweenPackets);
}
catch (InterruptedException ex) {
LOGGER.warn("Wait {}ms after send pom packet failed.", delayBetweenPackets, ex);
}
}
}
statusListener.finished();
}
};
// Start the prepare update process
updateWorker.execute(runnable);
}
@Override
public void performLoadDecoderInfo(
final List decodersToLoadInfo, final DecoderInfoStatusListener statusListener) {
LOGGER.info("Load decoder info for decoders: {}", decodersToLoadInfo);
statusListener.updateStatus(0);
final Node commandStationNode = getCommandStationNode(communication);
if (commandStationNode == null) {
LOGGER.warn("No command station node available! Operation aborted!");
// show dialog
JOptionPane.showMessageDialog(null,
Resources.getString(PomUpdateController.class, "no_commandstation_available"));
return;
}
Runnable runnable = new Runnable() {
public void run() {
final int totalDecoders = decodersToLoadInfo.size();
LOGGER.info("Start get decoder info for total decoder count: {}", totalDecoders);
int progress = 0;
int currentDecoder = 0;
for (Decoder decoder : decodersToLoadInfo) {
// fetch CV 7 and CV 8 for every decoder
AddressData addressData =
new AddressData(decoder.getAddress(), AddressTypeEnum.LOCOMOTIVE_FORWARD);
PomAcknowledge ack =
communication.sendReadCvPomRequest(commandStationNode.getNode(), addressData,
CommandStationPom.RD_BYTE, 7 /* CV 7 : decoder version */);
LOGGER.info("Send read POM CV 7 was acknowledgded: {}", ack);
ack =
communication.sendReadCvPomRequest(commandStationNode.getNode(), addressData,
CommandStationPom.RD_BYTE, 8 /* CV 8 : manufacturer */);
LOGGER.info("Send read POM CV 8 was acknowledgded: {}", ack);
currentDecoder++;
progress = (currentDecoder * 100) / totalDecoders;
statusListener.updateStatus(progress);
}
LOGGER.info("Send POM messages to get the decoder info has finished. Wait for the answers.");
statusListener.finished();
}
};
// Start the prepare update process
updateWorker.execute(runnable);
}
});
// add the node list listener
mainModel.addNodeListListener(this);
messageListener = new DefaultMessageListener() {
@Override
public void feedbackCv(byte[] address, final AddressData decoderAddress, int cvNumber, int cvData) {
LOGGER.info("CV was received, node addr: {}, decoder address: {}, cvNumber: {}, cvData: {}", address,
decoderAddress, cvNumber, cvData);
updatePomFeedback(decoderAddress, cvNumber, cvData);
// special processing for Tams decoder
if (cvNumber == 8 && cvData == 62) {
// Tams is manufaturer
LOGGER.info("Prepare to get the product id for the Tams decoder from CV 803.");
Runnable runnable = new Runnable() {
public void run() {
LOGGER.info("Get the product id for the Tams decoder from CV 803.");
final Node commandStationNode = getCommandStationNode(communication);
if (commandStationNode == null) {
LOGGER.warn("No command station node available! Operation aborted!");
}
else {
PomAcknowledge ack =
communication.sendReadCvPomRequest(commandStationNode.getNode(), decoderAddress,
CommandStationPom.RD_BYTE, 803 /* CV 803 : decoder identification */);
LOGGER.info("Send read POM CV 803 was acknowledgded: {}", ack);
}
}
};
// Start the prepare update process
updateWorker.execute(runnable);
}
}
};
communication.addMessageListener(messageListener);
}
private Node getCommandStationNode(final Communication communication) {
// final Communication communication = CommunicationFactory.getInstance();
if (commandStationNode == null) {
// search the command station node
Collection nodes = mainController.getNodes();
for (Node node : nodes) {
if (node.isCommandStation()) {
commandStationNode = node;
LOGGER.info("Found a command station node: {}", commandStationNode);
Feature genPomRepeat =
communication.readFeature(node.getNode(), BidibLibrary.FEATURE_GEN_POM_REPEAT, true);
if (genPomRepeat != null) {
pomRepeat = genPomRepeat.getValue();
LOGGER.info("Fetched pomRepeat count from command station node: ", pomRepeat);
}
else {
LOGGER.info("The command station node does not provide the feature FEATURE_GEN_POM_REPEAT.");
pomRepeat = 1;
}
break;
}
}
}
return commandStationNode;
}
@Override
public void nodeChanged() {
LOGGER.debug("The node has changed, current node in model: {}", node);
if (SwingUtilities.isEventDispatchThread()) {
internalNodeChanged();
}
else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
internalNodeChanged();
}
});
}
}
private void internalNodeChanged() {
LOGGER.debug("handle node has changed, node: {}", node);
if (node != null && node.equals(mainModel.getSelectedNode())) {
LOGGER.debug("The node in the model has not changed.");
return;
}
// check if we must close the pomUpdate view
if (pomUpdateView != null) {
LOGGER.info("Close the pomUpdate view: {}", pomUpdateView);
desktop.close(pomUpdateView);
}
// unregister node list listener
mainModel.removeNodeListListener(this);
}
@Override
public void listChanged() {
// TODO Auto-generated method stub
}
@Override
public void nodeStateChanged() {
}
@Override
public void listNodeAdded(Node node) {
}
@Override
public void listNodeRemoved(Node node) {
// TODO Auto-generated method stub
}
private void selectWindow(Dockable dockable) {
TabbedDockableContainer container = DockingUtilities.findTabbedDockableContainer(dockable);
if (container != null) {
container.setSelectedDockable(dockable);
}
else {
LOGGER.warn("Container not available, select component directly.");
dockable.getComponent().requestFocusInWindow();
}
}
private void updatePomFeedback(AddressData decoderAddress, int cvNumber, int cvData) {
pomUpdateModel.updatePomFeedback(decoderAddress, cvNumber, cvData);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy