
org.bidib.wizard.mvc.main.controller.MainController Maven / Gradle / Ivy
package org.bidib.wizard.mvc.main.controller;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.swing.ImageIcon;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.Predicate;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.bidib.jbidibc.core.BidibLibrary;
import org.bidib.jbidibc.core.ConnectionListener;
import org.bidib.jbidibc.core.Feature;
import org.bidib.jbidibc.core.NodeListener;
import org.bidib.jbidibc.core.ProtocolVersion;
import org.bidib.jbidibc.core.SoftwareVersion;
import org.bidib.jbidibc.core.StringData;
import org.bidib.jbidibc.core.enumeration.IoBehaviourEnum;
import org.bidib.jbidibc.core.enumeration.LcMacroState;
import org.bidib.jbidibc.core.enumeration.LcOutputType;
import org.bidib.jbidibc.core.enumeration.PortConfigKeys;
import org.bidib.jbidibc.core.enumeration.PortConfigStatus;
import org.bidib.jbidibc.core.enumeration.SysErrorEnum;
import org.bidib.jbidibc.core.exception.InvalidConfigurationException;
import org.bidib.jbidibc.core.exception.NoAnswerException;
import org.bidib.jbidibc.core.exception.PortNotFoundException;
import org.bidib.jbidibc.core.exception.PortNotOpenedException;
import org.bidib.jbidibc.core.exception.ReasonAware;
import org.bidib.jbidibc.core.helpers.Context;
import org.bidib.jbidibc.core.helpers.DefaultContext;
import org.bidib.jbidibc.core.node.BidibNode;
import org.bidib.jbidibc.core.node.ConfigurationVariable;
import org.bidib.jbidibc.core.node.RootNode;
import org.bidib.jbidibc.core.port.BytePortConfigValue;
import org.bidib.jbidibc.core.port.PortConfigValue;
import org.bidib.jbidibc.core.utils.ByteUtils;
import org.bidib.jbidibc.core.utils.NodeUtils;
import org.bidib.jbidibc.core.utils.ProductUtils;
import org.bidib.jbidibc.debug.DebugMessageListener;
import org.bidib.jbidibc.debug.DebugMessageReceiver;
import org.bidib.jbidibc.debug.DebugReader;
import org.bidib.jbidibc.exchange.vendorcv.VendorCvData;
import org.bidib.jbidibc.exchange.vendorcv.VendorCvFactory;
import org.bidib.jbidibc.ui.OS;
import org.bidib.wizard.comm.AnalogPortStatus;
import org.bidib.wizard.comm.BacklightPortStatus;
import org.bidib.wizard.comm.BidibStatus;
import org.bidib.wizard.comm.Communication;
import org.bidib.wizard.comm.CommunicationFactory;
import org.bidib.wizard.comm.InputPortStatus;
import org.bidib.wizard.comm.LightPortStatus;
import org.bidib.wizard.comm.MotorPortStatus;
import org.bidib.wizard.comm.ServoPortStatus;
import org.bidib.wizard.comm.SoundPortStatus;
import org.bidib.wizard.comm.SwitchPortStatus;
import org.bidib.wizard.comm.listener.CommunicationListener;
import org.bidib.wizard.dialog.FileDialog;
import org.bidib.wizard.labels.LabelType;
import org.bidib.wizard.labels.Labels;
import org.bidib.wizard.locale.Resources;
import org.bidib.wizard.main.DefaultApplicationContext;
import org.bidib.wizard.mvc.common.model.PreferencesPortType;
import org.bidib.wizard.mvc.main.model.Accessory;
import org.bidib.wizard.mvc.main.model.AccessoryFactory;
import org.bidib.wizard.mvc.main.model.AnalogPort;
import org.bidib.wizard.mvc.main.model.AnalogPortLabels;
import org.bidib.wizard.mvc.main.model.BacklightPort;
import org.bidib.wizard.mvc.main.model.BacklightPortLabels;
import org.bidib.wizard.mvc.main.model.DefaultTransferListener;
import org.bidib.wizard.mvc.main.model.FeedbackPort;
import org.bidib.wizard.mvc.main.model.FeedbackPortLabels;
import org.bidib.wizard.mvc.main.model.Flag;
import org.bidib.wizard.mvc.main.model.GenericPort;
import org.bidib.wizard.mvc.main.model.InputPort;
import org.bidib.wizard.mvc.main.model.InputPortLabels;
import org.bidib.wizard.mvc.main.model.LightPort;
import org.bidib.wizard.mvc.main.model.LightPortLabels;
import org.bidib.wizard.mvc.main.model.Macro;
import org.bidib.wizard.mvc.main.model.MacroFactory;
import org.bidib.wizard.mvc.main.model.MacroFactory.ExportFormat;
import org.bidib.wizard.mvc.main.model.MacroLabels;
import org.bidib.wizard.mvc.main.model.MacroRef;
import org.bidib.wizard.mvc.main.model.MacroSaveState;
import org.bidib.wizard.mvc.main.model.MainMessageListener;
import org.bidib.wizard.mvc.main.model.MainModel;
import org.bidib.wizard.mvc.main.model.MotorPort;
import org.bidib.wizard.mvc.main.model.MotorPortLabels;
import org.bidib.wizard.mvc.main.model.Node;
import org.bidib.wizard.mvc.main.model.NodeLabels;
import org.bidib.wizard.mvc.main.model.Port;
import org.bidib.wizard.mvc.main.model.ServoPort;
import org.bidib.wizard.mvc.main.model.ServoPortLabels;
import org.bidib.wizard.mvc.main.model.SoundPort;
import org.bidib.wizard.mvc.main.model.SoundPortLabels;
import org.bidib.wizard.mvc.main.model.SwitchPort;
import org.bidib.wizard.mvc.main.model.SwitchPortLabels;
import org.bidib.wizard.mvc.main.model.function.Function;
import org.bidib.wizard.mvc.main.model.listener.BacklightPortListener;
import org.bidib.wizard.mvc.main.model.listener.CvDefinitionRequestListener;
import org.bidib.wizard.mvc.main.model.listener.InputPortListener;
import org.bidib.wizard.mvc.main.model.listener.LightPortListener;
import org.bidib.wizard.mvc.main.model.listener.NodeListListener;
import org.bidib.wizard.mvc.main.model.listener.OutputListener;
import org.bidib.wizard.mvc.main.model.listener.ServoPortListener;
import org.bidib.wizard.mvc.main.model.listener.SwitchPortListener;
import org.bidib.wizard.mvc.main.view.MainNodeListListener;
import org.bidib.wizard.mvc.main.view.MainView;
import org.bidib.wizard.mvc.main.view.component.AccessoryFileDialog;
import org.bidib.wizard.mvc.main.view.component.LabeledDisplayItems;
import org.bidib.wizard.mvc.main.view.component.MacroFileDialog;
import org.bidib.wizard.mvc.main.view.menu.listener.MainMenuListener;
import org.bidib.wizard.mvc.main.view.panel.listener.AccessoryListListener;
import org.bidib.wizard.mvc.main.view.panel.listener.AccessoryTableListener;
import org.bidib.wizard.mvc.main.view.panel.listener.MacroListListener;
import org.bidib.wizard.mvc.main.view.panel.listener.MacroTableListener;
import org.bidib.wizard.mvc.main.view.panel.listener.StatusListener;
import org.bidib.wizard.mvc.main.view.statusbar.StatusBar;
import org.bidib.wizard.mvc.preferences.model.Preferences;
import org.bidib.wizard.mvc.script.view.NodeScripting;
import org.bidib.wizard.script.node.types.TargetType;
import org.bidib.wizard.utils.AccessoryLabelFactory;
import org.bidib.wizard.utils.AccessoryLabelUtils;
import org.bidib.wizard.utils.DockUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.vlsolutions.swing.docking.Dockable;
public class MainController implements MainControllerInterface {
private static final Logger LOGGER = LoggerFactory.getLogger(MainController.class);
private final MainModel model = new MainModel();
private MainView view;
private MainNodeListListener mainNodeListListener;
private Labels accessoryLabels;
private final AnalogPortLabels analogPortLabels = new AnalogPortLabels();
private final FeedbackPortLabels feedbackPortLabels = new FeedbackPortLabels();
private final InputPortLabels inputPortLabels = new InputPortLabels();
private final LightPortLabels lightPortLabels = new LightPortLabels();
private final BacklightPortLabels backlightPortLabels = new BacklightPortLabels();
private final MacroLabels macroLabels = new MacroLabels();
private final MotorPortLabels motorPortLabels = new MotorPortLabels();
private final NodeLabels nodeLabels = new NodeLabels();
private final ServoPortLabels servoPortLabels = new ServoPortLabels();
private final SoundPortLabels soundPortLabels = new SoundPortLabels();
private final SwitchPortLabels switchPortLabels = new SwitchPortLabels();
private Function>[] functions;
private boolean ignoreWaitTimeout;
private final Map newNodeCreatedThreadRegistry =
new LinkedHashMap();
private final ScheduledExecutorService selectedNodeChangeWorker = Executors.newScheduledThreadPool(1);
private Timer boosterCurrentTimer;
private static final long CURRENT_UPDATE_TIMEOUT = 3000;
public MainController() {
ignoreWaitTimeout = Preferences.getInstance().isIgnoreWaitTimeout();
LOGGER.info("Created MainController, ignoreWaitTimeout: {}", ignoreWaitTimeout);
}
/**
* Create a new wizard node and return the initialized wizard node.
*/
protected Node createNode(Communication communication, org.bidib.jbidibc.core.Node node) {
Node wizardNode = null;
LOGGER.info("Create new 'wizard' node from jbidibc.Node: {}", node);
if (communication != null && node != null) {
wizardNode = new Node(node);
communication.clearCachedData(node);
if (!communication.isNodeRegistered(node)) {
communication.registerNode(node);
}
boolean limitedNode = true;
int magic = BidibNode.BIDIB_MAGIC_UNKNOWN;
try {
magic = communication.getMagic(node);
}
catch (NoAnswerException ex) {
// the magic stays as BIDIB_MAGIC_UNKNOWN and this signals an error
LOGGER.warn("The node did not answer to the first getMagic request: {}", node, ex);
try {
Thread.sleep(500);
magic = communication.getMagic(node);
}
catch (NoAnswerException ex1) {
// the magic stays as BIDIB_MAGIC_UNKNOWN and this signals an error
LOGGER.warn("The node did not answer to the second getMagic request: {}", node, ex1);
}
catch (InterruptedException ex1) {
LOGGER.warn(
"The current thread was interrupted before the second getMagic request to node was sent: {}",
node, ex1);
}
}
switch (magic) {
case BidibNode.BIDIB_MAGIC_UNKNOWN:
// the node has an error
wizardNode.setNodeHasError(true);
break;
case BidibLibrary.BIDIB_BOOT_MAGIC:
LOGGER
.info(
"Node returned boot magic: {}. This is a limited node that does not answer to feature requests!",
magic);
wizardNode.setBootloaderNode(true);
// the node supports FW update by specification
wizardNode.setUpdatable(true);
break;
default:
LOGGER.info("Node returned magic: {}", magic);
// the node has no limitations
limitedNode = false;
// get the firmware version
SoftwareVersion softwareVersion = communication.getSoftwareVersion(node);
LOGGER.info("SW version of new node: {}", softwareVersion);
wizardNode.getNode().setSoftwareVersion(softwareVersion);
// get the protocol version
ProtocolVersion protocolVersion = communication.getProtocolVersion(node);
LOGGER.info("Protocol version of new node: {}", protocolVersion);
wizardNode.getNode().setProtocolVersion(protocolVersion);
// after we fetch the magic we must try to get the FEATURE_RELEVANT_PID_BITS
Feature relevantPidBits =
communication.readFeature(node, BidibLibrary.FEATURE_RELEVANT_PID_BITS, true);
if (relevantPidBits != null) {
wizardNode.getNode().setRelevantPidBits(relevantPidBits.getValue());
}
// get the FEATURE_STRING_SIZE
Feature stringSize = communication.readFeature(node, BidibLibrary.FEATURE_STRING_SIZE, true);
if (stringSize != null && stringSize.getValue() > 0) {
LOGGER.info("Node supports FEATURE_STRING_SIZE: {}", stringSize.getValue());
wizardNode.getNode().setStringSize(stringSize.getValue());
int namespace = StringData.NAMESPACE_NODE;
int index = 0;
String storedString = null;
try {
storedString = communication.getString(node, namespace, index);
}
catch (RuntimeException ex) {
LOGGER.warn("Fetch stored string failed, namespace: {}, index: {}", namespace, index, ex);
}
LOGGER.info("Fetched storedString[{}:{}]: {}", namespace, index, storedString);
wizardNode.getNode().setStoredString(index, storedString);
index++;
try {
storedString = communication.getString(node, namespace, index);
}
catch (RuntimeException ex) {
LOGGER.warn("Fetch stored string failed, namespace: {}, index: {}", namespace, index, ex);
}
LOGGER.info("Fetched storedString[{}:{}]: {}", namespace, index, storedString);
wizardNode.getNode().setStoredString(index, storedString);
}
else {
LOGGER.info("Node has not FEATURE_STRING_SIZE available or string size is 0.");
}
// check if the node supports FW update
wizardNode.setUpdatable(communication.isUpdatable(node));
if (ProductUtils.isOneBootloader(node.getUniqueId())) {
LOGGER.info("Found a OneBootloader node: {}", node);
}
break;
}
if (!wizardNode.isNodeHasError()) {
// collect some configuration of the node
wizardNode.setCommandStation(communication.isCommandStation(node));
if (wizardNode.isCommandStation()) {
LOGGER.info("Ask the commandstation node if the RailCom+ feature is available.");
wizardNode.setRailComPlusAvailable(communication.isRailComPlusAvailable(node));
LOGGER.info("Set the POM Update available for the command station node.");
wizardNode.setPomUpdateAvailable(true);
}
wizardNode.setBooster(communication.isBooster(node));
// check if the node has feedback ports to know its a feedback device
if (!limitedNode && NodeUtils.hasFeedbackFunctions(node.getUniqueId())) {
int numFeedbackPorts = communication.getFeedbackPorts(node).size();
LOGGER.info("Number of feedback ports: {}", numFeedbackPorts);
if (numFeedbackPorts > 0) {
LOGGER.debug("The new node has feedback ports.");
wizardNode.setAddressMessagesEnabled(communication.isAddressMessagesEnabled(node));
wizardNode.setFeedbackMessagesEnabled(communication.isFeedbackMessagesEnabled(node));
}
}
else {
LOGGER.debug("The new node has no feedback functions.");
}
// check if the node has switch functions
if (!limitedNode && NodeUtils.hasSwitchFunctions(node.getUniqueId())) {
Feature portFlatModel =
communication.readFeature(node, BidibLibrary.FEATURE_CTRL_PORT_FLAT_MODEL, true);
if (portFlatModel != null) {
wizardNode.getNode().setPortFlatModel(portFlatModel.getValue());
}
else {
LOGGER.info("The feature FEATURE_CTRL_PORT_FLAT_MODEL is not available.");
}
Feature switchPortConfigAvailable =
communication.readFeature(node, BidibLibrary.FEATURE_SWITCH_CONFIG_AVAILABLE, true);
if (switchPortConfigAvailable != null) {
wizardNode.getNode().setSwitchPortConfigAvailable(
switchPortConfigAvailable.getValue() == 1 ? true : false);
}
else {
LOGGER.info("The feature FEATURE_SPORT_CONFIG_AVAILABLE is not available.");
}
int numInputPorts = communication.getInputPorts(node).size();
LOGGER.debug("Number of input ports: {}", numInputPorts);
if (numInputPorts > 0) {
LOGGER.debug("The new node has input ports.");
wizardNode.setKeyMessagesEnabled(communication.isKeyMessagesEnabled(node));
}
int macroLength = communication.getMacroLength(node);
LOGGER.debug("Supported macro lenght: {}", macroLength);
if (macroLength > 0) {
LOGGER.debug("The new node has macros.");
// check from the features
wizardNode.setDccStartEnabled(communication.isDccStartEnabled(node));
wizardNode.setExternalStartEnabled(communication.isExternalStartEnabled(node));
}
wizardNode.setStorableMacroCount(communication.getStorableMacroCount(node));
}
else {
LOGGER.debug("The new node has no accessory functions.");
}
}
else {
LOGGER.error("The new node has errors: {}", wizardNode);
}
wizardNode.setLabel(nodeLabels.getLabel(node.getUniqueId()));
LOGGER.info("Prepared new 'wizard' node: {}", wizardNode);
}
return wizardNode;
}
@Override
public void clearNodes() {
LOGGER.info("Clear the nodes from the model.");
model.getStatusModel().setCd(false);
model.getStatusModel().setRx(false);
model.getStatusModel().setTx(false);
LOGGER.warn("Communication factory is not available!");
model.getStatusModel().setCd(false);
model.clearNodes();
}
@Override
public Collection getNodes() {
return model.getNodes();
}
private Node findNode(org.bidib.jbidibc.core.Node commNode) {
Node result = null;
if (commNode != null) {
for (Node node : model.getNodes()) {
if (node.getNode().getUniqueId() == commNode.getUniqueId()) {
result = node;
break;
}
}
}
return result;
}
private Function>[] getFunctions() {
Function>[] result = null;
if (functions != null) {
try {
result = new Function[functions.length];
for (int index = 0; index < functions.length; index++) {
if (functions[index] != null) {
result[index] = ((Function>) functions[index].clone());
}
}
}
catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
return result;
}
private void loadLabels() {
// make sure the labels directory is available and write enabled
String labelPath = Preferences.getInstance().getLabelPath();
LOGGER.info("Check if the directory for labels exists: {}", labelPath);
try {
Path dir = Paths.get(labelPath);
if (!Files.exists(dir)) {
LOGGER.info("Try to create the directory for labels: {}", labelPath);
try {
dir = Files.createDirectories(dir);
LOGGER.info("Created new directory for labels: {}", dir);
}
catch (IOException ioex) {
LOGGER.warn("Create new directory for labels failed.", ioex);
if (OS.isLinux() && labelPath.startsWith("/home/")) {
String userName = System.getProperty("user.name");
// check if the name is correct
int beginIndex = 6;
int endIndex = labelPath.indexOf("/", beginIndex);
String configuredUserName = labelPath.substring(beginIndex, endIndex);
if (!userName.equals(configuredUserName)) {
LOGGER
.warn(
"The current username '{}' does not match the configured username '{}' for the label path.",
userName, configuredUserName);
Object[] options =
{ Resources.getString(Labels.class, "labeldirerror.correct"),
Resources.getString(Labels.class, "labeldirerror.ignore") };
int answer =
JOptionPane.showOptionDialog(JOptionPane.getFrameForComponent(null), Resources
.getString(Labels.class, "labeldirerror.message_username_mismatch", new Object[] {
userName, configuredUserName, labelPath }), Resources.getString(Labels.class,
"labeldirerror.title"), JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE, null,
options, options[0]);
if (answer == JOptionPane.YES_OPTION) {
labelPath = "/home/" + userName + labelPath.substring(endIndex);
LOGGER.info("The user selected to correct the label path: {}", labelPath);
Preferences.getInstance().setLabelPath(labelPath);
Preferences.getInstance().save(null);
dir = Paths.get(labelPath);
if (!Files.exists(dir)) {
LOGGER.info("Try to create the directory for labels: {}", labelPath);
try {
dir = Files.createDirectories(dir);
LOGGER.info("Created new directory for labels: {}", dir);
}
catch (IOException ioex2) {
LOGGER.warn("Create new directory for labels failed.", ioex);
}
}
}
}
}
}
}
if (!Files.exists(dir)) {
LOGGER.warn("The directory for labels is not available. Check permission on path: {}", labelPath);
throw new IllegalStateException("The directory for labels is not available. Check permission on path: "
+ labelPath);
}
if (!Files.isWritable(dir)) {
LOGGER.warn("The directory for labels is not write enabled. Check permission on path: {}", labelPath);
throw new IllegalStateException(
"The directory for labels is not write enabled. Check permission on path: " + labelPath);
}
else {
LOGGER.info("The label directory is write enabled: {}", labelPath);
}
}
catch (Exception ex) {
LOGGER.warn(
"The directory for labels is not available or is not write enabled. Check permission on path: {}",
labelPath, ex);
JOptionPane.showMessageDialog(JOptionPane.getFrameForComponent(null),
Resources.getString(Labels.class, "labeldirerror.message", new Object[] { labelPath }),
Resources.getString(Labels.class, "labeldirerror.title"), JOptionPane.ERROR_MESSAGE);
}
analogPortLabels.load();
backlightPortLabels.load();
feedbackPortLabels.load();
inputPortLabels.load();
lightPortLabels.load();
motorPortLabels.load();
servoPortLabels.load();
soundPortLabels.load();
switchPortLabels.load();
nodeLabels.load();
// accessoryLabels.load();
AccessoryLabelFactory factory = new AccessoryLabelFactory();
accessoryLabels = factory.getAccessoryLabels(factory.getDefaultFileName());
macroLabels.load();
DefaultApplicationContext.getInstance().register(DefaultApplicationContext.KEY_ANALOGPORT_LABELS,
analogPortLabels);
DefaultApplicationContext.getInstance().register(DefaultApplicationContext.KEY_BACKLIGHTPORT_LABELS,
backlightPortLabels);
DefaultApplicationContext.getInstance().register(DefaultApplicationContext.KEY_FEEDBACKPORT_LABELS,
feedbackPortLabels);
DefaultApplicationContext.getInstance().register(DefaultApplicationContext.KEY_INPUTPORT_LABELS,
inputPortLabels);
DefaultApplicationContext.getInstance().register(DefaultApplicationContext.KEY_LIGHTPORT_LABELS,
lightPortLabels);
DefaultApplicationContext.getInstance().register(DefaultApplicationContext.KEY_MOTORPORT_LABELS,
motorPortLabels);
DefaultApplicationContext.getInstance().register(DefaultApplicationContext.KEY_SERVOPORT_LABELS,
servoPortLabels);
DefaultApplicationContext.getInstance().register(DefaultApplicationContext.KEY_SOUNDPORT_LABELS,
soundPortLabels);
DefaultApplicationContext.getInstance().register(DefaultApplicationContext.KEY_SWITCHPORT_LABELS,
switchPortLabels);
DefaultApplicationContext.getInstance().register(DefaultApplicationContext.KEY_NODE_LABELS, nodeLabels);
DefaultApplicationContext.getInstance().register(DefaultApplicationContext.KEY_MACRO_LABELS, macroLabels);
DefaultApplicationContext.getInstance().register(DefaultApplicationContext.KEY_ACCESSORY_LABELS,
accessoryLabels);
}
private void setDefaultCursor() {
view.setBusy(false);
}
private void setFunctions(Function>[] functions) {
if (functions != null) {
try {
this.functions = new Function[functions.length];
for (int index = 0; index < functions.length; index++) {
if (functions[index] != null) {
this.functions[index] = ((Function>) functions[index].clone());
}
}
}
catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
else {
this.functions = null;
}
}
private void setWaitCursor() {
view.setBusy(true);
}
/**
* Start the main controller.
*/
public void start() {
LOGGER.info("Start the main controller");
DefaultApplicationContext.getInstance().register(DefaultApplicationContext.KEY_PORTS_PROVIDER, model);
loadLabels();
// this initializes Bidib ...
CommunicationFactory.addTransferListener(new DefaultTransferListener(model));
CommunicationFactory.addMessageListener(new MainMessageListener(model));
CommunicationFactory.addNodeListener(new NodeListener() {
@Override
public void nodeLost(org.bidib.jbidibc.core.Node commNode) {
Node node = findNode(commNode);
LOGGER.info("Node lost, commNode: {}, node: {}", commNode, node);
if (node != null) {
LOGGER.info("Remove lost node from communication: {}", node);
Communication communication = CommunicationFactory.getInstance();
communication.removeNode(node.getNode());
model.removeNode(node);
// remove the node from the newNodeCreated-thread registry
synchronized (newNodeCreatedThreadRegistry) {
Thread newNodeCreatedThread = newNodeCreatedThreadRegistry.remove(commNode);
if (newNodeCreatedThread != null) {
try {
LOGGER.warn("Interrupt the newNodeCreatedThread: {}", newNodeCreatedThread);
if (newNodeCreatedThread.isAlive()) {
newNodeCreatedThread.interrupt();
}
}
catch (Exception ex) {
LOGGER.warn("Interrupt newNodeCreatedThread failed.", ex);
}
}
else {
LOGGER.info("The lost node is not in the registry of new created nodes threads.");
}
}
view.setStatusText(String.format(Resources.getString(MainController.class, "node-lost"), commNode),
StatusBar.DISPLAY_ERROR);
}
else {
LOGGER.debug("Node not found.");
}
}
@Override
public void nodeNew(final org.bidib.jbidibc.core.Node commNode) {
LOGGER.info("New node in system: {}", commNode);
// check if the root node is initialized ...
if (ByteUtils.arrayEquals(commNode.getAddr(), RootNode.ROOTNODE_ADDR)) {
LOGGER.info("Process the root node: {}", commNode);
}
else if (!CollectionUtils.isNotEmpty(getNodes())) {
LOGGER.warn("A new node is signaled but the root node was not processed yet. Skip the new node.");
return;
}
// TODO if an existing node is added with a new version this will not detect the problem!
// TODO change the logic here
LOGGER.info("Current newNodeCreatedThreadRegistry: {}", newNodeCreatedThreadRegistry);
Thread newNodeCreatedThread = null;
synchronized (newNodeCreatedThreadRegistry) {
org.bidib.jbidibc.core.Node foundKey =
org.apache.commons.collections4.CollectionUtils.find(newNodeCreatedThreadRegistry.keySet(),
new Predicate() {
@Override
public boolean evaluate(org.bidib.jbidibc.core.Node node) {
if (ByteUtils.arrayEquals(node.getAddr(), commNode.getAddr())
&& node.getUniqueId() == commNode.getUniqueId()) {
LOGGER.debug("Found equal node: {}", node);
return true;
}
return false;
}
});
if (foundKey != null) {
LOGGER.warn("The new node is already in the registration process: {}", commNode);
return;
}
// TODO we should better use a queue and let the network be read from only 1 thread
newNodeCreatedThread =
new Thread(new NewNodeReader(MainController.this, view, model, newNodeCreatedThreadRegistry,
commNode), "newNode-created-thread-" + String.valueOf(System.currentTimeMillis()));
// add the node to the newNodeCreated-thread registry
LOGGER
.info("Add new node to newNodeCreatedThreadRegistry and start thread, commNode: {}", commNode);
// synchronized (newNodeCreatedThreadRegistry) {
newNodeCreatedThreadRegistry.put(commNode, newNodeCreatedThread);
}
newNodeCreatedThread.start();
}
});
// create the main view
view = new MainView(model);
view.createComponents();
view.setIconImage(new ImageIcon(getClass().getResource("/icons/frameIcon_48x48.png")).getImage());
// layout the frame content
view.prepareFrame();
view.setVisible(true);
CommunicationFactory.addCommunicationListener(new CommunicationListener() {
@Override
public void opened(String port) {
view.setStatusText(Resources.getString(CommunicationFactory.class, "open-port-passed") + " " + port,
StatusBar.DISPLAY_NORMAL);
model.getStatusModel().setCd(true);
}
@Override
public void closed(final String port) {
// clear all nodes
if (SwingUtilities.isEventDispatchThread()) {
handleClosed(port);
}
else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
handleClosed(port);
}
});
}
}
private void handleClosed(final String port) {
LOGGER.info("Port was closed, set the status text and clear the nodes.");
if (StringUtils.isNotBlank(port)) {
view.setStatusText(Resources.getString(CommunicationFactory.class, "close-port-passed") + " "
+ port, StatusBar.DISPLAY_NORMAL);
}
else {
view.setStatusText(null, StatusBar.DISPLAY_NORMAL);
}
clearNodes();
model.getStatusModel().setCd(false);
model.signalResetInitialLoadFinished();
// release the cv definition
model.setCvDefinition(null);
}
@Override
public void initialized() {
}
@Override
public void status(String statusText, int displayDuration) {
view.setStatusText(statusText, displayDuration);
}
});
// display the connect hint in the status bar
view.setStatusText(Resources.getString(getClass(), "connectHint"), StatusBar.DISPLAY_NORMAL);
// add the node list listener
mainNodeListListener = new MainNodeListListener(view, model);
mainNodeListListener.setNodeLabels(nodeLabels);
mainNodeListListener.setAccessoryLabels(accessoryLabels);
mainNodeListListener.setMacroLabels(macroLabels);
mainNodeListListener.setPortLabels(analogPortLabels, feedbackPortLabels, inputPortLabels, lightPortLabels,
backlightPortLabels, motorPortLabels, servoPortLabels, soundPortLabels, switchPortLabels);
view.addNodeListListener(mainNodeListListener);
DefaultApplicationContext.getInstance().register(DefaultApplicationContext.KEY_MAINNODELISTLISTENER,
mainNodeListListener);
// add the node selection listener
view.addNodeListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(final ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
LOGGER.info("The list selection has changed on the node list: {}", e.getSource());
// use an executor
selectedNodeChangeWorker.schedule(new Runnable() {
@Override
public void run() {
LOGGER.info("Process node changed starting, Node-changed-thread-{}",
System.currentTimeMillis());
// TODO make sure the initial loading of the tree structure has finished
if (!model.isInitialLoadFinished()) {
LOGGER
.warn("The initial load of the nodeTab has not finished yet. Cancel selection and load of node details.");
return;
}
// select the node details tab
try {
String searchKey = "tabPanel";
LOGGER.info("Search for view with key: {}", searchKey);
Dockable tabPanel = view.getDesktop().getContext().getDockableByKey(searchKey);
DockUtils.selectWindow(tabPanel);
}
catch (Exception ex) {
LOGGER.warn("Select tabPanel failed.", ex);
}
try {
setWaitCursor();
StopWatch sw = new StopWatch();
sw.start();
final LabeledDisplayItems nodeList = (LabeledDisplayItems) e.getSource();
final Node node = nodeList.getSelectedItem();
// do not load the node if it is already selected
if (node != null && node.equals(model.getSelectedNode())) {
LOGGER.info("The node is already selected!");
return;
}
LOGGER.debug("Set the new selected node in the model: {}", node);
model.setSelectedNode(node);
view.setStatusText(
String.format(Resources.getString(MainController.class, "load-config"), node),
StatusBar.DISPLAY_NORMAL);
// fetch the cv definition
VendorCvData vendorCV = null;
if (node != null) {
try {
File file = new File("");
file = new File(file.getAbsoluteFile(), "data/BiDiBNodeVendorData");
String userHome = System.getProperty("user.home");
File searchPathUserHome =
new File(userHome, ".BiDiBWizard/data/BiDiBNodeVendorData");
String labelPath = Preferences.getInstance().getLabelPath();
File searchPathLabelPath = new File(labelPath, "data/BiDiBNodeVendorData");
vendorCV =
VendorCvFactory.getCvDefinition(node.getNode(),
searchPathUserHome.getAbsolutePath(),
searchPathLabelPath.getAbsolutePath(), file.getAbsolutePath(),
"classpath:/bidib");
}
catch (Exception ex) {
LOGGER.warn("Get CV definition for node failed.", ex);
}
if (vendorCV != null) {
node.setVendorCV(vendorCV);
node.prepareVendorCVTree();
}
else if (ProductUtils.isOneBootloader(node.getUniqueId())) {
LOGGER.info("The current node is a OneBootloader.");
}
}
model.setCvDefinition(vendorCV);
boolean nodeHasLimitations = true;
if (node != null) {
// ignore sys errors
nodeHasLimitations = node.isBootloaderNode() || node.isNodeHasError(true);
LOGGER.info("The node was checked for limitiations: {}", nodeHasLimitations);
}
if (!nodeHasLimitations && CommunicationFactory.getInstance() != null) {
getNodeConfiguration(node);
queryNodeStatus(node);
}
else {
// update all port lists with the values that are now available
model.updatePortLists();
}
sw.stop();
LOGGER.info("Process node changed took: {}", sw);
view.setStatusText(String.format(
Resources.getString(MainController.class, "load-config-finished"), node, sw),
StatusBar.DISPLAY_NORMAL);
}
catch (Exception ex) {
LOGGER.warn("Process node change caused an error: {}", ex);
}
finally {
setDefaultCursor();
LOGGER.info("Process node changed finished.");
}
}
}, 0, TimeUnit.MILLISECONDS);
}
}
});
view.addAccessoryListListener(new AccessoryListListener() {
@Override
public void exportAccessory(final Accessory accessory) {
// save the accessory
AccessoryFileDialog dialog = new AccessoryFileDialog(
view,
// default is legacy filter
FileDialog.SAVE, accessory) {
@Override
public void approve(String fileName) {
try {
ExportFormat exportFormat =
(getCheckUseLegacyFormat().isSelected() ? ExportFormat.serialization : ExportFormat.jaxb);
AccessoryFactory.saveAccessory(fileName, accessory, exportFormat);
// update the status bar
view.setStatusText(
String.format(Resources.getString(MainController.class, "exportedAccessory"), fileName),
StatusBar.DISPLAY_NORMAL);
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
};
dialog.showDialog();
}
@Override
public void importAccessory(final Accessory accessory) {
AccessoryFileDialog dialog = new AccessoryFileDialog(
view, FileDialog.OPEN, accessory) {
@Override
public void approve(String fileName) {
ExportFormat exportFormat =
(getCheckUseLegacyFormat().isSelected() ? ExportFormat.serialization : ExportFormat.jaxb);
Accessory accessory = AccessoryFactory.loadAccessory(fileName, exportFormat, model);
if (accessory != null) {
accessory.setId(model.getSelectedAccessory().getId());
// set the total number possible macros for this accessory
accessory.setMacroSize(CommunicationFactory.getInstance().getAccessoryLength(
model.getSelectedNode().getNode()));
model.replaceAccessory(accessory);
}
}
};
dialog.showDialog();
}
@Override
public void labelChanged(final Accessory accessory, String label) {
// final Accessory accessory = (Accessory) object;
// accessory.setLabel(label);
mainNodeListListener.replaceAccessoryLabel(model.getSelectedNode().getNode().getUniqueId(), accessory,
label, true);
}
@Override
public void reloadAccessory(Accessory accessory) {
if (accessory != null && accessory.hasPendingChanges()) {
// show dialog
int result =
JOptionPane.showConfirmDialog(view,
Resources.getString(MainController.class, "accessory_has_pending_changes"),
Resources.getString(MainController.class, "pending_changes"), JOptionPane.OK_CANCEL_OPTION);
if (result != JOptionPane.OK_OPTION) {
LOGGER.info("User canceled discard pending changes.");
return;
}
// reset pending changes
accessory.setPendingChanges(false);
}
CommunicationFactory.getInstance().reloadAccessory(model.getSelectedNode().getNode(), accessory);
accessory.setPendingChanges(false);
}
@Override
public void saveAccessory(Accessory accessory) {
CommunicationFactory.getInstance().saveAccessory(model.getSelectedNode().getNode(), accessory);
}
});
// add a list selection listener for the accessory list
view.addAccessoryListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(final ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
try {
// the selected accessory has changed
setWaitCursor();
final JList accessoryList = (JList) e.getSource();
Accessory accessory = accessoryList.getSelectedValue();
if (accessory != null) {
model.setSelectedAccessory(accessory);
}
}
finally {
setDefaultCursor();
}
}
}
});
view.addAccessoryTableListener(new AccessoryTableListener() {
@Override
public void delete(int[] rows) {
for (int index = rows.length - 1; index >= 0; index--) {
model.getSelectedAccessory().removeMacro(rows[index]);
}
}
private MacroRef findNextFreeMacro() {
SortedSet macroIds = new TreeSet<>();
for (Accessory accessory : model.getAccessories()) {
for (MacroRef aspect : accessory.getAspects()) {
macroIds.add(aspect.getId());
}
}
// macro id starts from 0
int macroId = 0;
for (Integer currentId : macroIds) {
if (currentId.intValue() == macroId) {
macroId++;
}
else {
LOGGER.info("Found free macroId: {}", macroId);
}
}
MacroRef macroRef = null;
if (macroId < model.getMacros().size()) {
LOGGER.info("Create new MacroRef with macroId: {}", macroId);
macroRef = new MacroRef(macroId);
}
else {
macroRef = new MacroRef();
}
return macroRef;
}
@Override
public void insertEmptyAfter(int row) {
MacroRef macroRef = findNextFreeMacro();
model.getSelectedAccessory().addAspectAfter(row, macroRef);
}
@Override
public void insertEmptyBefore(int row) {
MacroRef macroRef = findNextFreeMacro();
model.getSelectedAccessory().addAspectBefore(row >= 0 ? row : 0, macroRef);
}
@Override
public void testButtonPressed(int aspect) {
// TODO check if the accessory has pending changes
Accessory accessory = model.getSelectedAccessory();
if (accessory.hasPendingChanges()) {
// show a hint to transfer the macro to the node
int result =
JOptionPane.showConfirmDialog(view, Resources.getString(AccessoryTableListener.class,
"accessory_transfer_pending_changes_before_test"), Resources.getString(
AccessoryTableListener.class, "test"), JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE);
if (result == JOptionPane.YES_OPTION) {
CommunicationFactory.getInstance().saveAccessory(model.getSelectedNode().getNode(), accessory);
}
}
CommunicationFactory.getInstance().startAccessory(model.getSelectedNode().getNode(),
model.getSelectedAccessory(), aspect);
}
});
view.addAnalogPortListener(new OutputListener() {
@Override
public void labelChanged(Port port, String label) {
port.setLabel(label);
if (label != null && label.length() > 0) {
analogPortLabels.setLabel(model.getSelectedNode().getNode().getUniqueId(), port.getId(), label);
}
else {
analogPortLabels.removeLabel(model.getSelectedNode().getNode().getUniqueId(), port.getId());
}
try {
analogPortLabels.save();
}
catch (InvalidConfigurationException ex) {
LOGGER.warn("Save analog port labels failed.", ex);
String labelPath = ex.getReason();
JOptionPane.showMessageDialog(JOptionPane.getFrameForComponent(null),
Resources.getString(Labels.class, "labelfileerror.message", new Object[] { labelPath }),
Resources.getString(Labels.class, "labelfileerror.title"), JOptionPane.ERROR_MESSAGE);
}
}
@Override
public void statusChanged(Port port, AnalogPortStatus status) {
}
@Override
public void testButtonPressed(Port port) {
CommunicationFactory.getInstance().activateAnalogPort(model.getSelectedNode().getNode(), port.getId(),
(AnalogPortStatus) port.getStatus());
}
@Override
public void configChanged(Port port) {
}
});
view.addBoosterStatusListener(new StatusListener() {
@Override
public void switchedOff() {
CommunicationFactory.getInstance().boosterOff(model.getSelectedNode().getNode());
}
@Override
public void switchedOn() {
CommunicationFactory.getInstance().boosterOn(model.getSelectedNode().getNode());
}
});
view.addInputPortListener(new InputPortListener() {
@Override
public void labelChanged(Port port, String label) {
port.setLabel(label);
if (label != null && label.length() > 0) {
inputPortLabels.setLabel(model.getSelectedNode().getNode().getUniqueId(), port.getId(), label);
}
else {
inputPortLabels.removeLabel(model.getSelectedNode().getNode().getUniqueId(), port.getId());
}
try {
inputPortLabels.save();
}
catch (InvalidConfigurationException ex) {
LOGGER.warn("Save input port labels failed.", ex);
String labelPath = ex.getReason();
JOptionPane.showMessageDialog(JOptionPane.getFrameForComponent(null),
Resources.getString(Labels.class, "labelfileerror.message", new Object[] { labelPath }),
Resources.getString(Labels.class, "labelfileerror.title"), JOptionPane.ERROR_MESSAGE);
}
}
@Override
public void statusChanged(Port port, InputPortStatus status) {
}
@Override
public void configChanged(Port port) {
}
@Override
public void testButtonPressed(Port port) {
}
@Override
public void changePortType(LcOutputType portType, InputPort port) {
// TODO verify that this is not called twice! See listener in InputPortListPanel
LOGGER.info("The port type will change to: {}, port: {}", portType, port);
CommunicationFactory.getInstance().setPortParameters(model.getSelectedNode().getNode(), port.getId(),
portType, null);
}
@Override
public void valuesChanged(InputPort port, PortConfigKeys... portConfigKeys) {
}
});
view.addLightPortListener(new LightPortListener() {
@Override
public void valuesChanged(LightPort port) {
try {
CommunicationFactory.getInstance().setLightPortParameters(model.getSelectedNode().getNode(),
port.getId(), port.getPwmMin(), port.getPwmMax(), port.getDimMin(), port.getDimMax(),
port.getRgbValue());
}
catch (Exception ex) {
LOGGER.warn("Set the lightport parameters failed.", ex);
// model.getSelectedNode().setNodeHasError(true);
model.setNodeHasError(model.getSelectedNode(), true);
}
}
@Override
public void labelChanged(Port port, String label) {
port.setLabel(label);
if (label != null && label.length() > 0) {
lightPortLabels.setLabel(model.getSelectedNode().getNode().getUniqueId(), port.getId(), label);
}
else {
lightPortLabels.removeLabel(model.getSelectedNode().getNode().getUniqueId(), port.getId());
}
try {
lightPortLabels.save();
}
catch (InvalidConfigurationException ex) {
LOGGER.warn("Save light port labels failed.", ex);
String labelPath = ex.getReason();
JOptionPane.showMessageDialog(JOptionPane.getFrameForComponent(null),
Resources.getString(Labels.class, "labelfileerror.message", new Object[] { labelPath }),
Resources.getString(Labels.class, "labelfileerror.title"), JOptionPane.ERROR_MESSAGE);
}
}
@Override
public void statusChanged(Port port, LightPortStatus status) {
}
@Override
public void testButtonPressed(Port port) {
CommunicationFactory.getInstance().activateLightPort(model.getSelectedNode().getNode(), port.getId(),
(LightPortStatus) port.getStatus());
}
@Override
public void configChanged(Port port) {
}
});
// handle backlight
view.addBacklightPortListener(new BacklightPortListener() {
@Override
public void labelChanged(Port port, String label) {
port.setLabel(label);
if (label != null && label.length() > 0) {
backlightPortLabels.setLabel(model.getSelectedNode().getNode().getUniqueId(), port.getId(), label);
}
else {
backlightPortLabels.removeLabel(model.getSelectedNode().getNode().getUniqueId(), port.getId());
}
try {
backlightPortLabels.save();
}
catch (InvalidConfigurationException ex) {
LOGGER.warn("Save backlight port labels failed.", ex);
String labelPath = ex.getReason();
JOptionPane.showMessageDialog(JOptionPane.getFrameForComponent(null),
Resources.getString(Labels.class, "labelfileerror.message", new Object[] { labelPath }),
Resources.getString(Labels.class, "labelfileerror.title"), JOptionPane.ERROR_MESSAGE);
}
}
@Override
public void statusChanged(Port port, BacklightPortStatus status) {
LOGGER.debug("Status of backlight port has changed, port: {}, status: {}", port, status);
}
@Override
public void testButtonPressed(Port port) {
BacklightPort backlightPort = (BacklightPort) port;
LOGGER.info("Test pressed for port: {}, port.value: {}", port, backlightPort.getValue());
CommunicationFactory.getInstance().activateBacklightPort(model.getSelectedNode().getNode(),
backlightPort, backlightPort.getValue());
}
@Override
public void valuesChanged(BacklightPort port) {
LOGGER
.debug(
"Values have changed for backlight port: {}. Set the new backlight port parameters, dimSlopeDown: {}, dimSlopeUp: {}, dmxMapping: {}.",
port, port.getDimSlopeUp(), port.getDimSlopeDown(), port.getDmxMapping());
CommunicationFactory.getInstance().setBacklightPortParameters(model.getSelectedNode().getNode(),
port.getId(), port.getDimSlopeDown(), port.getDimSlopeUp(), port.getDmxMapping());
}
@Override
public void configChanged(Port port) {
}
});
view.addCvDefinitionRequestListener(new CvDefinitionRequestListener() {
@Override
public void loadCvValues(List configVariables) {
LOGGER.info("Load CV definition values!");
StopWatch sw = new StopWatch();
sw.start();
try {
setWaitCursor();
CommunicationFactory.getInstance().getConfigurationVariables(model.getSelectedNode().getNode(),
configVariables);
LOGGER.debug("Update model with configuration variables: {}", configVariables);
model.updateConfigurationVariableValues(configVariables);
}
finally {
setDefaultCursor();
}
sw.stop();
LOGGER.info("Load CV values has finished! Total loading duration: {}", sw);
view.setStatusText(String.format(Resources.getString(MainController.class, "loadCvFinished"), sw),
StatusBar.DISPLAY_NORMAL);
}
@Override
public void writeCvValues(List cvList) {
LOGGER.info("Write CV definition values!");
StopWatch sw = new StopWatch();
sw.start();
try {
setWaitCursor();
List configVars =
CommunicationFactory.getInstance().writeConfigurationVariables(
model.getSelectedNode().getNode(), cvList);
// iterate over the collection of stored variables in the model and update the values.
// After that notify the tree and delete the new values that are now stored in the node
model.updateConfigurationVariableValues(configVars);
}
finally {
setDefaultCursor();
}
sw.stop();
LOGGER.info("Write CV values has finished! Total writing duration: {}", sw);
view.setStatusText(String.format(Resources.getString(MainController.class, "writeCvFinished"), sw),
StatusBar.DISPLAY_NORMAL);
}
@Override
public void activateAspect(int aspectNumber) {
LOGGER.info("Start accessory with aspect: {}", aspectNumber);
try {
setWaitCursor();
// TODO fix this to use the correct accessory
Accessory accessory = new Accessory();
accessory.setId(0);
CommunicationFactory.getInstance().startAccessory(model.getSelectedNode().getNode(), accessory,
aspectNumber);
}
finally {
setDefaultCursor();
}
}
});
view.addMacroListListener(new MacroListListener() {
@Override
public void exportMacro(final Macro macro) {
if (CollectionUtils.isEmpty(macro.getFunctions())) {
// no functions in macro, ask the user to export empty macro
// show dialog
int result =
JOptionPane.showConfirmDialog(view,
Resources.getString(MainController.class, "ask_export_empty_macro"),
Resources.getString(MainController.class, "export_macro.title"),
JOptionPane.OK_CANCEL_OPTION);
if (result != JOptionPane.OK_OPTION) {
LOGGER.info("User canceled export empty macro.");
return;
}
}
FileDialog dialog = new MacroFileDialog(
view,
// default is legacy filter
FileDialog.SAVE, macro) {
@Override
public void approve(String fileName) {
try {
boolean flatPortModel = false;
if (model.getSelectedNode() != null
&& model.getSelectedNode().getNode().isPortFlatModelAvailable()) {
flatPortModel = true;
}
LOGGER.info("Set the flat port model flag: {}", flatPortModel);
macro.setFlatPortModel(flatPortModel);
ExportFormat exportFormat = ExportFormat.jaxb;
// (getCheckUseLegacyFormat().isSelected() ? ExportFormat.serialization
// : ExportFormat.jaxb);
MacroFactory.saveMacro(fileName, macro, exportFormat);
// update the status bar
view.setStatusText(
String.format(Resources.getString(MainController.class, "exportedMacro"), fileName),
StatusBar.DISPLAY_NORMAL);
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
};
dialog.showDialog();
}
@Override
public void importMacro() {
if (model.getSelectedMacro() != null
&& model.getSelectedMacro().getMacroSaveState().equals(MacroSaveState.PENDING_CHANGES)) {
// show dialog
int result =
JOptionPane.showConfirmDialog(view,
Resources.getString(MainController.class, "macro_has_pending_changes"),
Resources.getString(MainController.class, "pending_changes"), JOptionPane.OK_CANCEL_OPTION);
if (result != JOptionPane.OK_OPTION) {
LOGGER.info("User canceled discard pending changes.");
return;
}
// reset pending changes
model.getSelectedMacro().setMacroSaveState(MacroSaveState.SAVED_ON_NODE);
}
FileDialog dialog = new MacroFileDialog(
view, FileDialog.OPEN, null) {
@Override
public void approve(String fileName) {
ExportFormat exportFormat =
(getCheckUseLegacyFormat().isSelected() ? ExportFormat.serialization : ExportFormat.jaxb);
// a new macro instance is created
Macro macro = MacroFactory.loadMacro(fileName, exportFormat, model);
LOGGER.info("Loaded macro, flatPortModel: {}", macro.isFlatPortModel());
// set the new macro id
macro.setId(model.getSelectedMacro().getId());
// set pending changes flag
macro.setMacroSaveState(MacroSaveState.PENDING_CHANGES);
// replace the imported macro
replaceMacro(macro, false);
}
};
dialog.showDialog();
}
@Override
public void labelChanged(final Macro macro, String label) {
LOGGER.info("Label of macro has changed, macro: {}, label: {}", macro, label);
mainNodeListListener.replaceMacroLabel(model.getSelectedNode().getNode().getUniqueId(), macro, label,
true);
}
@Override
public void reloadMacro(Macro macro) {
if (macro != null && macro.getMacroSaveState().equals(MacroSaveState.PENDING_CHANGES)) {
// show dialog
int result =
JOptionPane.showConfirmDialog(view,
Resources.getString(MainController.class, "macro_has_pending_changes"),
Resources.getString(MainController.class, "pending_changes"), JOptionPane.OK_CANCEL_OPTION);
if (result != JOptionPane.OK_OPTION) {
LOGGER.info("User canceled discard pending changes.");
return;
}
// reset pending changes
macro.setMacroSaveState(MacroSaveState.SAVED_ON_NODE);
}
try {
LcMacroState lcMacroState =
CommunicationFactory.getInstance().reloadMacro(model.getSelectedNode(), macro);
LOGGER.info("Reload macro returned: {}", lcMacroState);
model.getSelectedMacro().setMacroSaveState(MacroSaveState.PERMANENTLY_STORED_ON_NODE);
macro.setContainsError(false);
}
catch (InvalidConfigurationException ex) {
LOGGER.warn("Restore macro content failed.", ex);
model.setNodeHasError(model.getSelectedNode(), true);
}
}
@Override
public void saveMacro(Macro macro) {
LOGGER.info("Save macro: {}", macro);
try {
// the macro save state is reset by the saveMacro call
LcMacroState lcMacroState =
CommunicationFactory.getInstance().saveMacro(model.getSelectedNode(), macro);
LOGGER.info("Save macro returned: {}", lcMacroState);
macro.setContainsError(false);
}
catch (InvalidConfigurationException ex) {
LOGGER.warn("Save macro content failed.", ex);
model.setNodeHasError(model.getSelectedNode(), true);
}
}
@Override
public void startMacro(Macro macro, boolean transferBeforeStart) {
LOGGER.info("Start macro: {}, transferBeforeStart: {}", macro, transferBeforeStart);
LcMacroState lcMacroState =
CommunicationFactory.getInstance().startMacro(model.getSelectedNode(), macro, transferBeforeStart);
LOGGER.info("Start macro returned: {}", lcMacroState);
}
@Override
public void stopMacro(Macro macro) {
LOGGER.info("Stop macro: {}", macro);
LcMacroState lcMacroState =
CommunicationFactory.getInstance().stopMacro(model.getSelectedNode().getNode(), macro);
LOGGER.info("Stop macro returned: {}", lcMacroState);
}
@Override
public void transferMacro(Macro macro) {
LOGGER.info("Transfer macro: {}", macro);
try {
CommunicationFactory.getInstance().transferMacro(model.getSelectedNode(), macro);
LOGGER.info("Transfer macro passed.");
}
catch (InvalidConfigurationException ex) {
LOGGER.warn("Transfer macro content failed.", ex);
model.setNodeHasError(model.getSelectedNode(), true);
}
}
@Override
public void initializeMacro(Macro macro) {
// and force the panels to reload
try {
if (model.getSelectedMacro() != null && model.getSelectedMacro().equals(macro)
&& model.getSelectedMacro().getMacroSaveState().equals(MacroSaveState.PENDING_CHANGES)) {
// show dialog
int result =
JOptionPane.showConfirmDialog(view,
Resources.getString(MainController.class, "macro_has_pending_changes"),
Resources.getString(MainController.class, "pending_changes"),
JOptionPane.OK_CANCEL_OPTION);
if (result != JOptionPane.OK_OPTION) {
LOGGER.info("User canceled discard pending changes.");
return;
}
// reset pending changes
model.getSelectedMacro().setMacroSaveState(MacroSaveState.SAVED_ON_NODE);
}
// initialize the macro
macro.initialize();
macro.setMacroSaveState(MacroSaveState.PENDING_CHANGES);
model.setSelectedMacro(macro);
}
catch (Exception ex) {
LOGGER.warn("Set the new selcted macro failed.", ex);
}
}
});
view.addMacroListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(final ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
final JList macroList = (JList) e.getSource();
try {
setWaitCursor();
if (!(macroList.getSelectedValue() instanceof Macro)) {
// do not react on change of label
return;
}
Macro macro = macroList.getSelectedValue();
if (macro != null) {
// This loads the macro content effectively ...
LOGGER.info("Load the macro content from the node.");
try {
macro =
CommunicationFactory.getInstance().getMacroContent(model.getSelectedNode(), macro);
model.setSelectedMacro(macro);
}
catch (InvalidConfigurationException ex) {
LOGGER.warn("Restore macro content failed.", ex);
// set an empty macro
macro.setContainsError(true);
model.setSelectedMacro(macro);
model.setNodeHasError(model.getSelectedNode(), true);
}
catch (Exception ex) {
LOGGER.warn("Get the content of the selected macro failed.", ex);
}
}
}
finally {
setDefaultCursor();
}
}
}
});
view.addMacroTableListener(new MacroTableListener() {
@Override
public void copy(Function extends BidibStatus>[] functions) {
setFunctions(functions);
}
@Override
public void cut(int[] rows, Function extends BidibStatus>[] functions) {
setFunctions(functions);
for (int index = rows.length - 1; index >= 0; index--) {
model.getSelectedMacro().removeFunction(rows[index]);
}
}
@Override
public void delete(int[] rows) {
for (int index = rows.length - 1; index >= 0; index--) {
model.getSelectedMacro().removeFunction(rows[index]);
}
}
@Override
public void insertEmptyAfter(int row) {
model.getSelectedMacro().addFunctionsAfter(row, null);
}
@Override
public void insertEmptyBefore(int row) {
model.getSelectedMacro().addFunctionsBefore(row >= 0 ? row : 0, null);
}
@Override
public void pasteAfter(int row) {
model.getSelectedMacro().addFunctionsAfter(row, getFunctions());
}
@Override
public void pasteBefore(int row) {
model.getSelectedMacro().addFunctionsBefore(row >= 0 ? row : 0, getFunctions());
}
@Override
public void pasteInvertedAfter(int row) {
model.getSelectedMacro().addFunctionsInvertedAfter(row, getFunctions());
}
});
MainMenuListener mainMenuListener = new DefaultMainMenuListener(view, this);
view.addMainMenuListener(mainMenuListener);
view.addToolBarListener(mainMenuListener);
view.addMotorPortListener(new OutputListener() {
@Override
public void labelChanged(Port port, String value) {
port.setLabel(value);
if (value != null && value.length() > 0) {
motorPortLabels.setLabel(model.getSelectedNode().getNode().getUniqueId(), port.getId(), value);
}
else {
motorPortLabels.removeLabel(model.getSelectedNode().getNode().getUniqueId(), port.getId());
}
try {
motorPortLabels.save();
}
catch (InvalidConfigurationException ex) {
LOGGER.warn("Save motor port labels failed.", ex);
String labelPath = ex.getReason();
JOptionPane.showMessageDialog(JOptionPane.getFrameForComponent(null),
Resources.getString(Labels.class, "labelfileerror.message", new Object[] { labelPath }),
Resources.getString(Labels.class, "labelfileerror.title"), JOptionPane.ERROR_MESSAGE);
}
}
@Override
public void statusChanged(Port port, MotorPortStatus status) {
}
@Override
public void testButtonPressed(Port port) {
CommunicationFactory.getInstance().activateMotorPort(model.getSelectedNode().getNode(), port.getId(),
(MotorPortStatus) port.getStatus());
}
@Override
public void configChanged(Port port) {
}
});
view.addServoPortListener(new ServoPortListener() {
@Override
public void labelChanged(Port port, String label) {
port.setLabel(label);
if (label != null && label.length() > 0) {
servoPortLabels.setLabel(model.getSelectedNode().getNode().getUniqueId(), port.getId(), label);
}
else {
servoPortLabels.removeLabel(model.getSelectedNode().getNode().getUniqueId(), port.getId());
}
try {
servoPortLabels.save();
}
catch (InvalidConfigurationException ex) {
LOGGER.warn("Save servo port labels failed.", ex);
String labelPath = ex.getReason();
JOptionPane.showMessageDialog(JOptionPane.getFrameForComponent(null),
Resources.getString(Labels.class, "labelfileerror.message", new Object[] { labelPath }),
Resources.getString(Labels.class, "labelfileerror.title"), JOptionPane.ERROR_MESSAGE);
}
}
@Override
public void statusChanged(Port port, ServoPortStatus status) {
LOGGER.debug("Status of servo port has changed, port: {}, status: {}", port, status);
}
@Override
public void testButtonPressed(Port port) {
LOGGER.info("Test pressed for port: {}", port);
CommunicationFactory.getInstance().activateServoPort(model.getSelectedNode().getNode(), port.getId(),
((ServoPort) port).getValue());
}
@Override
public void valuesChanged(ServoPort port) {
LOGGER.info("Values have changed for servo port: {}. Set the new servo parameters.", port);
CommunicationFactory.getInstance().setServoPortParameters(model.getSelectedNode().getNode(),
port.getId(), port.getTrimDown(), port.getTrimUp(), port.getSpeed());
}
@Override
public void configChanged(Port port) {
}
});
view.addSoundPortListener(new OutputListener() {
@Override
public void labelChanged(Port port, String label) {
port.setLabel(label);
if (label != null && label.length() > 0) {
soundPortLabels.setLabel(model.getSelectedNode().getNode().getUniqueId(), port.getId(), label);
}
else {
soundPortLabels.removeLabel(model.getSelectedNode().getNode().getUniqueId(), port.getId());
}
try {
soundPortLabels.save();
}
catch (InvalidConfigurationException ex) {
LOGGER.warn("Save sound port labels failed.", ex);
String labelPath = ex.getReason();
JOptionPane.showMessageDialog(JOptionPane.getFrameForComponent(null),
Resources.getString(Labels.class, "labelfileerror.message", new Object[] { labelPath }),
Resources.getString(Labels.class, "labelfileerror.title"), JOptionPane.ERROR_MESSAGE);
}
}
@Override
public void statusChanged(Port port, SoundPortStatus status) {
}
@Override
public void testButtonPressed(Port port) {
CommunicationFactory.getInstance().activateSoundPort(model.getSelectedNode().getNode(), port.getId(),
(SoundPortStatus) port.getStatus());
}
@Override
public void configChanged(Port port) {
}
});
view.addStatusListener(new StatusListener() {
@Override
public void switchedOff() {
model.getStatusModel().setModelClockStartEnabled(false);
model.getStatusModel().setRunning(false);
}
@Override
public void switchedOn() {
model.getStatusModel().setModelClockStartEnabled(true);
model.getStatusModel().setRunning(true);
}
});
view.addSwitchPortListener(new SwitchPortListener() {
@Override
public void labelChanged(Port port, String label) {
port.setLabel(label);
if (label != null && label.length() > 0) {
switchPortLabels.setLabel(model.getSelectedNode().getNode().getUniqueId(), port.getId(), label);
}
else {
switchPortLabels.removeLabel(model.getSelectedNode().getNode().getUniqueId(), port.getId());
}
try {
switchPortLabels.save();
}
catch (InvalidConfigurationException ex) {
LOGGER.warn("Save switch port labels failed.", ex);
String labelPath = ex.getReason();
JOptionPane.showMessageDialog(JOptionPane.getFrameForComponent(null),
Resources.getString(Labels.class, "labelfileerror.message", new Object[] { labelPath }),
Resources.getString(Labels.class, "labelfileerror.title"), JOptionPane.ERROR_MESSAGE);
}
}
@Override
public void statusChanged(Port port, SwitchPortStatus status) {
LOGGER.debug("Status of switchlight port has changed, port: {}, status: {}", port, status);
}
@Override
public void valuesChanged(SwitchPort port, PortConfigKeys... portConfigKeys) {
LOGGER.info("The port value are changed for port: {}", port);
Map> values = new LinkedHashMap<>();
for (PortConfigKeys key : portConfigKeys) {
switch (key) {
case BIDIB_PCFG_IO_CTRL:
IoBehaviourEnum ioBehaviour = port.getIoBehaviour();
values.put(BidibLibrary.BIDIB_PCFG_IO_CTRL, new BytePortConfigValue(ioBehaviour.getType()));
break;
case BIDIB_PCFG_TICKS:
int switchOffTime = port.getSwitchOffTime();
values.put(BidibLibrary.BIDIB_PCFG_TICKS,
new BytePortConfigValue(ByteUtils.getLowByte(switchOffTime)));
break;
default:
LOGGER.warn("Unsupported port config key detected: {}", key);
break;
}
}
// don't set the port type param to not send BIDIB_PCFG_RECONFIG
CommunicationFactory.getInstance().setPortParameters(model.getSelectedNode().getNode(), port.getId(),
null, values);
}
@Override
public void testButtonPressed(Port port) {
CommunicationFactory.getInstance().activateSwitchPort(model.getSelectedNode().getNode(), port.getId(),
(SwitchPortStatus) port.getStatus());
}
@Override
public void configChanged(Port port) {
}
@Override
public void changePortType(LcOutputType portType, SwitchPort port) {
LOGGER.info("The port type will change to: {}, port: {}", portType, port);
Map> values = new LinkedHashMap<>();
CommunicationFactory.getInstance().setPortParameters(model.getSelectedNode().getNode(), port.getId(),
portType, values);
}
});
view.setWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
stop();
}
});
if (Preferences.getInstance().isShowBoosterTable()) {
LOGGER.info("Show the booster table is selected in preferences.");
// open the booster table ...
try {
mainMenuListener.boosterTable();
}
catch (Exception ex) {
LOGGER.warn("Open booster table failed.", ex);
}
}
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();
if (model.getSelectedNode() != null && model.getSelectedNode().isBooster()) {
if (model.getBoosterCurrent() >= 0
&& model.getLastCurrentUpdate() < (now - CURRENT_UPDATE_TIMEOUT)) {
// the current value is outdated -> clear the value
LOGGER.info("the current value is outdated -> clear the value, node: {}",
model.getSelectedNode());
model.setBoosterCurrent(-1);
}
}
}
});
boosterCurrentTimer.setCoalesce(true);
boosterCurrentTimer.start();
}
catch (Exception ex) {
LOGGER.warn("Start the booster current timer failed.", ex);
}
}
private void getNodeConfiguration(Node node) {
LOGGER.info("Get the node configuration for node: {}, protocol version: {}", node, node
.getNode().getProtocolVersion());
Communication communication = CommunicationFactory.getInstance();
List feedbackPorts = new LinkedList();
List analogPorts = new LinkedList();
List inputPorts = new LinkedList();
List lightPorts = new LinkedList();
List backlightPorts = new LinkedList();
List motorPorts = new LinkedList();
List servoPorts = new LinkedList();
List soundPorts = new LinkedList();
final List switchPorts = new LinkedList();
if (node != null) {
// check if we have flat or type-oriented port model
if (node.getNode().isPortFlatModelAvailable()) {
LOGGER.info("Get the port information from flat port model.");
if (NodeUtils.hasFeedbackFunctions(node.getUniqueId())) {
LOGGER.debug("Get the feedback ports from the node.");
for (FeedbackPort feedbackPort : communication.getFeedbackPorts(node.getNode())) {
feedbackPort.setLabel(feedbackPortLabels.getLabel(node.getNode().getUniqueId(),
feedbackPort.getId()));
feedbackPorts.add(feedbackPort);
}
model.setFeedbackPorts(feedbackPorts);
}
else {
LOGGER.debug("Node has no feedback ports configured.");
model.setFeedbackPorts(feedbackPorts);
}
int totalPortCount = node.getNode().getPortFlatModel();
LOGGER.info("Create total number of ports: {}", totalPortCount);
List ports = new LinkedList<>();
for (int portId = 0; portId < totalPortCount; portId++) {
GenericPort port = new GenericPort(portId);
ports.add(port);
}
node.setGenericPorts(ports);
// clear the ports in the model to force usage of generic ports
model.setAnalogPorts(analogPorts);
model.setBacklightPorts(backlightPorts);
model.setLightPorts(lightPorts);
model.setInputPorts(inputPorts);
model.setMotorPorts(motorPorts);
model.setServoPorts(servoPorts);
model.setSoundPorts(soundPorts);
model.setSwitchPorts(switchPorts);
// trigger query of port config
LOGGER.info("Trigger query of port config for node: {}", node);
// check for the protocol version
if (node.getNode().getProtocolVersion().isHigherThan(ProtocolVersion.VERSION_0_5)) {
LOGGER.info("The current node uses queryAllPortConfig to get the port config from the node.");
// query the port config
communication.queryAllPortConfig(node.getNode(), null, null, null);
try {
boolean configPendingFound = false;
for (GenericPort port : node.getGenericPorts()) {
if (PortConfigStatus.CONFIG_PENDING.equals(port.getConfigStatus())) {
// found a port with config pending status
LOGGER.info("Found a port with config pending status: {}", port);
configPendingFound = true;
break;
}
}
if (configPendingFound) {
LOGGER
.info("Found config pending status found in generic ports, clear the port cache on the node and update the port lists after queryAllPortConfig.");
node.setNodeHasError(true);
node.setErrorState(SysErrorEnum.BIDIB_ERR_TXT,
ByteUtils.bstr(Resources.getString(MainController.class, "missing-port-config")));
node.clearPortCache();
model.updatePortLists();
}
else {
LOGGER.info("No pending config found, don't update of ports list.");
}
}
catch (Exception ex) {
LOGGER.warn("Check if all generic ports have been configured failed.", ex);
}
}
else {
communication.queryPortConfig(node.getNode(), node.getGenericPorts());
}
}
else {
// process port-type oriented model
LOGGER.info("Get the port information from port-type oriented model.");
if (NodeUtils.hasFeedbackFunctions(node.getUniqueId())) {
LOGGER.debug("Get the feedback ports from the node.");
for (FeedbackPort feedbackPort : communication.getFeedbackPorts(node.getNode())) {
feedbackPort.setLabel(feedbackPortLabels.getLabel(node.getNode().getUniqueId(),
feedbackPort.getId()));
feedbackPorts.add(feedbackPort);
}
model.setFeedbackPorts(feedbackPorts);
}
else {
LOGGER.debug("Node has no feedback ports configured.");
model.setFeedbackPorts(feedbackPorts);
}
// TODO if we use asynchronous initialization of ports we must create the ports
// first, add to model and trigger initialize afterwards
if (NodeUtils.hasSwitchFunctions(node.getUniqueId())) {
LOGGER.debug("Get the switch functions from the node.");
// check for the protocol version
boolean queryAllPortConfigSupported = false;
if (node.getNode().getProtocolVersion().isHigherThan(ProtocolVersion.VERSION_0_5)) {
// the OneDMX 2.0.0 is a special case
if (ProductUtils.isOneDMX(node.getUniqueId())
&& node.getNode().getSoftwareVersion().equals(new SoftwareVersion(2, 0, 0))) {
LOGGER.info("The current OneDMX-2.00.00 has no support for queryAllPortConfig.");
queryAllPortConfigSupported = false;
}
// the STu-0.01.04 is a special case
else if (ProductUtils.isSTu(node.getUniqueId())
&& node.getNode().getSoftwareVersion().equals(new SoftwareVersion(0, 1, 4))) {
LOGGER.info("The current STu-0.01.04 has no support for queryAllPortConfig.");
queryAllPortConfigSupported = false;
}
else {
LOGGER.info("The current node has support for queryAllPortConfig.");
queryAllPortConfigSupported = true;
}
}
LOGGER.debug("Get the analog ports from the node.");
for (AnalogPort analogPort : communication.getAnalogPorts(node.getNode())) {
analogPort
.setLabel(analogPortLabels.getLabel(node.getNode().getUniqueId(), analogPort.getId()));
analogPorts.add(analogPort);
}
model.setAnalogPorts(analogPorts);
if (CollectionUtils.isNotEmpty(analogPorts)) {
if (queryAllPortConfigSupported) {
communication.queryAllPortConfig(node.getNode(), LcOutputType.ANALOGPORT, 0,
analogPorts.size());
}
}
LOGGER.debug("Get the input ports from the node.");
for (InputPort inputPort : communication.getInputPorts(node.getNode())) {
inputPort.setLabel(inputPortLabels.getLabel(node.getNode().getUniqueId(), inputPort.getId()));
inputPorts.add(inputPort);
}
model.setInputPorts(inputPorts);
// TODO check if this would work
// LOGGER.info("Number of input ports from the node: {}", inputPorts.size());
// if (CollectionUtils.isNotEmpty(inputPorts)) {
// if (!queryAllPortConfigSupported) {
// // trigger query of input port config
// communication.queryInputPortConfig(node.getNode());
// }
// else {
// communication.queryAllPortConfig(node.getNode(), LcOutputType.INPUTPORT, 0,
// switchPorts.size());
// }
// }
LOGGER.debug("Get the switch ports from the node.");
for (SwitchPort switchPort : communication.getSwitchPorts(node.getNode())) {
// set the label
switchPort
.setLabel(switchPortLabels.getLabel(node.getNode().getUniqueId(), switchPort.getId()));
switchPorts.add(switchPort);
}
model.setSwitchPorts(switchPorts);
LOGGER.info("Number of switch ports from the node: {}", switchPorts.size());
if (CollectionUtils.isNotEmpty(switchPorts)) {
if (!queryAllPortConfigSupported) {
// trigger query of switch port config
communication.querySwitchPortConfig(node.getNode());
}
else {
communication.queryAllPortConfig(node.getNode(), LcOutputType.SWITCHPORT, 0,
switchPorts.size());
}
}
LOGGER.debug("Get the light ports from the node.");
for (LightPort lightPort : communication.getLightPorts(node.getNode())) {
lightPort.setLabel(lightPortLabels.getLabel(node.getNode().getUniqueId(), lightPort.getId()));
lightPorts.add(lightPort);
}
model.setLightPorts(lightPorts);
if (CollectionUtils.isNotEmpty(lightPorts)) {
if (!queryAllPortConfigSupported) {
// trigger query of light port config
communication.queryLightPortConfig(node.getNode());
}
else {
communication.queryAllPortConfig(node.getNode(), LcOutputType.LIGHTPORT, 0,
lightPorts.size());
}
}
LOGGER.debug("Get the backlight ports from the node.");
for (BacklightPort backlightPort : communication.getBacklightPorts(node.getNode())) {
backlightPort.setLabel(backlightPortLabels.getLabel(node.getNode().getUniqueId(),
backlightPort.getId()));
backlightPorts.add(backlightPort);
}
model.setBacklightPorts(backlightPorts);
if (CollectionUtils.isNotEmpty(backlightPorts)) {
if (!queryAllPortConfigSupported) {
// trigger query of backlight port config
communication.queryBacklightPortConfig(node.getNode());
}
else {
communication.queryAllPortConfig(node.getNode(), LcOutputType.BACKLIGHTPORT, 0,
backlightPorts.size());
}
}
Collection nodeMotorPorts = communication.getMotorPorts(node.getNode());
if (CollectionUtils.isNotEmpty(nodeMotorPorts)) {
LOGGER.debug("Get the motor ports from the node.");
for (MotorPort motorPort : nodeMotorPorts) {
motorPort
.setLabel(motorPortLabels.getLabel(node.getNode().getUniqueId(), motorPort.getId()));
motorPorts.add(motorPort);
}
}
else {
LOGGER.debug("The current node has no motor ports.");
}
model.setMotorPorts(motorPorts);
if (CollectionUtils.isNotEmpty(motorPorts)) {
if (queryAllPortConfigSupported) {
// query the configuration of the motor ports
communication.queryAllPortConfig(node.getNode(), LcOutputType.MOTORPORT, 0,
motorPorts.size());
}
}
LOGGER.debug("Get the servo ports from the node.");
for (ServoPort servoPort : communication.getServoPorts(node.getNode())) {
servoPort.setLabel(servoPortLabels.getLabel(node.getNode().getUniqueId(), servoPort.getId()));
servoPorts.add(servoPort);
}
model.setServoPorts(servoPorts);
if (CollectionUtils.isNotEmpty(servoPorts)) {
if (!queryAllPortConfigSupported) {
// trigger query of servo port config
communication.queryServoPortConfig(node.getNode());
}
else {
communication.queryAllPortConfig(node.getNode(), LcOutputType.SERVOPORT, 0,
servoPorts.size());
}
}
LOGGER.debug("Get the sound ports from the node.");
for (SoundPort soundPort : communication.getSoundPorts(node.getNode())) {
soundPort.setLabel(soundPortLabels.getLabel(node.getNode().getUniqueId(), soundPort.getId()));
soundPorts.add(soundPort);
}
model.setSoundPorts(soundPorts);
if (CollectionUtils.isNotEmpty(soundPorts)) {
if (queryAllPortConfigSupported) {
// query the configuration of the sound ports
communication.queryAllPortConfig(node.getNode(), LcOutputType.SOUNDPORT, 0,
soundPorts.size());
}
}
if (!queryAllPortConfigSupported) {
LOGGER.info("Get the port mapping.");
PortRemappingSupport portRemappingSupport = new PortRemappingSupport();
portRemappingSupport.processPortRemapping(node, switchPorts, inputPorts, servoPorts);
}
}
else {
LOGGER.info("Node has no switch functions configured.");
model.setAnalogPorts(analogPorts);
model.setInputPorts(inputPorts);
model.setSwitchPorts(switchPorts);
model.setLightPorts(lightPorts);
model.setBacklightPorts(backlightPorts);
model.setMotorPorts(motorPorts);
model.setServoPorts(servoPorts);
model.setSoundPorts(soundPorts);
}
// update all port lists with the values that are now available
model.updatePortLists();
}
List flags = new LinkedList();
List macros = new LinkedList();
List accessories = new LinkedList();
if (NodeUtils.hasAccessoryFunctions(node.getUniqueId())) {
LOGGER.debug("Get the flags.");
for (Flag flag : communication.getFlags()) {
flags.add(flag);
}
model.setFlags(flags);
// check if the node has macros
Feature macroLevel =
communication.readFeature(node.getNode(), BidibLibrary.FEATURE_CTRL_MAC_LEVEL, true);
int macroLevelValue = 0;
if (macroLevel != null) {
macroLevelValue = macroLevel.getValue();
}
if (macroLevelValue > 0) {
LOGGER.info("Get the macros from the node.");
try {
for (Macro macro : communication.getMacros(node.getNode())) {
// set the user-defined label
macro.setLabel(macroLabels.getLabel(node.getNode().getUniqueId(), macro.getId()));
// reset the changed flag on the macros
macro.setMacroSaveState(MacroSaveState.PERMANENTLY_STORED_ON_NODE);
// add the current macro to the list
macros.add(macro);
}
}
catch (RuntimeException e) {
LOGGER.warn("Get the macros from the node failed.", e);
model.setNodeHasError(node, true);
}
}
else {
LOGGER.info("The current node has no macros.");
}
model.setMacros(macros);
LOGGER.info("Get the accessories from the node.");
try {
Collection nodeAccessories = communication.getAccessories(node.getNode());
if (nodeAccessories != null) {
for (Accessory accessory : nodeAccessories) {
// accessory
// .setLabel(accessoryLabels.getLabel(node.getNode().getUniqueId(), accessory.getId()));
LabelType label =
AccessoryLabelUtils.getAccessoryLabel(accessoryLabels, node.getNode().getUniqueId(),
accessory.getId());
accessory.setLabel(label.getLabelString());
// reset the changed flag on the accessory
accessory.setPendingChanges(false);
accessories.add(accessory);
}
}
else {
LOGGER.debug("Node has no accessories configured.");
}
}
catch (NoAnswerException e) {
LOGGER.warn("Get accessories from node failed.", e);
model.setNodeHasError(node, true);
}
model.setAccessories(accessories);
if (CollectionUtils.isNotEmpty(accessories)) {
// Feature accessoryMacroMapped =
// communication.readFeature(node.getNode(), BidibLibrary.FEATURE_ACCESSORY_MACROMAPPED, false);
// if (accessoryMacroMapped != null && accessoryMacroMapped.getValue() < 1) {
LOGGER.info("Query the accessory state from the node.");
try {
communication.queryAccessoryState(node.getNode(), accessories.toArray(new Accessory[0]));
}
catch (Exception ex) {
LOGGER.warn("Query accessory state from node failed.", ex);
}
// }
}
else {
LOGGER.info("No accessories on node available.");
}
}
else {
model.setFlags(flags);
model.setMacros(macros);
model.setAccessories(accessories);
}
}
}
private void queryNodeStatus(Node node) {
Communication communication = CommunicationFactory.getInstance();
// initialize the current values
if (NodeUtils.hasBoosterFunctions(node.getUniqueId())) {
LOGGER.info("The node is a booster, set the booster maximum value.");
// set the max booster current
int boosterMaxCurrent = communication.getBoosterMaximumCurrent(node.getNode());
LOGGER.info("Set the booster maximum current value: {}", boosterMaxCurrent);
model.setBoosterMaximumCurrent(boosterMaxCurrent);
// trigger fetch the current booster values
communication.boosterQuery(node.getNode());
}
else {
LOGGER.info("Current node has no booster functions: {}", node);
}
if (model.getFeedbackPorts().size() > 0) {
LOGGER.info("Query the feedback ports.");
communication.getFeedbackPortStatus(node.getNode(), 0, model.getFeedbackPorts().size());
}
else {
LOGGER.info("No feedback ports to query available.");
}
// check if the query port status feature is enabled
if (NodeUtils.hasSwitchFunctions(node.getUniqueId())) {
LOGGER.info("The node has switching functions.");
if (CollectionUtils.isNotEmpty(model.getInputPorts())) {
LOGGER.info("Query the status of the input ports.");
List portIds = new LinkedList();
for (InputPort inputPort : model.getInputPorts()) {
if (inputPort.isEnabled()) {
portIds.add(inputPort.getId());
}
else {
LOGGER.info("Skip query port status of disabled input port with portId: {}", inputPort.getId());
}
}
communication.getInputPortStatus(node.getNode(), portIds);
}
if (communication.isQueryPortStatusEnabled(node.getNode())) {
try {
List servoPorts = new LinkedList(model.getServoPorts());
LOGGER.info("Query the port status for the servo ports: {}", servoPorts);
List portIds = new LinkedList();
for (ServoPort servoPort : servoPorts) {
if (servoPort.isEnabled()) {
// query the port status
portIds.add(servoPort.getId());
}
else {
LOGGER.info("Skip query port status of disabled servo port with portId: {}",
servoPort.getId());
}
}
communication.queryPortStatus(LcOutputType.SERVOPORT, node.getNode(), portIds);
}
catch (Exception ex) {
LOGGER.warn("Query the port status for the servo ports failed.", ex);
}
try {
List switchPorts = new LinkedList(model.getSwitchPorts());
LOGGER.info("Query the port status for the switch ports: {}", switchPorts);
List portIds = new LinkedList();
for (SwitchPort switchPort : switchPorts) {
if (switchPort.isEnabled()) {
// query the port status
portIds.add(switchPort.getId());
}
else {
LOGGER.info("Skip query port status of disabled switch port with portId: {}",
switchPort.getId());
}
}
communication.queryPortStatus(LcOutputType.SWITCHPORT, node.getNode(), portIds);
}
catch (Exception ex) {
LOGGER.warn("Query the port status for the switch ports failed.", ex);
}
try {
LOGGER.info("Query the port status for the backlight ports: {}", model.getBacklightPorts());
for (BacklightPort backlightPort : model.getBacklightPorts()) {
communication
.queryPortStatus(LcOutputType.BACKLIGHTPORT, node.getNode(), backlightPort.getId());
}
}
catch (Exception ex) {
LOGGER.warn("Query the port status for the backlight ports failed.", ex);
}
try {
LOGGER.info("Query the port status for the light ports: {}", model.getLightPorts());
for (LightPort lightPort : model.getLightPorts()) {
communication.queryPortStatus(LcOutputType.LIGHTPORT, node.getNode(), lightPort.getId());
}
}
catch (Exception ex) {
LOGGER.warn("Query the port status for the light ports failed.", ex);
}
}
else {
LOGGER.info("Query port status is not enabled for node: {}", node);
}
}
else {
LOGGER.info("Node has no switch port functions.");
}
}
@Override
public void stop() {
LOGGER.info("Stop the main controller.");
selectedNodeChangeWorker.shutdownNow();
view.saveWindowPosition();
view.setVisible(false);
System.exit(0);
}
@Override
public void addNodeListListener(NodeListListener nodeListListener) {
LOGGER.info("Add new nodeListListener: {}", nodeListListener);
model.addNodeListListener(nodeListListener);
}
@Override
public void removeNodeListListener(NodeListListener nodeListListener) {
LOGGER.info("Remove nodeListListener: {}", nodeListListener);
model.removeNodeListListener(nodeListListener);
}
private NodeScripting nodeScripting;
@Override
public synchronized NodeScripting getNodeScripting() {
if (nodeScripting == null) {
LOGGER.info("Create new NodeScripting.");
nodeScripting = new DefaultNodeScripting(model, this);
}
return nodeScripting;
}
@Override
public void openConnection() {
// use a thread to open the port
LOGGER.info("Start a thread to open the port.");
Thread openThread = new Thread(new Runnable() {
@Override
public void run() {
LOGGER.info("The open thread is starting.");
final Context context = new DefaultContext();
// connect to system
view.setStatusText(Resources.getString(DefaultMainMenuListener.class, "open-port"),
StatusBar.DISPLAY_NORMAL);
try {
// get the value from the preferences
context.register("ignoreWrongReceiveMessageNumber",
Boolean.valueOf(Preferences.getInstance().isIgnoreWrongReceiveMessageNumber()));
context.register("ignoreFlowControl",
Boolean.valueOf(Preferences.getInstance().isIgnoreFlowControl()));
CommunicationFactory.getInstance().open(null, context);
LOGGER.info("Opened communication.");
}
catch (InvalidConfigurationException ex) {
LOGGER.warn("Open port and create nodes failed.", ex);
if (ex.getCause() instanceof ReasonAware) {
ReasonAware reasonAware = (ReasonAware) ex.getCause();
String causeTemplate = Resources.getString(ReasonAware.class, "causeTemplate");
String reason = Resources.getString(reasonAware.getClass(), reasonAware.getReason());
view.setStatusText(String.format(causeTemplate, ex.getCause().getMessage(), reason),
StatusBar.DISPLAY_NORMAL);
showErrorDialog(String.format(causeTemplate, ex.getCause().getMessage(), reason),
Resources.getString(MainController.class, "error.title"));
}
else {
String causeTemplate =
Resources.getString(InvalidConfigurationException.class, "causeTemplate");
String message = null;
if (StringUtils.isNotBlank(ex.getReason())) {
message = Resources.getString(ex.getClass(), ex.getReason());
}
else {
message = (ex.getCause() != null ? ex.getCause().getMessage() : ex.getMessage());
}
view.setStatusText(String.format(causeTemplate, message), StatusBar.DISPLAY_NORMAL);
showErrorDialog(String.format(causeTemplate, message),
Resources.getString(MainController.class, "error.title"));
}
}
catch (NoAnswerException ex) {
LOGGER.warn("Open port and establish communication failed.", ex);
view.setStatusText(Resources.getString(ex.getClass(), "bidib-communication-failure"),
StatusBar.DISPLAY_NORMAL);
context.register("testIsDebugIfActive", Boolean.TRUE);
context.register("noAnswerEx", ex);
}
catch (Exception ex) {
LOGGER.warn("Open port and create nodes failed.", ex);
if (ex.getCause() != null) {
view.setStatusText(Resources.getString(null, ex.getCause().getClass().getName()) + " "
+ ex.getCause().getMessage(), StatusBar.DISPLAY_NORMAL);
showErrorDialog(Resources.getString(null, ex.getCause().getClass().getName()) + " "
+ ex.getCause().getMessage(), Resources.getString(MainController.class, "error.title"));
}
else {
view.setStatusText(Resources.getString(null, ex.getClass().getName()) + " " + ex.getMessage(),
StatusBar.DISPLAY_NORMAL);
showErrorDialog(Resources.getString(null, ex.getClass().getName()) + " " + ex.getMessage(),
Resources.getString(MainController.class, "error.title"));
}
}
LOGGER.info("The open thread has finished.");
if (context.get("testIsDebugIfActive", Boolean.class, Boolean.FALSE) == Boolean.TRUE) {
LOGGER.info("Test if the debug interface is active.");
// use the debug controller to check the interface
SwingUtilities.invokeLater(new Runnable() {
public void run() {
PreferencesPortType portType = Preferences.getInstance().getSelectedPortType();
String commPort = portType.getConnectionName();
String result = sendInfoRequest(commPort);
NoAnswerException ex = context.get("noAnswerEx", NoAnswerException.class, null);
if (StringUtils.isNotBlank(result)) {
showErrorDialog(Resources.getString(ex.getClass(),
"bidib-communication-failure-debug-interface-active", new Object[] { result }),
Resources.getString(MainController.class, "error.title"));
}
else {
// display the original error message
showErrorDialog(Resources.getString(ex.getClass(), "bidib-communication-failure"),
Resources.getString(MainController.class, "error.title"));
}
}
});
}
}
}, "openThread");
LOGGER.info("Start the open thread.");
openThread.start();
}
private String sendInfoRequest(String portName) {
LOGGER.warn("Send the info request to the root node, portName: {}", portName);
final StringBuilder receivedMessages = new StringBuilder();
try {
final Object resultLock = new Object();
int baudRate = 19200;
DebugMessageListener messageListener = null;
DebugMessageReceiver messageReceiver = null;
DebugReader debugReader = null;
if (debugReader == null) {
LOGGER.info("Create new instance of debug reader.");
try {
messageListener = new DebugMessageListener() {
@Override
public void debugMessage(final String message) {
LOGGER.info("debug message received: {}", message);
receivedMessages.append(message);
if (message.indexOf('\n') > -1) {
LOGGER.info("Received CR, notify the waiting thread.");
synchronized (resultLock) {
resultLock.notify();
}
}
}
};
messageReceiver = new DebugMessageReceiver();
messageReceiver.addMessageListener(messageListener);
debugReader = new DebugReader(messageReceiver);
}
catch (Exception ex) {
LOGGER.warn("Open debug port failed.", ex);
}
}
try {
debugReader.open(portName, baudRate, new ConnectionListener() {
@Override
public void opened(String port) {
LOGGER.info("Port opened: {}", port);
}
@Override
public void closed(String port) {
LOGGER.info("Port closed: {}", port);
}
}, null);
}
catch (PortNotFoundException | PortNotOpenedException ex) {
LOGGER.warn("Open debug port failed.", ex);
}
catch (Exception ex) {
LOGGER.warn("Open debug port failed.", ex);
}
String infoRequest = "?";
// send twice because the first message might not be evaluated correct because of data in buffer
// on the receiving side because the debug interface expects the line feed.
debugReader.send(infoRequest);
debugReader.send(infoRequest);
synchronized (resultLock) {
resultLock.wait(1000);
}
debugReader.close();
messageReceiver.removeMessageListener(messageListener);
messageListener = null;
messageReceiver = null;
debugReader = null;
}
catch (Exception ex) {
LOGGER.error("Send info request failed.", ex);
}
finally {
}
return receivedMessages.toString();
}
private void showErrorDialog(final String message, final String title) {
if (SwingUtilities.isEventDispatchThread()) {
JOptionPane.showMessageDialog(view, message, title, JOptionPane.ERROR_MESSAGE);
}
else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JOptionPane.showMessageDialog(view, message, title, JOptionPane.ERROR_MESSAGE);
}
});
}
}
@Override
public void allBoosterOff() {
for (Node node : model.getNodes()) {
if (NodeUtils.hasBoosterFunctions(node.getUniqueId())) {
LOGGER.info("Switch booster off: {}", node);
try {
CommunicationFactory.getInstance().boosterOff(node.getNode());
}
catch (Exception ex) {
LOGGER.warn("Switch booster off failed for node: {}", node, ex);
}
}
}
}
@Override
public void replaceMacro(Macro macro, boolean saveOnNode) {
LOGGER.info("Replace the macro: {}", macro);
if (macro != null) {
// set the total number possible functions for this macro
macro.setFunctionSize(CommunicationFactory.getInstance().getMacroLength(model.getSelectedNode().getNode()));
if (model.getSelectedMacro() != null
&& model.getSelectedMacro().getMacroSaveState().equals(MacroSaveState.PENDING_CHANGES)) {
// show dialog
int result =
JOptionPane.showConfirmDialog(view,
Resources.getString(MainController.class, "macro_has_pending_changes"),
Resources.getString(MainController.class, "pending_changes"), JOptionPane.OK_CANCEL_OPTION);
if (result != JOptionPane.OK_OPTION) {
LOGGER.info("User canceled discard pending changes.");
return;
}
}
// replace the macro in the main model
model.replaceMacro(macro);
}
if (saveOnNode) {
LOGGER.info("Transfer the macro to node and save permanently: {}", macro);
try {
CommunicationFactory.getInstance().saveMacro(model.getSelectedNode(), macro);
LOGGER.info("Transfer and save macro passed.");
// reset pending changes
// macro.setMacroSaveState(false);
}
catch (InvalidConfigurationException ex) {
LOGGER.warn("Transfer macro content or save macro failed.", ex);
model.setNodeHasError(model.getSelectedNode(), true);
}
}
}
@Override
public void resetNode(Node node) {
LOGGER.info("Reset the current node: {}", node);
CommunicationFactory.getInstance().reset(node.getNode());
}
@Override
public void replaceAccessory(Accessory accessory, boolean saveOnNode) {
LOGGER.info("Replace the accessory: {}", (accessory != null ? accessory.getDebugString() : null));
if (accessory != null) {
model.replaceAccessory(accessory);
}
if (saveOnNode) {
LOGGER.info("Transfer the accessory to node and save permanently.");
try {
CommunicationFactory.getInstance().saveAccessory(model.getSelectedNode().getNode(), accessory);
LOGGER.info("Transfer and save accessory passed.");
}
catch (InvalidConfigurationException ex) {
LOGGER.warn("Transfer accessory or save accessory failed.", ex);
model.setNodeHasError(model.getSelectedNode(), true);
}
}
}
@Override
public void replacePortConfig(TargetType portType, final Map> portConfig) {
LOGGER.info("Replace the port config, portType: {}, portConfig: {}", portType, portConfig);
try {
Node node = model.getSelectedNode();
switch (portType.getScriptingTargetType()) {
case BACKLIGHTPORT: {
Number lightDimmDown = (Number) portConfig.get(BidibLibrary.BIDIB_PCFG_DIMM_DOWN_8_8).getValue();
int dimmDown = lightDimmDown.intValue();
Number lightDimmUp = (Number) portConfig.get(BidibLibrary.BIDIB_PCFG_DIMM_UP_8_8).getValue();
int dimmUp = lightDimmUp.intValue();
int dmxMapping = 1;
CommunicationFactory.getInstance().setBacklightPortParameters(node.getNode(),
portType.getPortNum().intValue(), dimmDown, dimmUp, dmxMapping);
}
break;
case LIGHTPORT: {
Number lightLevelOff = (Number) portConfig.get(BidibLibrary.BIDIB_PCFG_LEVEL_PORT_OFF).getValue();
int levelOn = lightLevelOff.intValue();
Number lightLevelOn = (Number) portConfig.get(BidibLibrary.BIDIB_PCFG_LEVEL_PORT_ON).getValue();
int levelOff = lightLevelOn.intValue();
Number lightDimmDown = (Number) portConfig.get(BidibLibrary.BIDIB_PCFG_DIMM_DOWN_8_8).getValue();
int dimmDown = lightDimmDown.intValue();
Number lightDimmUp = (Number) portConfig.get(BidibLibrary.BIDIB_PCFG_DIMM_UP_8_8).getValue();
int dimmUp = lightDimmUp.intValue();
Integer lightRgb = null;
PortConfigValue> rgbValue = portConfig.get(BidibLibrary.BIDIB_PCFG_RGB);
if (rgbValue != null) {
lightRgb = (Integer) rgbValue.getValue();
}
CommunicationFactory.getInstance().setLightPortParameters(node.getNode(),
portType.getPortNum().intValue(), levelOff, levelOn, dimmDown, dimmUp, lightRgb);
}
break;
case SERVOPORT: {
Number servoTrimDown = (Number) portConfig.get(BidibLibrary.BIDIB_PCFG_SERVO_ADJ_L).getValue();
int trimDown = servoTrimDown.intValue();
Number servoTrimUp = (Number) portConfig.get(BidibLibrary.BIDIB_PCFG_SERVO_ADJ_H).getValue();
int trimUp = servoTrimUp.intValue();
Number servoSpeed = (Number) portConfig.get(BidibLibrary.BIDIB_PCFG_SERVO_SPEED).getValue();
int speed = servoSpeed.intValue();
CommunicationFactory.getInstance().setServoPortParameters(node.getNode(),
portType.getPortNum().intValue(), trimDown, trimUp, speed);
}
break;
case SWITCHPORT: {
Number switchIoType = (Number) portConfig.get(BidibLibrary.BIDIB_PCFG_IO_CTRL).getValue();
int ioType = switchIoType.intValue();
IoBehaviourEnum ioBehaviour = IoBehaviourEnum.valueOf(ByteUtils.getLowByte(ioType));
Number switchTime = (Number) portConfig.get(BidibLibrary.BIDIB_PCFG_TICKS).getValue();
int time = switchTime.intValue();
CommunicationFactory.getInstance().setSwitchPortParameters(node.getNode(),
portType.getPortNum().intValue(), ioBehaviour, time);
}
break;
default:
LOGGER.warn("Unsupported port type detected: {}", portType);
break;
}
}
catch (Exception ex) {
LOGGER.warn("Replace port config on node failed.", ex);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy