All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bidib.wizard.api.model.Macro Maven / Gradle / Ivy

The newest version!
package org.bidib.wizard.api.model;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.bidib.wizard.api.model.function.EmptyFunction;
import org.bidib.wizard.api.model.function.Function;
import org.bidib.wizard.api.model.function.InputFunction;
import org.bidib.wizard.api.model.function.PortAware;
import org.bidib.wizard.api.model.listener.MacroListener;
import org.bidib.wizard.api.utils.MacroUtils;
import org.bidib.wizard.model.ports.Port;
import org.bidib.wizard.model.status.BidibStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jgoodies.binding.beans.Model;

public class Macro extends Model implements LabelAware, TooltipAware {
    private static final long serialVersionUID = 1L;

    private static final Logger LOGGER = LoggerFactory.getLogger(Macro.class);

    public static final String PROPERTY_PENDING_CHANGES = "pendingChanges";

    public static final String PROPERTY_LABEL = "label";

    public static final int REPEAT_INFINITE_CYCLES = 0;

    public static final int REPEAT_RANGE_MIN_VALUE = 0;

    public static final int REPEAT_RANGE_MAX_VALUE = 250;

    private static final int DEFAULT_CYCLES = 1;

    private static final int DEFAULT_SLOWDOWN = 1;

    public static final int SLOWDOWN_RANGE_MIN_VALUE = 1;

    public static final int SLOWDOWN_RANGE_MAX_VALUE = 250;

    public static final Macro NONE = new Macro.Builder(-1).build();

    private final Collection listeners = new LinkedList();

    private int cycles = DEFAULT_CYCLES;

    private List> functions = new ArrayList>();

    /**
     * the maximum number of functions that can be stored in this macro
     */
    private int functionSize;

    private int id;

    private String label;

    private int speed = DEFAULT_SLOWDOWN;

    private Set startConditions = new HashSet();

    private boolean containsError;

    private MacroSaveState macroSaveState = MacroSaveState.PENDING_CHANGES;

    private boolean flatPortModel;

    /**
     * This should be only called by xml decoder ...
     */
    public Macro() {
    }

    /**
     * Create a new instance of Macro.
     * 
     * @param functionSize
     *            the maximum number of functions that can be stored in this macro.
     */
    public Macro(int functionSize) {
        this.functionSize = functionSize;
    }

    private Macro(Builder builder) {
        setId(builder.id);
        setLabel(builder.label);
    }

    public void addMacroListener(MacroListener l) {
        listeners.add(l);
    }

    public void removeMacroListener(MacroListener l) {
        listeners.remove(l);
    }

    /**
     * Initialize the macro with default values.
     */
    public void initialize() {
        setCycles(DEFAULT_CYCLES);
        setSpeed(DEFAULT_SLOWDOWN);
        startConditions.clear();
        clearFunctions();
        setContainsError(false);
    }

    public boolean isValid() {
        for (Function function : functions) {
            if (function instanceof EmptyFunction) {
                LOGGER.warn("Found EmptyFunction in functions list! This is not allowed!");
                return false;
            }
            else if (function instanceof PortAware) {
                PortAware> portAction = (PortAware>) function;
                if (portAction.getPort() == null || portAction.getPort().getId() < 0) {
                    LOGGER
                        .warn(
                            "Found illegal portnumber in PortFunction in functions list! This is not allowed! Current portAction: {}",
                            portAction);
                    return false;
                }
            }
            else if (function instanceof InputFunction) {
                InputFunction portAction = (InputFunction) function;
                if (portAction.getInput() == null || portAction.getInput().getId() < 0) {
                    LOGGER
                        .warn(
                            "Found illegal portnumber in InputFunction in functions list! This is not allowed! Current portAction: {}",
                            portAction);
                    return false;
                }
            }
        }
        return true;
    }

    // equals method is overwritten, so we have to find the correct value
    // manually
    public int getFunctionIndex(Function function) {
        int result = -1;

        for (int index = 0; index < functions.size(); index++) {
            if (functions.get(index) == function) {
                result = index;
                break;
            }
        }
        return result;
    }

    /**
     * Add a function at the specified index if the index is lower than the maximum number of functions that can be
     * stored in this macro.
     * 
     * @param index
     *            the index to set the function at
     * @param function
     *            the new function
     * @return function successfully added
     */
    private boolean addFunction(int index, Function function) {
        boolean result = false;

        if (functions.size() < functionSize) {
            this.functions.add(index, function);
            result = true;
        }

        setMacroSaveState(MacroSaveState.PENDING_CHANGES);

        return result;
    }

    public void addFunctionsAfter(int row, Function[] functions) {
        List> functionsAdded = new LinkedList>();

        if (functions != null) {
            for (int index = 0; index < functions.length; index++) {
                if (addFunction(row + index + 1, functions[index])) {
                    functionsAdded.add(functions[index]);
                }
            }
        }
        else if (addFunction(row + 1, null)) {
            functionsAdded.add(null);
        }
        if (functionsAdded.size() > 0) {
            fireFunctionsAdded(row + 1, functionsAdded.toArray(new Function[0]));
        }
    }

    public void addFunctionsBefore(int row, Function[] functions) {
        List> functionsAdded = new LinkedList>();

        if (functions != null) {
            for (int index = 0; index < functions.length; index++) {
                if (addFunction(row + index, functions[index])) {
                    functionsAdded.add(functions[index]);
                }
            }
        }
        else if (addFunction(row, null)) {
            functionsAdded.add(null);
        }
        if (functionsAdded.size() > 0) {
            fireFunctionsAdded(row, functionsAdded.toArray(new Function[0]));
        }
    }

    public void addFunctionsInvertedAfter(int row, Function[] functions) {
        List> functionsAdded = new LinkedList>();

        if (ArrayUtils.isNotEmpty(functions)) {

            for (int index = 0; index < functions.length; index++) {
                // invert the action and add the function
                Function currentFunction = functions[index];
                Function invertedFunction = MacroUtils.getInvertedFunction(currentFunction);
                if (invertedFunction == null) {
                    invertedFunction = currentFunction;
                }
                if (addFunction(row + index + 1, invertedFunction)) {
                    functionsAdded.add(invertedFunction);
                }
            }
        }
        else if (addFunction(row + 1, null)) {
            functionsAdded.add(null);
        }
        if (functionsAdded.size() > 0) {
            fireFunctionsAdded(row + 1, functionsAdded.toArray(new Function[0]));
        }
    }

    public void clearFunctions() {
        functions.clear();
        fireFunctionsRemoved();
    }

    public Function getFunction(int index) {
        Function result = null;

        if (index >= 0 && index < functions.size()) {
            result = functions.get(index);
        }
        return result;
    }

    /**
     * @return the number of macro steps
     */
    public int getFunctionCount() {
        if (CollectionUtils.isNotEmpty(functions)) {
            return functions.size();
        }
        return 0;
    }

    public List> getFunctions() {
        return Collections.unmodifiableList(functions);
    }

    public void setFunctions(List> functions) {

        fireFunctionsRemoved();

        if (functions.size() > functionSize) {
            // the maximum number of functions that can be stored (defined by hardware) is exceeded
            LOGGER
                .warn(
                    "The maximum number of functions ({}) that can be stored is exceeded. Storing sublist of provided functions.",
                    functionSize);
            this.functions = new ArrayList>(functions.subList(0, functionSize - 1));
        }
        else {
            // set the new functions
            this.functions = new ArrayList>(functions);
        }

        fireFunctionsAdded(0, this.functions.toArray(new Function[0]));

        setMacroSaveState(MacroSaveState.PENDING_CHANGES);
    }

    public void removeFunction(int index) {
        functions.remove(index);

        setMacroSaveState(MacroSaveState.PENDING_CHANGES);
        fireFunctionRemoved(index);
    }

    public void moveFunction(int fromIndex, int toIndex) {
        LOGGER.info("Move function from index: {} to index: {}", fromIndex, toIndex);

        Function fromFunction = null;
        int targetIndex = toIndex;
        if (targetIndex > (functions.size() - 1)) {
            targetIndex = functions.size() - 1;
            LOGGER.info("Move function to end targetIndex: {}", targetIndex);

            fromFunction = functions.remove(fromIndex);
            LOGGER
                .info("Removed from fromIndex: {}, fromFunction: {}", fromIndex, Function.getDebugString(fromFunction));

            functions.add(targetIndex, fromFunction);
        }
        else if (targetIndex > fromIndex) {
            targetIndex = toIndex - 1;
            LOGGER.info("i. Move function targetIndex: {}, fromIndex: {}", targetIndex, fromIndex);

            fromFunction = functions.remove(fromIndex);
            LOGGER
                .info("i. Removed from fromIndex: {}, fromFunction: {}", fromIndex,
                    Function.getDebugString(fromFunction));
            functions.add(targetIndex, fromFunction);
        }
        else { // targetIndex <= fromIndex
            LOGGER.info("ii. Move function targetIndex: {}, fromIndex: {}", targetIndex, fromIndex);

            fromFunction = functions.remove(fromIndex);
            LOGGER
                .info("ii. Removed from fromIndex: {}, fromFunction: {}", fromIndex,
                    Function.getDebugString(fromFunction));
            functions.add(targetIndex, fromFunction);
        }

        setMacroSaveState(MacroSaveState.PENDING_CHANGES);
        fireFunctionMoved(fromIndex, targetIndex, fromFunction);
    }

    public void replaceFunction(int index, Function function) {
        functions.set(index, function);
        setMacroSaveState(MacroSaveState.PENDING_CHANGES);
    }

    /**
     * @return the maximum number of functions that can be stored in this macro
     */
    public int getFunctionSize() {
        return functionSize;
    }

    /**
     * @param functionSize
     *            the maximum number of functions that can be stored in this macro
     */
    public void setFunctionSize(int functionSize) {
        this.functionSize = functionSize;
        // limit the functions to new max size
        if (functions.size() > functionSize) {
            for (int index = functions.size() - 1; index >= functionSize; index--) {
                functions.remove(index);
            }
        }
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Override
    public String getLabel() {
        return label;
    }

    @Override
    public void setLabel(String label) {
        String oldValue = this.label;
        this.label = label;

        firePropertyChange(PROPERTY_LABEL, oldValue, label);
    }

    /**
     * @return the macro slowdown value
     */
    public int getSpeed() {
        return speed;
    }

    /**
     * Set the macro slowdown value.
     * 
     * @param speed
     *            the macro slowdown value
     */
    public void setSpeed(int speed) {
        LOGGER.info("Set the macro slowdown value: {}", speed);

        if (speed > SLOWDOWN_RANGE_MAX_VALUE) {
            // max speed value is 250
            LOGGER.warn("Adjust max value for speed to {}, original value: {}", SLOWDOWN_RANGE_MAX_VALUE, speed);
            speed = SLOWDOWN_RANGE_MAX_VALUE;
        }
        else if (speed < SLOWDOWN_RANGE_MIN_VALUE) {
            // min speed value is 1
            LOGGER.warn("Adjust min value for speed to {}, original value: {}", SLOWDOWN_RANGE_MIN_VALUE, speed);
            speed = SLOWDOWN_RANGE_MIN_VALUE;
        }

        this.speed = speed;

        fireSpeedChanged();
    }

    /**
     * returns the number of cycles for this macro. This is the repeat count before the macro has finished. Be aware
     * that a value of '0' means endless execution of the macro.
     * 
     * @return the number of cycles
     */
    public int getCycles() {
        return cycles;
    }

    public void setCycles(int cycles) {
        if (cycles > REPEAT_RANGE_MAX_VALUE) {
            LOGGER.warn("Adjust max value for cycles to {}, original value: {}", REPEAT_RANGE_MAX_VALUE, cycles);
            cycles = REPEAT_RANGE_MAX_VALUE;
        }
        else if (cycles < REPEAT_RANGE_MIN_VALUE) {
            LOGGER.warn("Adjust min value for cycles to {}, original value: {}", REPEAT_RANGE_MIN_VALUE, cycles);
            cycles = REPEAT_RANGE_MIN_VALUE;
        }

        this.cycles = cycles;

        fireCyclesChanged();
    }

    /**
     * @return the flatPortModel
     */
    public boolean isFlatPortModel() {
        return flatPortModel;
    }

    /**
     * @param flatPortModel
     *            the flatPortModel to set
     */
    public void setFlatPortModel(boolean flatPortModel) {
        this.flatPortModel = flatPortModel;
    }

    public Collection getStartConditions() {
        return Collections.unmodifiableCollection(startConditions);
    }

    public void clearStartConditions() {
        startConditions.clear();

        // fire the start conditions have changed
        fireStartConditionChanged();
    }

    public void addStartCondition(StartCondition startCondition) {
        LOGGER.debug("Add new start condition: {}", startCondition);
        startConditions.add(startCondition);

        // fire the start conditions have changed
        fireStartConditionChanged();
    }

    public void removeStartCondition(StartCondition startCondition) {
        startConditions.remove(startCondition);

        // fire the start conditions have changed
        fireStartConditionChanged();
    }

    public void setStartConditions(Collection startConditions) {

        this.startConditions = new HashSet();
        if (startConditions != null) {
            this.startConditions.addAll(startConditions);
        }

        // fire the start conditions have changed
        fireStartConditionChanged();
    }

    /**
     * @return the containsError
     */
    public boolean isContainsError() {
        return containsError;
    }

    /**
     * @param containsError
     *            the containsError to set
     */
    public void setContainsError(boolean containsError) {
        this.containsError = containsError;
    }

    private void fireFunctionsAdded(int stepNumber, final Function[] functions) {
        for (MacroListener l : listeners) {
            l.functionsAdded(id, stepNumber, functions);
        }
    }

    private void fireFunctionRemoved(int stepNumber) {
        for (MacroListener l : listeners) {
            l.functionRemoved(id, stepNumber);
        }
    }

    private void fireFunctionMoved(int fromIndex, int toIndex, Function fromFunction) {
        for (MacroListener l : listeners) {
            l.functionMoved(id, fromIndex, toIndex, fromFunction);
        }
    }

    private void fireFunctionsRemoved() {
        for (MacroListener l : listeners) {
            l.functionsRemoved(id);
        }
    }

    private void fireStartConditionChanged() {
        LOGGER.debug("fireStartConditionChanged.");
        for (MacroListener l : listeners) {
            l.startConditionChanged();
        }
    }

    private void fireSpeedChanged() {
        LOGGER.debug("fireSpeedChanged.");
        for (MacroListener l : listeners) {
            l.slowdownFactorChanged();
        }
    }

    private void fireCyclesChanged() {
        LOGGER.debug("fireCyclesChanged.");
        for (MacroListener l : listeners) {
            l.cyclesChanged();
        }
    }

    /**
     * @return the pendingChanges
     */
    public MacroSaveState getMacroSaveState() {
        return macroSaveState;
    }

    /**
     * @param macroSaveState
     *            the pendingChanges to set
     */
    public void setMacroSaveState(MacroSaveState macroSaveState) {
        MacroSaveState oldValue = this.macroSaveState;
        this.macroSaveState = macroSaveState;

        LOGGER.info("Changed the macro save state, new: {}, old: {}", this.macroSaveState, oldValue);

        firePropertyChange(PROPERTY_PENDING_CHANGES, oldValue, this.macroSaveState);
    }

    @Override
    public String getTooltipKey() {

        String tootipKey = null;
        switch (macroSaveState) {
            case NOT_LOADED_FROM_NODE:
                tootipKey = "not-loaded-from-node";
                break;
            case PENDING_CHANGES:
                tootipKey = "pending-changes";
                break;
            case SAVED_ON_NODE:
                tootipKey = "saved-on-node";
                break;
            case PERMANENTLY_STORED_ON_NODE:
                tootipKey = "permanently-stored-on-node";
                break;
            default:
                LOGGER.error("Unknown macro save state detected: {}", macroSaveState);
                break;
        }
        return tootipKey;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Macro) {
            return ((Macro) obj).getId() == getId();
        }
        return false;
    }

    @Override
    public int hashCode() {
        return getId();
    }

    public String getDebugString() {
        return getClass().getSimpleName() + "[cycles=" + cycles + ",functions=" + functions + ",functionSize="
            + functionSize + ",id=" + id + ",label=" + label + ",speed=" + speed + ",startConditions=" + startConditions
            + ",containsError=" + containsError + ",flatPortModel=" + flatPortModel + "]";
    }

    @Override
    public String toString() {
        String result = null;

        if (StringUtils.isNotBlank(label)) {
            result = label;
        }
        // else {
        // result = Resources.getString(getClass(), "label") + "_" + id;
        // }
        return result;
    }

    public static class Builder {
        private final int id;

        private String label;

        public Builder(int id) {
            this.id = id;
        }

        public Builder setLabel(String label) {
            this.label = label;
            return this;
        }

        public Macro build() {
            return new Macro(this);
        }
    }

    public static Macro cloneMacro(final Macro macro) {

        // create a clone of the macro
        final Macro macroClone = new Macro(macro.getFunctionSize());
        macroClone.setId(macro.getId());
        macroClone.setLabel(macro.getLabel());

        macroClone.setCycles(macro.getCycles());
        macroClone.setSpeed(macro.getSpeed());
        macroClone.setStartConditions(macro.getStartConditions());

        macroClone.setFunctions(macro.getFunctions());
        macroClone.setMacroSaveState(macro.getMacroSaveState());
        macroClone.setFlatPortModel(macro.isFlatPortModel());

        return macroClone;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy