org.bidib.wizard.dmx.client.controller.DmxModelerController Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bidibwizard-dmx-client Show documentation
Show all versions of bidibwizard-dmx-client Show documentation
jBiDiB BiDiB Wizard DMX client POM
package org.bidib.wizard.dmx.client.controller;
import java.io.File;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.function.Supplier;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.collections4.Predicate;
import org.apache.commons.collections4.SetUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.bidib.jbidibc.core.node.ConfigurationVariable;
import org.bidib.jbidibc.messages.SoftwareVersion;
import org.bidib.jbidibc.messages.utils.ByteUtils;
import org.bidib.jbidibc.messages.utils.NodeUtils;
import org.bidib.wizard.api.locale.Resources;
import org.bidib.wizard.api.model.NodeInterface;
import org.bidib.wizard.api.model.NodeListProvider;
import org.bidib.wizard.api.model.NodeProvider;
import org.bidib.wizard.api.model.listener.DefaultNodeListListener;
import org.bidib.wizard.api.model.listener.NodeListListener;
import org.bidib.wizard.api.service.node.NodeService;
import org.bidib.wizard.client.common.uils.SwingUtils;
import org.bidib.wizard.client.common.view.DefaultBusyFrame;
import org.bidib.wizard.client.common.view.DockKeys;
import org.bidib.wizard.client.common.view.DockUtils;
import org.bidib.wizard.common.labels.WizardLabelWrapper;
import org.bidib.wizard.common.model.settings.WizardSettingsInterface;
import org.bidib.wizard.common.service.SettingsService;
import org.bidib.wizard.core.model.connection.ConnectionRegistry;
import org.bidib.wizard.dmx.client.controller.listener.DmxModelerControllerListener;
import org.bidib.wizard.dmx.client.controller.listener.ProgressStatusCallback;
import org.bidib.wizard.dmx.client.controller.listener.TransferListener;
import org.bidib.wizard.dmx.client.event.DmxChannelWrapperEvent;
import org.bidib.wizard.dmx.client.event.DmxTimelineWrapperEvent;
import org.bidib.wizard.dmx.client.model.DmxChannelValue;
import org.bidib.wizard.dmx.client.model.DmxDataRow;
import org.bidib.wizard.dmx.client.model.DmxFixedPattern.FixedPatternItem;
import org.bidib.wizard.dmx.client.model.DmxOverlay.OverlayItem;
import org.bidib.wizard.dmx.client.model.DmxScenery;
import org.bidib.wizard.dmx.client.model.DmxSceneryModel;
import org.bidib.wizard.dmx.client.model.TimeDataIndex;
import org.bidib.wizard.dmx.client.schema.dmxscenery.DmxNodeType;
import org.bidib.wizard.dmx.client.schema.dmxscenery.DmxSceneriesExchange;
import org.bidib.wizard.dmx.client.schema.dmxscenery.DmxSceneriesType;
import org.bidib.wizard.dmx.client.schema.dmxscenery.DmxSceneryType;
import org.bidib.wizard.dmx.client.schema.dmxscenery.DmxTimeType;
import org.bidib.wizard.dmx.client.schema.dmxscenery.DmxTimesType;
import org.bidib.wizard.dmx.client.view.DmxDimmerConfigView;
import org.bidib.wizard.dmx.client.view.DmxSceneryView;
import org.bidib.wizard.dmx.client.view.DmxToolBarProvider;
import org.bidib.wizard.dmx.client.view.dialog.CvTransferProgressDialog;
import org.bidib.wizard.model.dmx.DmxChannel;
import org.bidib.wizard.model.dmx.DmxChannelWrapper;
import org.bidib.wizard.model.dmx.DmxDimmer;
import org.bidib.wizard.model.dmx.DmxTimelineWrapper;
import org.bidib.wizard.model.dmx.LineColors;
import org.oxbow.swingbits.dialog.task.TaskDialogs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.vlsolutions.swing.docking.Dockable;
import com.vlsolutions.swing.docking.DockingConstants;
import com.vlsolutions.swing.docking.DockingDesktop;
import com.vlsolutions.swing.docking.RelativeDockablePosition;
import com.vlsolutions.swing.docking.event.DockableStateChangeEvent;
import com.vlsolutions.swing.docking.event.DockableStateChangeListener;
public class DmxModelerController implements DmxModelerControllerListener, TransferListener {
private static final Logger LOGGER = LoggerFactory.getLogger(DmxModelerController.class);
private static final String FILENAME_DMX_SCENERY_KEY = "dmxSceneryFileName";
private static final String FILENAME_DMX_SCENERY_PATTERN =
"Scenery-default-%010X." + DmxScenery.SCENERIES_EXTENSION;
private final JFrame parent;
private DockingDesktop desktop;
private DockableStateChangeListener dockableStateChangeListener;
private NodeListProvider nodeListProvider;
private DmxSceneryModel dmxSceneryModel;
private DmxSceneryView dmxSceneryView;
private DmxToolBarProvider dmxToolBarProvider;
private NodeListListener nodeListListener;
private final NodeInterface node;
@Autowired
private NodeService nodeService;
@Autowired
private SettingsService settingsService;
@Autowired
private WizardLabelWrapper wizardLabelWrapper;
private final Supplier nodeProviderSupplier;
private String sceneryFilename;
public DmxModelerController(final NodeInterface node, final JFrame parent,
final Supplier nodeProviderSupplier) {
this.parent = parent;
this.node = node;
this.nodeProviderSupplier = nodeProviderSupplier;
this.dmxSceneryModel = new DmxSceneryModel(this.node);
}
@Override
public String getSceneryFilename() {
return sceneryFilename;
}
public void setSceneryFilename(String sceneryFilename) {
this.sceneryFilename = sceneryFilename;
}
private static String prepareSceneryFilename(final NodeInterface node) {
long uniqueId = NodeUtils.getUniqueIdIgnoreClassbits(node.getUniqueId());
String filename = String.format(FILENAME_DMX_SCENERY_PATTERN, uniqueId);
LOGGER.info("Prepared DMX scenery filename: {}", filename);
return filename;
}
/**
* Start the controller.
*
* @param desktop
* the desktop
* @param nodeListProvider
* the nodeListProvider
*/
public void start(final DockingDesktop desktop, final NodeListProvider nodeListProvider) {
this.nodeListProvider = nodeListProvider;
this.desktop = desktop;
LOGGER.info("Start controller.");
// check if the booster table view is already opened
String searchKey = DockKeys.DMX_SCENERY_VIEW;
LOGGER.info("Search for view with key: {}", searchKey);
Dockable view = desktop.getContext().getDockableByKey(searchKey);
if (view != null) {
LOGGER.info("Select the existing DMX scenery view.");
DockUtils.selectWindow(view);
return;
}
final String defaultFilename = prepareSceneryFilename(this.node);
final WizardSettingsInterface wizardSettings = settingsService.getWizardSettings();
String storedDmxSceneryFileName = wizardSettings.getWorkingDirectory(FILENAME_DMX_SCENERY_KEY);
if (StringUtils.isNotBlank(storedDmxSceneryFileName)) {
LOGGER.info("Use stored location for DMX scenery files: {}", storedDmxSceneryFileName);
try {
final File storedFile = new File(storedDmxSceneryFileName);
if (storedFile.getParentFile().exists()) {
this.sceneryFilename = new File(storedFile.getParentFile(), defaultFilename).getPath();
LOGGER.info("Prepared the default DMX scenery file: {}", this.sceneryFilename);
}
}
catch (Exception ex) {
LOGGER.warn("Get the dmx scenery file location from stored scenery filename failed.", ex);
}
}
if (StringUtils.isBlank(this.sceneryFilename)) {
LOGGER.info("Create the defautl DMX scenery file location.");
String storedWorkingDirectory = wizardSettings.getWorkingDirectory(WORKING_DIR_DMX_SCENERY_KEY);
if (StringUtils.isBlank(storedWorkingDirectory)) {
// create default location
final String defaultConfigurationDirectory = settingsService.getMiscSettings().getBidibConfigDir();
final File location = new File(defaultConfigurationDirectory, "config");
if (!location.exists()) {
try {
FileUtils.forceMkdir(location);
}
catch (Exception ex) {
LOGGER.warn("Create directory for DMX scenery files failed.", ex);
}
}
storedWorkingDirectory = new File(location, defaultFilename).getPath();
LOGGER.info("Prepared the location for DMX scenery files: {}", storedWorkingDirectory);
}
else {
storedWorkingDirectory = new File(storedWorkingDirectory, defaultFilename).getPath();
LOGGER.info("Prepared the location for DMX scenery files: {}", storedWorkingDirectory);
}
this.sceneryFilename = storedWorkingDirectory;
}
createDmxSceneryView();
}
private Dockable createDmxSceneryView() {
LOGGER.info("Create the DMX scenery view.");
this.dmxToolBarProvider = new DmxToolBarProvider(desktop, this);
this.dmxToolBarProvider.createToolBar();
this.dmxSceneryView =
new DmxSceneryView(desktop, this.node, this.dmxSceneryModel, this.settingsService, this.wizardLabelWrapper,
this, dmxToolBarProvider);
dmxSceneryView.createPanel();
// add the view below the nodelist panel
final Dockable nodeListPanel = desktop.getContext().getDockableByKey(DockKeys.NODE_LIST_PANEL);
desktop.split(nodeListPanel, dmxSceneryView, DockingConstants.SPLIT_BOTTOM, 0.2);
// create the nodeList listener
this.nodeListListener = new DefaultNodeListListener() {
@Override
public void listNodeAdded(final NodeInterface node) {
LOGGER.info("The nodelist has a new node: {}", node);
nodeNew(node);
}
@Override
public void listNodeRemoved(final NodeInterface node) {
LOGGER.info("The nodelist has a node removed: {}", node);
nodeRemoved(node);
}
};
// register as nodeList listener at the main model
nodeListProvider.addNodeListListener(nodeListListener);
this.dockableStateChangeListener = new DockableStateChangeListener() {
@Override
public void dockableStateChanged(DockableStateChangeEvent event) {
LOGGER.info("State of dockable has changed, event: {}", event);
if (event.getNewState().getDockable().equals(dmxSceneryView) && event.getNewState().isClosed()) {
LOGGER.info("DmxSceneryView was closed, free resources.");
try {
desktop.removeDockableStateChangeListener(dockableStateChangeListener);
}
catch (Exception ex) {
LOGGER
.warn("Remove dockableStateChangeListener from desktop failed: "
+ dockableStateChangeListener, ex);
}
finally {
dockableStateChangeListener = null;
}
cleanup();
// release the view instance
dmxSceneryView = null;
}
}
};
desktop.addDockableStateChangeListener(this.dockableStateChangeListener);
// read the initial CV values from the tools section
readAllCvValuesFromNode();
return dmxSceneryView;
}
private void cleanup() {
LOGGER.info("Cleanup and free resources.");
try {
// remove node listener from communication factory
if (nodeListListener != null) {
DmxModelerController.this.nodeListProvider.removeNodeListListener(nodeListListener);
}
}
catch (Exception ex) {
LOGGER.warn("Unregister controller as node listener failed.", ex);
}
// close the DMX dimmer config views
closeDmxDimmerConfigViews(dmxSceneryModel);
if (DmxModelerController.this.dmxToolBarProvider != null) {
DmxModelerController.this.dmxToolBarProvider.removeView(dmxSceneryView);
}
if (dmxSceneryView != null) {
dmxSceneryView.cleanup();
}
}
@Override
public void transmit(final ProgressStatusCallback callback) {
LOGGER.info("Transmit the data.");
try {
if (callback != null) {
callback.statusChanged(10);
}
readToolsDataCvValues(callback);
}
finally {
if (callback != null) {
callback.transferFinished();
}
}
}
@Override
public void loadScenery(String fileName) {
LOGGER.info("Load scenery from file: {}", fileName);
final DmxSceneriesType dmxSceneriesType = DmxSceneriesExchange.builder().loadDmxSceneries(new File(fileName));
final Map configVariables = node.getConfigVariables();
final List configVariablesToWrite = new LinkedList<>();
if (dmxSceneriesType != null && CollectionUtils.isNotEmpty(dmxSceneriesType.getDmxNode())) {
// TODO support multiple dmx nodes
if (CollectionUtils.isNotEmpty(dmxSceneriesType.getDmxNode().get(0).getDmxScenery())) {
final DmxSceneryType dmxSceneryType = dmxSceneriesType.getDmxNode().get(0).getDmxScenery().get(0);
final DmxScenery dmxScenery =
new DmxScenery(dmxSceneryType.getSceneryName())
.withDmxScenery(dmxSceneryModel, dmxSceneryType, configVariables,
dmxDimmers -> checkForUnmappedChannels(dmxDimmers, configVariablesToWrite),
dmxChannelValue -> {
Integer value = dmxChannelValue.getNewValue();
if (value != null) {
String cvNum = dmxChannelValue.getConfigVar().getName();
configVariablesToWrite
.add(ConfigurationVariable.from(cvNum, ByteUtils.getIntLowByteValue(value)));
LOGGER.info("Add new value to write for dmxChannelValue: {}", dmxChannelValue);
}
else {
LOGGER.info("No new value to write for dmxChannelValue: {}", dmxChannelValue);
}
});
LOGGER.info("Add new DMX scenery: {}", dmxScenery);
// checkForUnmappedChannels(dmxScenery.getDmxDimmers(), configVariablesToWrite);
final DmxScenery existingScenery =
IterableUtils.find(dmxSceneryModel.getSceneries(), new Predicate() {
@Override
public boolean evaluate(DmxScenery currentScenery) {
if (currentScenery.getName().equals(dmxScenery.getName())) {
LOGGER.info("Found scenery to replace: {}", currentScenery);
return true;
}
return false;
}
});
if (existingScenery != null) {
// remove existing scenery
dmxSceneryModel.removeScenery(existingScenery);
}
dmxSceneryModel.addScenery(dmxScenery);
// add the time values
if (dmxSceneriesType.getDmxNode().get(0).getDmxTimes() != null) {
if (CollectionUtils.isNotEmpty(dmxSceneriesType.getDmxNode().get(0).getDmxTimes().getDmxTime())) {
final List dmxTimes =
dmxSceneriesType.getDmxNode().get(0).getDmxTimes().getDmxTime();
for (DmxTimeType dmxTimeType : dmxTimes) {
int cvNumLow = dmxTimeType.getCvNumberLow();
int cvNumHigh = dmxTimeType.getCvNumberHigh();
int timeOffset = dmxTimeType.getTimeOffset();
int[] ticks = calculateTimeTicks(timeOffset);
// TODO
ConfigurationVariable timeTickCv = from(configVariables, cvNumLow);
if (timeTickCv.setValueIfDifferent(Integer.toString(ticks[1]))) {
configVariablesToWrite.add(timeTickCv);
}
timeTickCv = from(configVariables, cvNumHigh);
if (timeTickCv.setValueIfDifferent(Integer.toString(ticks[0]))) {
configVariablesToWrite.add(timeTickCv);
}
}
}
}
// TODO process the dmxScenery
if (!configVariablesToWrite.isEmpty()) {
LOGGER.info("Write changed scenery CV variables to node.");
writeCvValues(this.node, configVariablesToWrite);
}
else {
LOGGER.info("No changed scenery CV variables found to write on node.");
}
// this.dmxSceneryModel.addScenery(dmxScenery);
// reset the dirty flag
this.dmxSceneryModel.setHasPendingChanges(false);
}
}
}
@Override
public void saveScenery(final String fileName, final DmxScenery dmxScenery) {
final DmxSceneriesType dmxSceneriesType = new DmxSceneriesType();
final NodeInterface node = dmxSceneryModel.getNode();
// dmx node
final DmxNodeType dmxNode =
new DmxNodeType()
.withId(ByteUtils.formatHexUniqueId(node.getUniqueId()))
.withNodeName(StringUtils.isNotBlank(node.getLabel()) ? node.getLabel()
: ByteUtils.formatHexUniqueId(node.getUniqueId()))
.withDmxTimes(new DmxTimesType());
dmxSceneriesType.getDmxNode().add(dmxNode);
final Map configVariables = node.getConfigVariables();
// add the time values
for (int timeIndex = 0; timeIndex < DmxSceneryModel.TOTAL_AVAILABLE_TICKS; timeIndex++) {
int cvNumLow = DmxSceneryModel.TICKS_CV_START + (timeIndex * 2);
int cvNumHigh = DmxSceneryModel.TICKS_CV_START + 1 + (timeIndex * 2);
Optional ticksLowValue = optFrom(configVariables, cvNumLow);
Optional ticksHighValue = optFrom(configVariables, cvNumHigh);
int time = calculateTime(ticksHighValue, ticksLowValue);
DmxTimeType dmxTime =
new DmxTimeType().withCvNumberLow(cvNumLow).withCvNumberHigh(cvNumHigh).withTimeOffset(time);
dmxNode.getDmxTimes().getDmxTime().add(dmxTime);
}
for (DmxScenery currentDmxScenery : dmxSceneryModel.getSceneries()) {
if (currentDmxScenery.getName().equals(dmxScenery.getName())) {
LOGGER.info("Store points of scenery: {}", currentDmxScenery);
final DmxSceneryType dmxSceneryType =
currentDmxScenery.fromDmxScenery(dmxDimmer -> getCvValuesForDimmer(dmxDimmer));
dmxNode.getDmxScenery().add(dmxSceneryType);
break;
}
}
if (CollectionUtils.isNotEmpty(dmxSceneriesType.getDmxNode())) {
DmxSceneriesExchange.builder().saveDmxSceneries(dmxSceneriesType, fileName);
final WizardSettingsInterface wizardSettings = settingsService.getWizardSettings();
wizardSettings.setWorkingDirectory(FILENAME_DMX_SCENERY_KEY, fileName);
}
}
private void closeDmxDimmerConfigViews(final DmxSceneryModel dmxSceneryModel) {
// check if DMX modeler views are opened
for (DmxScenery dmxScenery : dmxSceneryModel.getSceneries()) {
for (DmxDimmer dmxDimmer : dmxScenery.getDmxDimmers()) {
String searchKey = DmxDimmerConfigView.prepareKey(dmxDimmer);
LOGGER.info("Search for view with key: {}", searchKey);
Dockable dmxDimmerConfigView = desktop.getContext().getDockableByKey(searchKey);
if (dmxDimmerConfigView != null) {
LOGGER.info("Close the dmxDimmerConfigView: {}", dmxDimmerConfigView);
desktop.close(dmxDimmerConfigView);
}
}
}
}
private void nodeNew(final NodeInterface node) {
}
private void cleanupSelectedNode(final NodeListListener nodeListListener) {
for (DmxDimmer dmxDimmer : dmxSceneryModel.getDimmers()) {
// check if the dimmer is opened
String searchKey = DmxDimmerConfigView.prepareKey(dmxDimmer);
LOGGER.info("Search for view with key: {}", searchKey);
Dockable dmxDimmerConfigViewDockable = desktop.getContext().getDockableByKey(searchKey);
if (dmxDimmerConfigViewDockable != null && dmxDimmerConfigViewDockable instanceof DmxDimmerConfigView) {
LOGGER.info("Close the dmxDimmerConfigView: {}", dmxDimmerConfigViewDockable);
desktop.close(dmxDimmerConfigViewDockable);
}
}
// check if we must close the scenery panel
Dockable dmxSceneryDockable = desktop.getContext().getDockableByKey(DockKeys.DMX_SCENERY_VIEW);
if (dmxSceneryDockable != null) {
LOGGER.info("Close the DMX scenery panel: {}", dmxSceneryDockable);
desktop.close(dmxSceneryDockable);
}
}
private void nodeRemoved(final NodeInterface node) {
if (this.node != null && this.node.equals(node)) {
LOGGER.info("The selected node was removed from the node list.");
cleanupSelectedNode(this.nodeListListener);
}
}
@Override
public void openDmxDimmerConfigView(final DmxDimmer dmxDimmer) {
// check if the channels of the dimmer are configured
final List unmappedDmxChannels = new ArrayList<>();
for (DmxChannel dmxChannel : dmxDimmer.getDmxChannels()) {
Integer dmxChannelId = dmxChannel.getDmxChannelWrapper().getDmxChannelId();
final Predicate missing =
(channelId) -> this.dmxSceneryModel
.getConfiguredDmxChannelsMap().keySet().stream()
.filter(dcw -> Objects.equals(channelId, dcw.getDmxChannelId())).findFirst().isEmpty();
if (dmxChannelId == null || missing.evaluate(dmxChannelId)) {
LOGGER.info("Invalid DMX channel configuration detected for dmxChannelId: {}", dmxChannelId);
unmappedDmxChannels.add(dmxChannelId);
}
}
if (!unmappedDmxChannels.isEmpty()) {
TaskDialogs
.build(JOptionPane.getFrameForComponent(this.parent),
Resources.getString(DmxModelerController.class, "unmapped-dmx-channel.instruction"),
Resources
.getString(DmxModelerController.class, "unmapped-dmx-channel.text",
unmappedDmxChannels.toString()))
.title(Resources.getString(DmxModelerController.class, "unmapped-dmx-channel.title")).inform();
return;
}
// check if the dimmer config is already opened
String searchKey = DmxDimmerConfigView.prepareKey(dmxDimmer);
LOGGER.info("Search for view with key: {}", searchKey);
Dockable dmxDimmerConfigView = desktop.getContext().getDockableByKey(searchKey);
if (dmxDimmerConfigView == null) {
LOGGER.info("Create new DmxDimmerConfigView.");
// Create the DMX modeler view for the scenery
final DmxDimmerConfigView view =
new DmxDimmerConfigView(this.desktop, this, dmxDimmer, this.dmxSceneryModel, this.nodeProviderSupplier,
dmxToolBarProvider);
Dockable tabPanel = desktop.getContext().getDockableByKey(DockKeys.TAB_PANEL);
if (tabPanel != null) {
desktop.createTab(tabPanel, view, 1, true);
}
else {
desktop.addDockable(view, RelativeDockablePosition.RIGHT);
}
}
else {
LOGGER.info("Select the existing dmxDimmer config view.");
DockUtils.selectWindow(dmxDimmerConfigView);
}
}
@Override
public void writeCvValues(final DmxDataRow dmxDataRow, final Runnable finishedCallback) {
LOGGER.info("Write CV values of dmxDataRow: {}", dmxDataRow);
final List configVariablesToWrite = new LinkedList<>();
prepareBrightnessCvToWrite(configVariablesToWrite, dmxDataRow);
writeCvValues(this.node, configVariablesToWrite);
// reset the dirty flag
dmxDataRow.setDirty(false);
if (finishedCallback != null) {
finishedCallback.run();
}
}
@Override
public void writeCvValues(final List dmxDataRows, final Runnable finishedCallback) {
LOGGER.info("Write CV values of dmxDataRows: {}", dmxDataRows);
final List configVariablesToWrite = new LinkedList<>();
for (DmxDataRow dmxDataRow : dmxDataRows) {
prepareBrightnessCvToWrite(configVariablesToWrite, dmxDataRow);
}
if (CollectionUtils.isNotEmpty(configVariablesToWrite)) {
writeCvValues(this.node, configVariablesToWrite);
for (DmxDataRow dmxDataRow : dmxDataRows) {
// reset the dirty flag
dmxDataRow.setDirty(false);
}
if (finishedCallback != null) {
finishedCallback.run();
}
}
else {
LOGGER.info("No changed CV variables to write available.");
}
}
@Override
public void writeBrightnessCvValues(final NodeInterface node, final List changedDmxChannels) {
final List configVariablesToWrite = new ArrayList<>();
for (DmxChannelWrapper dmxChannelWrapper : changedDmxChannels) {
final List dmxBrightnessValues =
this.dmxSceneryModel.getConfiguredDmxChannelsMap().get(dmxChannelWrapper);
for (DmxChannelValue dmxChannelValue : dmxBrightnessValues) {
if (dmxChannelValue.getConfigVar().setValueIfDifferent(String.valueOf(dmxChannelValue.getNewValue()))) {
configVariablesToWrite.add(dmxChannelValue.getConfigVar());
}
}
}
LOGGER.info("Write CV values to node: {}", configVariablesToWrite);
// store the changed values
writeCvValues(this.dmxSceneryModel.getNode(), configVariablesToWrite);
}
private void writeCvValues(final NodeInterface node, final List configVariablesToWrite) {
if (CollectionUtils.isNotEmpty(configVariablesToWrite)) {
// write the CV to the node
final List configVariablesWritten =
this.nodeService
.setConfigVariables(ConnectionRegistry.CONNECTION_ID_MAIN, node, configVariablesToWrite);
LOGGER.info("Update model with configuration variables: {}", configVariablesWritten);
node.updateConfigVariableValues(configVariablesWritten, true);
}
else {
LOGGER.warn("No configuration variables to write available.");
throw new InvalidDataException("Action failed.");
}
}
private void prepareBrightnessCvToWrite(
final List configVariablesToWrite, final DmxDataRow dmxDataRow) {
LOGGER.info("Prepare the CV write, dmxDataRow: {}", dmxDataRow);
if (!dmxDataRow.isRowValid()) {
LOGGER.warn("The dmxDataRow is not valid: {}", dmxDataRow);
throw new InvalidDataException("The dmxDataRow is not valid.");
}
// DMX channel
// brightness values
if (dmxDataRow.getDataIndex() instanceof TimeDataIndex) {
// prepare the CV values
for (DmxChannelValue dmxChannelValue : dmxDataRow.getCvValues()) {
Integer value = dmxChannelValue.getNewValue();
if (value != null) {
String cvNum = dmxChannelValue.getConfigVar().getName();
configVariablesToWrite.add(ConfigurationVariable.from(cvNum, ByteUtils.getIntLowByteValue(value)));
}
else {
LOGGER.debug("No new value for dmxChannelValue: {}", dmxChannelValue);
}
}
LOGGER.debug("Prepared CVs: {}", configVariablesToWrite);
}
}
@Override
public void activateTime(final LocalDateTime time) {
// TODO make acceleration configurable
int acceleration = 10;
LOGGER.info("Activate time, time: {}, acceleration: {}", time, acceleration);
try {
DefaultBusyFrame.setWaitCursor(parent);
nodeService.clock(ConnectionRegistry.CONNECTION_ID_MAIN, node, time, acceleration);
}
finally {
DefaultBusyFrame.setDefaultCursor(parent);
}
}
private void prepareReadTicksCVs(
final Map configVariables,
final List configVariablesToRead) {
// prepare the config variables to read
for (int timeIndex = 0; timeIndex < DmxSceneryModel.TOTAL_AVAILABLE_TICKS; timeIndex++) {
// read the time ticks
ConfigurationVariable cv = from(configVariables, DmxSceneryModel.TICKS_CV_START + (timeIndex * 2));
if (cv.getValue() == null) {
// set the timeout marker
cv.setTimeout(true);
configVariablesToRead.add(cv);
}
cv = from(configVariables, DmxSceneryModel.TICKS_CV_START + 1 + (timeIndex * 2));
if (cv.getValue() == null) {
// set the timeout marker
cv.setTimeout(true);
configVariablesToRead.add(cv);
}
}
}
private void prepareReadAllConfiguredBrightnessCVs(
final Map configVariables,
final List configVariablesToRead, boolean v2Firmware) {
// check if the DMX channel is configured
for (int dmxChannelIndex = 0; dmxChannelIndex < DmxSceneryModel.TOTAL_AVAILABLE_CHANNELS; dmxChannelIndex++) {
int cvNumChannelId =
(v2Firmware ? DmxSceneryModel.FIRST_DMX_CHANNEL_CV_START_V2
: DmxSceneryModel.FIRST_DMX_CHANNEL_CV_START)
+ (dmxChannelIndex * DmxSceneryModel.DMX_CHANNEL_CV_GAP);
ConfigurationVariable cv = from(configVariables, cvNumChannelId);
if (StringUtils.isNotBlank(cv.getValue())) {
try {
int dmxChannelId = Integer.parseInt(cv.getValue());
if (dmxChannelId > 0) {
LOGGER.info("Found configured dmx channel: {}", cv);
prepareReadBrightnessCVs(configVariables, configVariablesToRead, cvNumChannelId);
}
}
catch (Exception ex) {
LOGGER.warn("Check if dmx channel is configured failed for cv: {}", cv, ex);
}
}
}
}
/**
* Prepare all CV of the brightness values of the dmx channel.
*
* @param configVariables
* the current config variables
* @param configVariablesToRead
* the list of CV to read
* @param cvNumChannelId
* the CV number of the DMX channel id (this is the first CV in the block)
*/
private void prepareReadBrightnessCVs(
final Map configVariables,
final List configVariablesToRead, int cvNumChannelId) {
// read all brightness values for this dmx channel
for (int brightnessValueIndex =
2; brightnessValueIndex < DmxSceneryModel.DMX_CHANNEL_CV_GAP; brightnessValueIndex++) {
int cvNumBrightness = cvNumChannelId + brightnessValueIndex;
LOGGER.debug("Current cvNumBrightness: {}", cvNumBrightness);
ConfigurationVariable cvBrightness = from(configVariables, cvNumBrightness);
if (cvBrightness.getValue() == null) {
// set the timeout marker
cvBrightness.setTimeout(true);
configVariablesToRead.add(cvBrightness);
}
}
}
@Override
public List getCvValuesForDimmer(final DmxDimmer dmxDimmer) {
LOGGER.info("Get the CV values for dmxDimmer: {}", dmxDimmer.getName());
// get the configured dmx channels of the dimmer
int numDmxChannels = dmxDimmer.getDmxChannels().size();
LOGGER.info("Configured DMX channels on this dimmer: {}", numDmxChannels);
final Integer[] dmxChannelsOfDimmer = new Integer[numDmxChannels];
for (int dmxChannelId = 0; dmxChannelId < numDmxChannels; dmxChannelId++) {
dmxChannelsOfDimmer[dmxChannelId] =
dmxDimmer.getDmxChannels().get(dmxChannelId).getDmxChannelWrapper().getDmxChannelId();
LOGGER.debug("Set DMX channel Id at index: {}, value: {}", dmxChannelId, dmxChannelsOfDimmer[dmxChannelId]);
}
// Update the dmxDataRows with the values read
final Map configVariables = node.getConfigVariables();
// prepare the mapping for dmx channel id to the cv number where it is stored for the selected dimmer
final Map dmxChannelToCvNumMap = new HashMap<>();
for (int dmxChannelIndex = 0; dmxChannelIndex < numDmxChannels; dmxChannelIndex++) {
Integer dmxChannelValue = dmxChannelsOfDimmer[dmxChannelIndex];
LOGGER.debug("Current dmxChannelValue: {}", dmxChannelValue);
// we must search the CV number of the dmxChannel
Integer cvNumber =
this.dmxSceneryModel
.getConfiguredDmxChannelsMap().keySet().stream()
.filter(dcw -> dcw.getDmxChannelId() == dmxChannelValue).map(dcw -> dcw.getCvNumber()).findFirst()
.orElse(null);
if (cvNumber != null) {
dmxChannelToCvNumMap.put(dmxChannelValue, cvNumber);
}
else {
LOGGER.warn("No configured CV number available for dmxChannelValue: {}", dmxChannelValue);
}
}
final List dmxDataRows =
prepareDmxDataRowWithBrightnessValues(configVariables, dmxChannelsOfDimmer, dmxChannelToCvNumMap);
// store the data rows
this.dmxSceneryModel.addDmxDataRows(dmxDimmer, dmxDataRows);
return dmxDataRows;
}
private int calculateTime(Optional ticksHighValue, Optional ticksLowValue) {
int hours = ticksHighValue.map(val -> val * 100).orElse(0);
int minutes = ticksLowValue.map(val -> val).orElse(0);
int time = hours + minutes;
return time;
}
private int[] calculateTimeTicks(int timeValue) {
int hours = timeValue / 100;
int minutes = timeValue % 100;
return new int[] { hours, minutes };
}
/**
* Prepare the brightness values of the provided dmx channels from the cached values.
*
* @param updatedConfigVariables
* the configuration variables to read the values from
* @param dmxChannelsOfDimmer
* the dmx channels
* @param dmxChannelToCvNumMap
* the map of dmx channel id to cv number
* @return the list of DmxDataRow instances
*/
private List prepareDmxDataRowWithBrightnessValues(
final Map updatedConfigVariables, final Integer[] dmxChannelsOfDimmer,
final Map dmxChannelToCvNumMap) {
// process the config variables
// load the configured time stamps
final List dmxDataRows = new ArrayList<>();
for (int timeIndex = 0; timeIndex < DmxSceneryModel.TOTAL_AVAILABLE_TICKS; timeIndex++) {
Optional ticksLowValue =
optFrom(updatedConfigVariables, DmxSceneryModel.TICKS_CV_START + (timeIndex * 2));
Optional ticksHighValue =
optFrom(updatedConfigVariables, DmxSceneryModel.TICKS_CV_START + 1 + (timeIndex * 2));
int time = calculateTime(ticksHighValue, ticksLowValue);
final DmxDataRow dmxDataRow =
new DmxDataRow(new TimeDataIndex(timeIndex, time, LocalTime.of(time / 60, time % 60)));
int numDmxChannels = dmxChannelsOfDimmer.length;
// get the brightness values for the configured channels
for (int dmxChannelIndex = 0; dmxChannelIndex < numDmxChannels; dmxChannelIndex++) {
// TODO if the DMX channel is not configured in the DMX channel brightness values we have a problem
Integer dmxChannelValue = dmxChannelsOfDimmer[dmxChannelIndex];
LOGGER.debug("Current dmxChannelValue: {}", dmxChannelValue);
final List existingBrightnessValues =
this.dmxSceneryModel
.getConfiguredDmxChannelsMap().entrySet().stream()
.filter(entry -> Objects.equals(dmxChannelValue, entry.getKey().getDmxChannelId())).findFirst()
.map(entry -> entry.getValue()).orElse(null);
if (existingBrightnessValues != null) {
// we must search the CV number of the dmxChannel because this is the first cv of this dmx channel
Integer cvNumber = dmxChannelToCvNumMap.get(dmxChannelValue);
// calculate the cv number of the brightness of the current time index of the dmx channel
int cvNum = timeIndex + cvNumber + 2;
final String cvName = Integer.toString(cvNum);
LOGGER.debug("Current cvNumber: {}, cvNum: {}, cvName: {}", cvNumber, cvNum, cvName);
DmxChannelValue existing =
IterableUtils
.find(existingBrightnessValues,
val -> Objects.equals(val.getConfigVar().getName(), cvName));
if (existing == null) {
final DmxChannelValue newValue = new DmxChannelValue(from(updatedConfigVariables, cvNum));
dmxDataRow.addCvValue(newValue);
LOGGER.info("Add new dmxChannelValue: {}", newValue);
existingBrightnessValues.add(newValue);
}
else {
LOGGER.info("Add existing dmxChannelValue: {}", existing);
dmxDataRow.addCvValue(existing);
}
}
else {
LOGGER.warn("No brightness values found for dmxChannelValue: {}", dmxChannelValue);
}
}
dmxDataRows.add(dmxDataRow);
}
return dmxDataRows;
}
private static ConfigurationVariable from(final Map configVariables, int cvNumber) {
return configVariables.get(Integer.toString(cvNumber));
}
private static Optional optFrom(final Map configVariables, int cvNumber) {
return Optional
.ofNullable(configVariables.get(Integer.toString(cvNumber))).filter(cv -> cv.getValue() != null)
.map(cv -> (cv.getValue().isBlank() ? null : Integer.valueOf(cv.getValue())));
}
private boolean isV2Firmware() {
boolean v2Firmware = node.getNode().getSoftwareVersion().isHigherOrEqualThan(SoftwareVersion.build(2, 0, 0));
return v2Firmware;
}
protected void readToolsDataCvValues(final ProgressStatusCallback callback) {
boolean v2Firmware = isV2Firmware();
LOGGER
.info("Read the toolsdata CV values. Current FIRST_DMX_CHANNEL_CV_START: {}, DMX_CHANNEL_CV_GAP: {}",
v2Firmware ? DmxSceneryModel.FIRST_DMX_CHANNEL_CV_START_V2 : DmxSceneryModel.FIRST_DMX_CHANNEL_CV_START,
DmxSceneryModel.DMX_CHANNEL_CV_GAP);
final List configVariablesToRead = new ArrayList<>();
final Map configVariables = node.getConfigVariables();
// tools data starts from CV 1710 -> number of configured dimmers
ConfigurationVariable cv = from(configVariables, DmxSceneryModel.CV_CONFIGURED_DIMMERS);
if (cv != null && cv.getValue() == null) {
configVariablesToRead.add(cv);
}
// read CVs of all possible configured DMX channels
for (int index = 0; index < DmxSceneryModel.TOTAL_AVAILABLE_CHANNELS; index++) {
cv =
from(configVariables, (v2Firmware ? DmxSceneryModel.FIRST_DMX_CHANNEL_CV_START_V2
: DmxSceneryModel.FIRST_DMX_CHANNEL_CV_START) + (index * DmxSceneryModel.DMX_CHANNEL_CV_GAP));
if (cv.getValue() == null) {
configVariablesToRead.add(cv);
}
}
// read the configured channels of the configured dimmers
for (int index = 0; index < DmxSceneryModel.MAX_CONFIGURED_DIMMERS; index++) {
cv =
from(configVariables,
DmxSceneryModel.CV_CONFIGURED_DIMMER_0_CHANNELS + (index * DmxSceneryModel.DIMMER_GAP));
if (cv.getValue() == null) {
configVariablesToRead.add(cv);
}
for (int channel = 0; channel < DmxSceneryModel.MAX_CONFIGURED_CHANNELS; channel++) {
// assigned dmx channel color
int cvNum =
DmxSceneryModel.CV_CONFIGURED_DIMMER_0_CHANNEL_COLORS_START + (channel * 2)
+ index * DmxSceneryModel.DIMMER_GAP;
// LOGGER.info("Get CV with num: {}", cvNum);
cv = from(configVariables, cvNum);
if (cv != null) {
if (cv.getValue() == null) {
configVariablesToRead.add(cv);
}
}
else {
LOGGER.warn("No configured CV available to get the dmx channel color with cvNum: {}", cvNum);
}
// assigned dmx channel number
cvNum =
DmxSceneryModel.CV_CONFIGURED_DIMMER_0_CHANNEL_COLORS_START + (channel * 2)
+ index * DmxSceneryModel.DIMMER_GAP + 1;
cv = from(configVariables, cvNum);
if (cv != null) {
if (cv.getValue() == null) {
configVariablesToRead.add(cv);
}
}
else {
LOGGER.warn("No configured CV available to get the dmx channel number with cvNum: {}", cvNum);
}
}
}
// prepare the ticks CVs to read from the node
prepareReadTicksCVs(configVariables, configVariablesToRead);
LOGGER.debug("Prepared CVs: {}", configVariablesToRead);
if (callback != null) {
callback.messageChanged("Read CVs from node: " + configVariablesToRead.size());
}
// read configured dmx channels and ticks CVs from the node
readCvFromNode(configVariablesToRead);
// remove all already read data
configVariablesToRead.clear();
// read all brightness CVs for the configured DMX channels
prepareReadAllConfiguredBrightnessCVs(configVariables, configVariablesToRead, v2Firmware);
LOGGER.debug("Prepared brightness CVs to read: {}", configVariablesToRead);
if (callback != null) {
callback.messageChanged("Read CVs from node: " + configVariablesToRead.size());
callback.statusChanged(30);
}
readCvFromNode(configVariablesToRead);
// Update the dmxDataRows with the values read
final Map updatedConfigVariables = node.getConfigVariables();
// find all configured dmx channel of the node
for (int dmxChannelIndex = 0; dmxChannelIndex < DmxSceneryModel.TOTAL_AVAILABLE_CHANNELS; dmxChannelIndex++) {
int cvNum =
(v2Firmware ? DmxSceneryModel.FIRST_DMX_CHANNEL_CV_START_V2
: DmxSceneryModel.FIRST_DMX_CHANNEL_CV_START)
+ (dmxChannelIndex * DmxSceneryModel.DMX_CHANNEL_CV_GAP);
Integer dmxChannelValue = optFrom(updatedConfigVariables, cvNum).orElse(null);
// keep the configured channels in the map
if (dmxChannelValue != null && dmxChannelValue.intValue() > 0) {
Optional existing = getConfiguredDmxChannelWrapper(dmxChannelValue);
if (existing.isEmpty()) {
LOGGER.info("Register the DMX channel: {}", dmxChannelValue);
this.dmxSceneryModel
.getConfiguredDmxChannelsMap()
.put(new DmxChannelWrapper(dmxChannelValue, cvNum), new ArrayList<>());
}
}
else {
LOGGER.warn("No dmxChannelValue for cvNum: {}", cvNum);
this.dmxSceneryModel
.getConfiguredDmxChannelsMap()
.put(new DmxChannelWrapper(dmxChannelValue, cvNum), new ArrayList<>());
}
}
// check if we have a stored scenery available and take the names from there
Optional defaultScenery = Optional.empty();
if (StringUtils.isBlank(this.sceneryFilename)) {
final WizardSettingsInterface wizardSettings = settingsService.getWizardSettings();
String storedDmxSceneryFileName = wizardSettings.getWorkingDirectory(FILENAME_DMX_SCENERY_KEY);
if (StringUtils.isNotBlank(storedDmxSceneryFileName)) {
LOGGER.info("Use stored location for DMX scenery files: {}", storedDmxSceneryFileName);
this.sceneryFilename = storedDmxSceneryFileName;
}
else {
LOGGER.info("No stored default scenery file available.");
this.sceneryFilename = prepareSceneryFilename(this.node);
}
}
try {
final File sceneryFile = new File(this.sceneryFilename);
if (sceneryFile.exists()) {
LOGGER.info("Found scenery file: {}", this.sceneryFilename);
final DmxSceneriesType dmxSceneriesType = DmxSceneriesExchange.builder().loadDmxSceneries(sceneryFile);
if (dmxSceneriesType != null) {
if (CollectionUtils.isNotEmpty(dmxSceneriesType.getDmxNode())) {
// TODO support more than a single dmx node
if (CollectionUtils.isNotEmpty(dmxSceneriesType.getDmxNode().get(0).getDmxScenery())) {
final DmxSceneryType dmxSceneryType =
dmxSceneriesType.getDmxNode().get(0).getDmxScenery().get(0);
defaultScenery =
Optional
.ofNullable(DmxScenery
.fromDmxSceneryType(dmxSceneryType,
dmxChannelNumber -> getConfiguredDmxChannelWrapper(dmxChannelNumber)
.orElse(null)));
LOGGER.info("Found defaultScenery: {}", defaultScenery);
}
}
}
}
}
catch (Exception ex) {
LOGGER.warn("Load stored default scenery failed.", ex);
}
// create the DMX scenery
final DmxScenery dmxScenery = prepareScenery(updatedConfigVariables, defaultScenery, v2Firmware);
final List configVariablesToWrite = new ArrayList<>();
checkForUnmappedChannels(dmxScenery.getDmxDimmers(), configVariablesToWrite);
if (!configVariablesToWrite.isEmpty()) {
// Write the updated DMX-Channel CV on the node
writeCvValues(this.node, configVariablesToWrite);
}
SwingUtils.executeInEDT(() -> this.dmxSceneryModel.addScenery(dmxScenery));
}
private void checkForUnmappedChannels(
final List dmxDimmers, final List configVariablesToWrite) {
LOGGER.info("Check for unmapped channels of dimmers: {}", dmxDimmers);
// check if we have an unmapped DMX channel in the dimmers
final List mappedChannels = new ArrayList<>();
final List unmappedChannels = new ArrayList<>();
for (DmxDimmer dmxDimmer : dmxDimmers) {
for (DmxChannel dmxChannel : dmxDimmer.getDmxChannels()) {
Integer channelId = dmxChannel.getDmxChannelWrapper().getDmxChannelId();
Optional existing =
this.dmxSceneryModel
.getConfiguredDmxChannelsMap().keySet().stream()
.filter(dcw -> Objects.equals(channelId, dcw.getDmxChannelId())).findFirst();
if (existing.isPresent()) {
LOGGER.info("Found mapped channel: {}, dimmer: {}", channelId, dmxDimmer.getName());
mappedChannels.add(DmxChannelWrapper.searchKey(channelId));
}
else {
LOGGER.info("Found unmapped channel: {}, dimmer: {}", channelId, dmxDimmer.getName());
unmappedChannels.add(DmxChannelWrapper.searchKey(channelId));
}
}
}
// check if we have unmapped DMX channels
if (!unmappedChannels.isEmpty()) {
for (DmxChannelWrapper unmappedChannelId : unmappedChannels) {
// find 'free' mapping in CVs
final List configuredDmxChannelIds =
new ArrayList<>(this.dmxSceneryModel.getConfiguredDmxChannelsMap().keySet());
Collections
.sort(configuredDmxChannelIds,
(dcw0, dcw1) -> Integer.compare(dcw0.getCvNumber(), dcw1.getCvNumber()));
for (DmxChannelWrapper currentChannelId : configuredDmxChannelIds) {
LOGGER
.info("Process currentChannelId: {}, unmappedChannelId: {}", currentChannelId,
unmappedChannelId);
// check if the current channel id is used already in a dimmer
if (mappedChannels
.stream().filter(wrapper -> wrapper.getDmxChannelId() == currentChannelId.getDmxChannelId())
.findFirst().isEmpty()) {
int cvNum = currentChannelId.getCvNumber();
LOGGER
.info(
"Found 'free' mapping to re-use with channelId: {} for unmappedChannelId: {}, cvNum: {}",
currentChannelId, unmappedChannelId, cvNum);
final List existingChannelValues =
this.dmxSceneryModel.getConfiguredDmxChannelsMap().remove(currentChannelId);
final DmxChannelWrapper newWrapper =
new DmxChannelWrapper(unmappedChannelId.getDmxChannelId(), cvNum);
this.dmxSceneryModel.getConfiguredDmxChannelsMap().put(newWrapper, existingChannelValues);
mappedChannels.add(newWrapper);
// prepare update the DMX-Channel CV on the node
configVariablesToWrite
.add(ConfigurationVariable
.from(cvNum, ByteUtils.getIntLowByteValue(unmappedChannelId.getDmxChannelId())));
break;
}
else {
LOGGER.info("Current channel is mapped: {}", currentChannelId);
}
}
}
}
}
private void readCvFromNode(final List configVariablesToRead) {
if (CollectionUtils.isNotEmpty(configVariablesToRead)) {
final List configVariablesRead =
this.nodeService
.queryConfigVariables(ConnectionRegistry.CONNECTION_ID_MAIN, node, configVariablesToRead);
LOGGER.debug("Update model with configuration variables: {}", configVariablesRead);
// check if timeout detected
final List configVariablesTimeoutToRead = new ArrayList<>();
for (ConfigurationVariable cv : configVariablesRead) {
if (cv.isTimeout()) {
LOGGER.warn("Detected CV with timeout: {}", cv);
configVariablesTimeoutToRead.add(cv);
}
}
if (!configVariablesTimeoutToRead.isEmpty()) {
LOGGER.info("Fetch the timed out cv values once again.");
final List configVariablesTimeoutRead =
this.nodeService
.queryConfigVariables(ConnectionRegistry.CONNECTION_ID_MAIN, node,
configVariablesTimeoutToRead);
for (ConfigurationVariable cv : configVariablesTimeoutRead) {
if (!cv.isTimeout()) {
LOGGER.info("Update the re-read value from cv: {}", cv);
configVariablesRead
.stream().filter(cvFilter -> Objects.equals(cvFilter.getName(), cv.getName()))
.map(cvFilter -> {
cvFilter.setValue(cv.getValue());
cvFilter.setTimeout(false);
return cvFilter;
});
}
}
}
//
node.updateConfigVariableValues(configVariablesRead, true);
}
else {
LOGGER.info("No unread CV detected.");
}
}
private DmxScenery prepareScenery(
final Map updatedConfigVariables, final Optional defaultScenery,
boolean v2Firmware) {
// create the DMX scenery
final DmxScenery dmxScenery =
new DmxScenery(defaultScenery.map(ds -> ds.getId()).orElse(UUID.randomUUID().toString()));
// set the name of the scenery
dmxScenery
.setName(defaultScenery
.map(ds -> ds.getName()).orElse(Resources.getString(DmxModelerController.class, "dmx-scenery")));
Optional configuredDimmersValue =
optFrom(updatedConfigVariables, DmxSceneryModel.CV_CONFIGURED_DIMMERS);
int configuredDimmersCount = configuredDimmersValue.orElse(0);
if (configuredDimmersCount > 0) {
final Optional> defaultDimmers =
defaultScenery.filter(ds -> !ds.getDmxDimmers().isEmpty()).map(ds -> ds.getDmxDimmers());
for (int index = 0; index < configuredDimmersCount; index++) {
final int listIndex = index;
Optional defaultDimmer =
defaultDimmers.filter(dds -> dds.size() > listIndex).map(dds -> dds.get(listIndex));
final DmxDimmer dmxDimmer =
new DmxDimmer(defaultDimmer.map(dd -> dd.getId()).orElse(UUID.randomUUID().toString()));
int configuredDimmerChannels =
optFrom(updatedConfigVariables,
DmxSceneryModel.CV_CONFIGURED_DIMMER_0_CHANNELS + (index * DmxSceneryModel.DIMMER_GAP))
.orElse(0);
// set the name of the dimmer
dmxDimmer
.withName(defaultDimmer
.map(dd -> dd.getName())
.orElse(Resources.getString(DmxModelerController.class, "dmx-dimmer", index)));
if (configuredDimmerChannels == 0 && index >= DmxSceneryModel.MAX_CONFIGURED_DIMMERS) {
// TODO more dimmers than storage in node -> check the default scenery
configuredDimmerChannels = defaultDimmer.map(dd -> dd.getDmxChannels().size()).orElse(0);
}
final List dmxChannels = new ArrayList<>();
for (int channelColorIdx = 0; channelColorIdx < configuredDimmerChannels; channelColorIdx++) {
// assigned dmx channel color
int cvNum =
DmxSceneryModel.CV_CONFIGURED_DIMMER_0_CHANNEL_COLORS_START + (channelColorIdx * 2)
+ index * DmxSceneryModel.DIMMER_GAP;
Optional opChannelColor = optFrom(updatedConfigVariables, cvNum);
if (opChannelColor.isEmpty()) {
opChannelColor =
Optional.ofNullable(channelColorSupplier(defaultDimmer, channelColorIdx).get());
}
int channelColor = opChannelColor.orElse(null);
// assigned dmx channel number
cvNum =
DmxSceneryModel.CV_CONFIGURED_DIMMER_0_CHANNEL_COLORS_START + (channelColorIdx * 2)
+ index * DmxSceneryModel.DIMMER_GAP + 1;
final int channelColorIdxFin = channelColorIdx;
Integer dmxChannelNumber = optFrom(updatedConfigVariables, cvNum).orElseGet(() -> {
final DmxChannel dmxChannel = getDmxChannel(defaultDimmer, channelColorIdxFin);
if (dmxChannel != null) {
return dmxChannel.getDmxChannelWrapper().getDmxChannelId();
}
return null;
});
LOGGER.info("Current dmxChannelNumber: {}, channelColor: {}", dmxChannelNumber, channelColor);
Optional existing = getConfiguredDmxChannelWrapper(dmxChannelNumber);
if (existing.isPresent()) {
DmxChannel dmxChannel =
new DmxChannel(dmxDimmer, existing.get())
.withLineColor(LineColors.fromColorValue(channelColor));
dmxChannels.add(dmxChannel);
}
else {
LOGGER.warn("Skip unconfigured DMX channel number: {}", dmxChannelNumber);
}
}
dmxDimmer.setDmxChannels(dmxChannels);
dmxScenery.addDmxDimmer(dmxDimmer);
}
}
return dmxScenery;
}
private DmxChannel getDmxChannel(final Optional dmxDimmer, int channelColorIdx) {
if (dmxDimmer.isEmpty() || !(dmxDimmer.get().getDmxChannels().size() > channelColorIdx)) {
return null;
}
return dmxDimmer.get().getDmxChannels().get(channelColorIdx);
}
protected static Supplier channelColorSupplier(
final Optional defaultDimmer, int channelColorIdx) {
if (defaultDimmer.isPresent() && defaultDimmer.get().getDmxChannels().size() > channelColorIdx) {
DmxChannel dc = defaultDimmer.get().getDmxChannels().get(channelColorIdx);
return () -> Optional
.of(dc).map(idc -> idc.getLineColor()).map(lc -> Integer.valueOf(LineColors.toColorValue(lc)))
.orElse(Integer.valueOf(255));
}
return () -> Integer.valueOf(0);
}
private Optional getConfiguredDmxChannelWrapper(Integer dmxChannelValue) {
return this.dmxSceneryModel
.getConfiguredDmxChannelsMap().keySet().stream()
.filter(dcw -> Objects.equals(dmxChannelValue, dcw.getDmxChannelId())).findFirst();
}
private void writeToolsDataCvValues(final List errorMessages, boolean v2Firmware) {
LOGGER.info("Write the toolsdata CV values.");
final List configVariablesToWrite = new ArrayList<>();
if (CollectionUtils.isNotEmpty(this.dmxSceneryModel.getSceneries())) {
final DmxScenery dmxScenery = this.dmxSceneryModel.getSceneries().get(0);
final Map configVariables = node.getConfigVariables();
// check if we have dimmers
if (CollectionUtils.isNotEmpty(dmxScenery.getDmxDimmers())) {
List dmxDimmers = dmxScenery.getDmxDimmers();
int configuredDimmers = dmxDimmers.size();
ConfigurationVariable configuredDimmersCv =
from(configVariables, DmxSceneryModel.CV_CONFIGURED_DIMMERS);
if (configuredDimmersCv.setValueIfDifferent(String.valueOf(configuredDimmers))) {
configVariablesToWrite.add(configuredDimmersCv);
}
if (configuredDimmers > 0) {
int index = 0;
int maxDimmerIndex = configuredDimmers;
if (maxDimmerIndex > DmxSceneryModel.MAX_CONFIGURED_DIMMERS) {
maxDimmerIndex = DmxSceneryModel.MAX_CONFIGURED_DIMMERS;
LOGGER
.warn(
"More DMX dimmers configured than storage in CV space available. The stored configuration on the node will be truncated to match the available space.");
// notify user about truncated configuration
errorMessages.add(Resources.getString(DmxModelerController.class, "dimmer-space-exceeded"));
}
for (int dmxDimmerIndex = 0; dmxDimmerIndex < maxDimmerIndex; dmxDimmerIndex++) {
final DmxDimmer dmxDimmer = dmxDimmers.get(dmxDimmerIndex);
int configuredDimmerChannels = dmxDimmer.getDmxChannels().size();
LOGGER
.debug("Current dimmer: {}, the configured dimmer channels: {}", dmxDimmer,
configuredDimmerChannels);
ConfigurationVariable configuredDimmerChannelsCv =
from(configVariables,
DmxSceneryModel.CV_CONFIGURED_DIMMER_0_CHANNELS + (index * DmxSceneryModel.DIMMER_GAP));
if (configuredDimmerChannelsCv.setValueIfDifferent(String.valueOf(configuredDimmerChannels))) {
configVariablesToWrite.add(configuredDimmerChannelsCv);
}
int maxChannelIndex = configuredDimmerChannels;
if (maxChannelIndex > DmxSceneryModel.MAX_CONFIGURED_CHANNELS) {
maxChannelIndex = DmxSceneryModel.MAX_CONFIGURED_CHANNELS;
LOGGER
.warn(
"More DMX channels configured than storage in CV space available. The stored configuration on the node will be truncated to match the available space.");
// notify user about truncated configuration
errorMessages
.add(Resources.getString(DmxModelerController.class, "channel-space-exceeded"));
}
for (int channelColorIdx = 0; channelColorIdx < maxChannelIndex; channelColorIdx++) {
int cvNum =
DmxSceneryModel.CV_CONFIGURED_DIMMER_0_CHANNEL_COLORS_START + (channelColorIdx * 2)
+ index * DmxSceneryModel.DIMMER_GAP;
int channelColor =
LineColors.toColorValue(dmxDimmer.getDmxChannels().get(channelColorIdx).getLineColor());
LOGGER.debug("Current color CV: {}, channelColor: {}", cvNum, channelColor);
ConfigurationVariable configuredChannelColorCv = from(configVariables, cvNum);
if (configuredChannelColorCv.setValueIfDifferent(String.valueOf(channelColor))) {
configVariablesToWrite.add(configuredChannelColorCv);
}
// next cv is the dmx channel
cvNum++;
int dmxChannel =
dmxDimmer
.getDmxChannels().get(channelColorIdx).getDmxChannelWrapper().getDmxChannelId();
LOGGER.debug("Current channel CV: {}, dmx channel: {}", cvNum, dmxChannel);
ConfigurationVariable configuredDmxChannelCv = from(configVariables, cvNum);
if (configuredDmxChannelCv.setValueIfDifferent(String.valueOf(dmxChannel))) {
configVariablesToWrite.add(configuredDmxChannelCv);
}
}
index++;
}
}
}
else {
ConfigurationVariable configuredDimmersCv =
from(configVariables, DmxSceneryModel.CV_CONFIGURED_DIMMERS);
if (configuredDimmersCv.setValueIfDifferent(String.valueOf(0))) {
configVariablesToWrite.add(configuredDimmersCv);
}
}
if (CollectionUtils.isNotEmpty(configVariablesToWrite)) {
LOGGER.info("Write toolsdata variables to node.");
this.nodeService
.setConfigVariables(ConnectionRegistry.CONNECTION_ID_MAIN, node, configVariablesToWrite);
}
else {
LOGGER.info("No changed CV variables found to write on node.");
}
// reset the dirty flag
this.dmxSceneryModel.setHasPendingChanges(false);
}
}
@Override
public void selectedDmxChannelChanged(DmxDimmer dmxDimmer, int dmxChannelId) {
// Search if the dimmer config is already opened
String searchKey = DmxDimmerConfigView.prepareKey(dmxDimmer);
LOGGER.info("Search for view with key: {}", searchKey);
Dockable dmxDimmerConfigViewDockable = desktop.getContext().getDockableByKey(searchKey);
if (dmxDimmerConfigViewDockable != null) {
try {
final DmxDimmerConfigView view = (DmxDimmerConfigView) dmxDimmerConfigViewDockable;
view.selectedDmxChannelChanged(dmxDimmer, dmxChannelId);
}
catch (Exception ex) {
LOGGER.warn("Process selected DMX channel changed in dimmer config view failed.", ex);
}
}
}
@Override
public Map> getConfiguredDmxChannelsMap() {
return MapUtils.unmodifiableMap(this.dmxSceneryModel.getConfiguredDmxChannelsMap());
}
@Override
public void setChangedDmxChannelConfiguration(List changedDmxChannels) {
final Map configVariables = node.getConfigVariables();
final List configVariablesToWrite = new ArrayList<>();
final List configVariablesToRead = new ArrayList<>();
// the changed dmx channels contains the new dmx channel id and the cv number.
// we must check if the previous configured channel id was 0 for the cv number to detect if we must load the cv
// values with the brightness for this channel
for (DmxChannelWrapper wrapper : changedDmxChannels) {
int channelCv = wrapper.getCvNumber();
// get the configuration variable for the current channel cv
final ConfigurationVariable configuredChannelCv = from(configVariables, channelCv);
// check if the previous channel id is 0
int prevChannelId = Integer.parseInt(configuredChannelCv.getValue());
if (prevChannelId == 0) {
LOGGER
.info(
"The previous channel id was not assigned. We have to load the cv values of the brightness for this channel. Use the channelCv: {}",
channelCv);
// we must prepare to load the cv values of the brightness for this channel
prepareReadBrightnessCVs(configVariables, configVariablesToRead, channelCv);
}
// set the new channel id value
if (configuredChannelCv.setValueIfDifferent(String.valueOf(wrapper.getDmxChannelId().intValue()))) {
configVariablesToWrite.add(configuredChannelCv);
}
}
if (CollectionUtils.isNotEmpty(configVariablesToWrite)) {
LOGGER.info("Write variables to node.");
// write the CV to the node
final List configVariablesWritten =
this.nodeService
.setConfigVariables(ConnectionRegistry.CONNECTION_ID_MAIN, node, configVariablesToWrite);
LOGGER.info("Update model with configuration variables: {}", configVariablesWritten);
node.updateConfigVariableValues(configVariablesWritten, true);
// update the mappings
for (DmxChannelWrapper wrapper : changedDmxChannels) {
int channelCv = wrapper.getCvNumber();
this.dmxSceneryModel
.getConfiguredDmxChannelsMap().keySet().stream().filter(dcw -> dcw.getCvNumber() == channelCv)
.findFirst().ifPresent(dcw -> dcw.setDmxChannelId(wrapper.getDmxChannelId()));
}
}
else {
LOGGER.info("No changed CV variables found to write on node.");
}
// read the cv brightness values of the new configured DMX channels from the node
LOGGER.info("Read the brightness cv values from the node.");
readCvFromNode(configVariablesToRead);
// notify the listener of changes
for (DmxChannelWrapper wrapper : changedDmxChannels) {
this.dmxSceneryModel.publishDmxChannelEvent(new DmxChannelWrapperEvent(wrapper, false));
}
}
@Override
public Set getConfiguredDmxTimelineSet() {
final Map updatedConfigVariables = node.getConfigVariables();
final Set configuredDmxTimelineSet = new HashSet<>();
// find all configured dmx times of the node
for (int timeIndex = 0; timeIndex < DmxSceneryModel.TOTAL_AVAILABLE_TICKS; timeIndex++) {
int cvNumLow = DmxSceneryModel.TICKS_CV_START + (timeIndex * 2);
int cvNumHigh = DmxSceneryModel.TICKS_CV_START + 1 + (timeIndex * 2);
Optional ticksLowValue = optFrom(updatedConfigVariables, cvNumLow);
Optional ticksHighValue = optFrom(updatedConfigVariables, cvNumHigh);
int dmxTime = calculateTime(ticksHighValue, ticksLowValue);
LOGGER.info("Register the DMX time: {}, cvNumLow: {}, cvNumHigh: {}", dmxTime, cvNumLow, cvNumHigh);
configuredDmxTimelineSet.add(new DmxTimelineWrapper(dmxTime, cvNumHigh, cvNumLow));
}
return SetUtils.unmodifiableSet(configuredDmxTimelineSet);
}
@Override
public void setChangedDmxTimelineConfiguration(List changedDmxTimeline) {
final Map configVariables = node.getConfigVariables();
final List configVariablesToWrite = new ArrayList<>();
// final List configVariablesToRead = new ArrayList<>();
for (DmxTimelineWrapper wrapper : changedDmxTimeline) {
int cvNumHigh = wrapper.getCvNumberHigh();
int cvNumLow = wrapper.getCvNumberLow();
int dmxTime = wrapper.getDmxTime();
LOGGER
.info("Current dmxTime: {}, cvNumHigh: {}, cvNumLow: {}, timeHigh: {}, timeLow: {}", dmxTime, cvNumHigh,
cvNumLow, dmxTime / 60, dmxTime % 60);
// get the configuration variable for the current channel cv
final ConfigurationVariable configuredCvTimeHigh = from(configVariables, cvNumHigh);
final ConfigurationVariable configuredCvTimeLow = from(configVariables, cvNumLow);
// set the new time value
if (configuredCvTimeHigh.setValueIfDifferent(String.valueOf(dmxTime / 100))) {
configVariablesToWrite.add(configuredCvTimeHigh);
}
if (configuredCvTimeLow.setValueIfDifferent(String.valueOf(dmxTime % 100))) {
configVariablesToWrite.add(configuredCvTimeLow);
}
}
if (CollectionUtils.isNotEmpty(configVariablesToWrite)) {
LOGGER.info("Write variables to node.");
// write the CV to the node
final List configVariablesWritten =
this.nodeService
.setConfigVariables(ConnectionRegistry.CONNECTION_ID_MAIN, node, configVariablesToWrite);
LOGGER.info("Update model with configuration variables: {}", configVariablesWritten);
node.updateConfigVariableValues(configVariablesWritten, true);
// notify the listener of changes
this.dmxSceneryModel.publishDmxTimelineEvent(new DmxTimelineWrapperEvent());
}
else {
LOGGER.info("No changed CV variables found to write on node.");
}
}
@Override
public boolean hasPendingChanges(final DmxDimmer dmxDimmer) {
String searchKey = DmxDimmerConfigView.prepareKey(dmxDimmer);
LOGGER.info("Search for view with key: {}", searchKey);
Dockable dmxDimmerConfigView = desktop.getContext().getDockableByKey(searchKey);
if (dmxDimmerConfigView instanceof DmxDimmerConfigView) {
LOGGER.info("Check for pending changes, dmxDimmerConfigView: {}", dmxDimmerConfigView);
return ((DmxDimmerConfigView) dmxDimmerConfigView).hasPendingChanges();
}
return false;
}
@Override
public void writeChangedCvValuesToNode() {
LOGGER.info("Write all changed CV values to node.");
boolean v2Firmware = isV2Firmware();
final List errorMessages = new ArrayList<>();
// check if we have to write the tools data values
if (this.dmxSceneryModel.isHasPendingChanges()) {
LOGGER.info("The DMX scenery model has pending changes.");
writeToolsDataCvValues(errorMessages, v2Firmware);
}
// check if DMX modeler views are opened
final List dmxDimmerConfigViews = new ArrayList<>();
for (DmxScenery dmxScenery : dmxSceneryModel.getSceneries()) {
for (DmxDimmer dmxDimmer : dmxScenery.getDmxDimmers()) {
String searchKey = DmxDimmerConfigView.prepareKey(dmxDimmer);
LOGGER.info("Search for view with key: {}", searchKey);
Dockable dmxDimmerConfigView = desktop.getContext().getDockableByKey(searchKey);
if (dmxDimmerConfigView != null) {
LOGGER.info("Add the dmxDimmerConfigView: {}", dmxDimmerConfigView);
dmxDimmerConfigViews.add(dmxDimmerConfigView);
}
}
}
for (Dockable dockable : dmxDimmerConfigViews) {
if (dockable instanceof DmxDimmerConfigView) {
DmxDimmerConfigView dmxDimmerConfigView = (DmxDimmerConfigView) dockable;
try {
dmxDimmerConfigView.writeChangedCvValues();
}
catch (InvalidDataException ex) {
LOGGER.warn("The CV data to write is not valid.", ex);
TaskDialogs
.build(JOptionPane.getFrameForComponent(this.parent),
Resources.getString(DmxModelerController.class, "invalid-cv-data.instruction"),
Resources.getString(DmxModelerController.class, "invalid-cv-data.text"))
.title(Resources.getString(DmxModelerController.class, "invalid-cv-data.title"))
.showException(ex);
}
}
}
if (!errorMessages.isEmpty()) {
// show warning dialog
// prepare message text
StringBuilder sb =
new StringBuilder(Resources.getString(DmxModelerController.class, "storage-cv-data-exceeded.text"));
for (String errorMessage : errorMessages) {
sb.append(errorMessage).append("\n");
}
TaskDialogs
.build(JOptionPane.getFrameForComponent(this.parent),
Resources.getString(DmxModelerController.class, "storage-cv-data-exceeded.instruction"),
sb.toString())
.title(Resources.getString(DmxModelerController.class, "storage-cv-data-exceeded.title")).inform();
}
}
@Override
public void readAllCvValuesFromNode() {
LOGGER.info("Read all relevant CV values from the node.");
SwingUtilities
.invokeLater(
() -> new CvTransferProgressDialog(dmxSceneryView.getComponent(), true, DmxModelerController.this));
}
@Override
public void getFixedPatternCvValues(final List items, final ProgressStatusCallback callback) {
LOGGER.info("Read the fixed pattern cv values.");
final List configVariablesToRead = new ArrayList<>();
final Map configVariables = node.getConfigVariables();
for (FixedPatternItem item : items) {
int cvNumber = item.getCvNumber();
ConfigurationVariable cvDmxChannelId = from(configVariables, cvNumber);
if (cvDmxChannelId.getValue() == null) {
configVariablesToRead.add(cvDmxChannelId);
}
ConfigurationVariable cvBrightness = from(configVariables, cvNumber + 1);
if (cvBrightness.getValue() == null) {
configVariablesToRead.add(cvBrightness);
}
}
LOGGER.debug("Prepared CVs: {}", configVariablesToRead);
if (callback != null) {
callback.messageChanged("Read CVs from node: " + configVariablesToRead.size());
}
// read dmx channels and brightness CVs from the node
readCvFromNode(configVariablesToRead);
final Map updatedConfigVariables = node.getConfigVariables();
for (FixedPatternItem item : items) {
int cvNumber = item.getCvNumber();
Integer dmxChannelValue = optFrom(updatedConfigVariables, cvNumber).orElse(null);
item.setDmxChannelId(dmxChannelValue);
Integer brightnessValue = optFrom(updatedConfigVariables, cvNumber + 1).orElse(null);
item.setBrightnessValue(brightnessValue);
}
}
@Override
public void writeFixedPatternCvValues(List fixedPatternItems, Runnable finishedCallback) {
LOGGER.info("Write CV values of fixedPatternItems: {}", fixedPatternItems);
final List configVariablesToWrite = new LinkedList<>();
final Map configVariables = node.getConfigVariables();
for (FixedPatternItem item : fixedPatternItems) {
int cvNumber = item.getCvNumber();
if (item.getDmxChannelId() != null && item.getBrightnessValue() != null) {
Integer channelId = item.getDmxChannelId();
Integer brightness = item.getBrightnessValue();
addIfDifferent(configVariables, configVariablesToWrite, cvNumber, channelId);
addIfDifferent(configVariables, configVariablesToWrite, cvNumber + 1, brightness);
}
else {
LOGGER.debug("No new value for item: {}", item);
addIfDifferent(configVariables, configVariablesToWrite, cvNumber, 0);
addIfDifferent(configVariables, configVariablesToWrite, cvNumber + 1, 0);
}
}
if (CollectionUtils.isNotEmpty(configVariablesToWrite)) {
writeCvValues(this.node, configVariablesToWrite);
if (finishedCallback != null) {
finishedCallback.run();
}
}
else {
LOGGER.info("No changed CV variables to write available.");
}
}
@Override
public void getOverlayCvValues(List items, ProgressStatusCallback callback) {
LOGGER.info("Read the overlay cv values.");
final List configVariablesToRead = new ArrayList<>();
boolean v2Firmware = node.getNode().getSoftwareVersion().isHigherOrEqualThan(SoftwareVersion.build(2, 0, 0));
final Map configVariables = node.getConfigVariables();
for (OverlayItem item : items) {
int cvNumber = item.getCvNumber();
ConfigurationVariable cvDmxChannelIdA = from(configVariables, cvNumber);
if (cvDmxChannelIdA.getValue() == null) {
configVariablesToRead.add(cvDmxChannelIdA);
}
ConfigurationVariable cvBrightnessA = from(configVariables, cvNumber + 1);
if (cvBrightnessA.getValue() == null) {
configVariablesToRead.add(cvBrightnessA);
}
if (v2Firmware) {
ConfigurationVariable cvDmxChannelIdB = from(configVariables, cvNumber + 2);
if (cvDmxChannelIdB.getValue() == null) {
configVariablesToRead.add(cvDmxChannelIdB);
}
ConfigurationVariable cvBrightnessB = from(configVariables, cvNumber + 3);
if (cvBrightnessB.getValue() == null) {
configVariablesToRead.add(cvBrightnessB);
}
ConfigurationVariable cvTransitionTime = from(configVariables, cvNumber + 4);
if (cvTransitionTime.getValue() == null) {
configVariablesToRead.add(cvTransitionTime);
}
}
else {
ConfigurationVariable cvTransitionTime = from(configVariables, cvNumber + 2);
if (cvTransitionTime.getValue() == null) {
configVariablesToRead.add(cvTransitionTime);
}
}
}
LOGGER.debug("Prepared CVs: {}", configVariablesToRead);
if (callback != null) {
callback.messageChanged("Read CVs from node: " + configVariablesToRead.size());
}
// read dmx channels and brightness CVs from the node
readCvFromNode(configVariablesToRead);
final Map updatedConfigVariables = node.getConfigVariables();
for (OverlayItem item : items) {
int cvNumber = item.getCvNumber();
Integer dmxChannelValueA = optFrom(updatedConfigVariables, cvNumber).orElse(null);
item.setDmxChannelIdA(dmxChannelValueA);
Integer brightnessValueA = optFrom(updatedConfigVariables, cvNumber + 1).orElse(null);
item.setBrightnessValueA(brightnessValueA);
if (v2Firmware) {
Integer dmxChannelValueB = optFrom(updatedConfigVariables, cvNumber + 2).orElse(null);
item.setDmxChannelIdB(dmxChannelValueB);
Integer brightnessValueB = optFrom(updatedConfigVariables, cvNumber + 3).orElse(null);
item.setBrightnessValueB(brightnessValueB);
Integer transitionTime = optFrom(updatedConfigVariables, cvNumber + 4).orElse(null);
item.setTransitionTime(transitionTime);
}
else {
Integer transitionTime = optFrom(updatedConfigVariables, cvNumber + 2).orElse(null);
item.setTransitionTime(transitionTime);
}
}
}
private static void add(final List configVariablesToWrite, int cvNumber, Integer value) {
configVariablesToWrite.add(ConfigurationVariable.from(cvNumber, ByteUtils.getIntLowByteValue(value)));
}
private static void none() {
}
private static void addIfDifferent(
final Map configVariables,
final List configVariablesToWrite, int cvNumber, Integer value) {
optFrom(configVariables, cvNumber).ifPresent(val -> {
if (!Objects.equals(val, value)) {
add(configVariablesToWrite, cvNumber, value);
}
else {
none();
}
});
}
@Override
public void writeOverlayCvValues(List items, Runnable finishedCallback) {
LOGGER.info("Write CV values of overlayItems: {}", items);
final List configVariablesToWrite = new LinkedList<>();
boolean v2Firmware = node.getNode().getSoftwareVersion().isHigherOrEqualThan(SoftwareVersion.build(2, 0, 0));
final Map configVariables = node.getConfigVariables();
for (OverlayItem item : items) {
int cvNumber = item.getCvNumber();
if (item.getDmxChannelIdA() != null && item.getBrightnessValueA() != null
&& item.getTransitionTime() != null) {
final Integer channelIdA = item.getDmxChannelIdA();
final Integer brightnessA = item.getBrightnessValueA();
final Integer transitionTime = item.getTransitionTime();
addIfDifferent(configVariables, configVariablesToWrite, cvNumber, channelIdA);
addIfDifferent(configVariables, configVariablesToWrite, cvNumber + 1, brightnessA);
if (v2Firmware) {
if (item.getDmxChannelIdB() != null && item.getBrightnessValueB() != null) {
final Integer channelIdB = item.getDmxChannelIdB();
final Integer brightnessB = item.getBrightnessValueB();
addIfDifferent(configVariables, configVariablesToWrite, cvNumber + 2, channelIdB);
addIfDifferent(configVariables, configVariablesToWrite, cvNumber + 3, brightnessB);
}
else {
addIfDifferent(configVariables, configVariablesToWrite, cvNumber + 2, 0);
addIfDifferent(configVariables, configVariablesToWrite, cvNumber + 3, 0);
}
addIfDifferent(configVariables, configVariablesToWrite, cvNumber + 4, transitionTime);
}
else {
addIfDifferent(configVariables, configVariablesToWrite, cvNumber + 2, transitionTime);
}
}
else if (v2Firmware && item.getDmxChannelIdB() != null && item.getBrightnessValueB() != null
&& item.getTransitionTime() != null) {
final Integer channelIdB = item.getDmxChannelIdB();
final Integer brightnessB = item.getBrightnessValueB();
final Integer transitionTime = item.getTransitionTime();
addIfDifferent(configVariables, configVariablesToWrite, cvNumber + 2, channelIdB);
addIfDifferent(configVariables, configVariablesToWrite, cvNumber + 3, brightnessB);
// channel A is 0
addIfDifferent(configVariables, configVariablesToWrite, cvNumber, 0);
addIfDifferent(configVariables, configVariablesToWrite, cvNumber + 1, 0);
addIfDifferent(configVariables, configVariablesToWrite, cvNumber + 4, transitionTime);
}
else {
LOGGER.debug("No new value for item: {}", item);
addIfDifferent(configVariables, configVariablesToWrite, cvNumber, 0);
addIfDifferent(configVariables, configVariablesToWrite, cvNumber + 1, 0);
if (v2Firmware) {
addIfDifferent(configVariables, configVariablesToWrite, cvNumber + 2, 0);
addIfDifferent(configVariables, configVariablesToWrite, cvNumber + 3, 0);
addIfDifferent(configVariables, configVariablesToWrite, cvNumber + 4, 0);
}
else {
addIfDifferent(configVariables, configVariablesToWrite, cvNumber + 2, 0);
}
}
}
if (CollectionUtils.isNotEmpty(configVariablesToWrite))
{
writeCvValues(this.node, configVariablesToWrite);
if (finishedCallback != null) {
finishedCallback.run();
}
}
else {
LOGGER.info("No changed CV variables to write available.");
}
}
@Override
public void setPendingChanges(boolean hasPendingChanges) {
LOGGER.info("Set the pending changes flag: {}", hasPendingChanges);
// set the dirty flag
this.dmxSceneryModel.setHasPendingChanges(hasPendingChanges);
}
}