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

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

There is a newer version: 2.0.29
Show newest version
package org.bidib.wizard.api.model;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.bidib.jbidibc.messages.AccessoryState;
import org.bidib.jbidibc.messages.AccessoryStateOptions;
import org.bidib.jbidibc.messages.BidibLibrary;
import org.bidib.jbidibc.messages.enums.AccessoryExecutionState;
import org.bidib.jbidibc.messages.enums.TimeBaseUnitEnum;
import org.bidib.jbidibc.messages.utils.AccessoryStateUtils;
import org.bidib.jbidibc.messages.utils.ByteUtils;
import org.bidib.wizard.api.model.listener.AccessoryListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jgoodies.binding.beans.Model;

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

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

    public static final String PROPERTY_PENDING_CHANGES = "pendingChanges";

    public static final String PROPERTY_TOTAL_ASPECTS_OF_ACCESSORY = "totalAspectsOfAccessory";

    public static final String PROPERTY_STARTUP_STATE = "startupState";

    public static final String PROPERTY_ACCESSORY_STATE = "accessoryState";

    public static final String PROPERTY_ACCESSORY_EXECUTION_STATE = "accessoryExecutionState";

    public static final String PROPERTY_PARA_SWITCH_TIME = "switchTime";

    public static final String PROPERTY_PARA_TIME_BASE_UNIT = "timeBaseUnit";

    public static final String PROPERTY_PARA_HAS_EMERGENCY_STOP = "hasEmergencyStop";

    public static final String PROPERTY_PARA_USES_STRINGS = "usesStrings";

    public static final String PROPERTY_PARA_OPERATION_MODE_ACCESSORY_NUMBER = "operationModeAccessoryNumber";

    public static final String PROPERTY_LABEL = "label";

    public static final String PROPERTYNAME_ERROR = "error";

    public static final Accessory NONE = new Accessory.Builder(null)./* setLabel(""). */build();

    private transient final Collection listeners = new LinkedList();

    private Integer id;

    private String label;

    private List aspects = new ArrayList<>();

    private int maxMacroMappedAspects;

    private transient AccessoryExecutionState accessoryExecutionState;

    private transient AccessoryState accessoryState;

    private transient AccessoryStateOptions accessoryStateOptions;

    private transient AccessoryErrorState accessoryErrorState;

    private static enum AccessoryConfigState {
        pending, initialized;
    }

    private static enum AccessoryErrorState {
        valid, error;
    }

    /**
     * The accessory startup state
     */
    private Integer startupState;

    /**
     * The accessory switch time
     */
    private Integer switchTime;

    /**
     * The accessory switch time bas unit
     */
    private TimeBaseUnitEnum timeBaseUnit = TimeBaseUnitEnum.UNIT_100MS;

    /**
     * The accessory supports emergency stop para
     */
    private Boolean hasEmergencyStop;

    /**
     * The accessory supports uses strings para
     */
    private Boolean usesStrings = Boolean.FALSE;

    /**
     * The accessory number of the operation mode accessory
     */
    private Integer operationModeAccessoryNumber;

    private int totalAspects;

    private transient AccessoryConfigState accessoryConfigState = AccessoryConfigState.pending;

    private transient AccessorySaveState accessorySaveState = AccessorySaveState.PENDING_CHANGES;

    private transient boolean editable = true;

    public Accessory() {

    }

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

    public void addAccessoryListener(AccessoryListener l) {
        listeners.add(l);
    }

    public void removeAccessoryListener(AccessoryListener l) {
        listeners.remove(l);
    }

    public Integer getId() {
        return this.id;
    }

    public void setId(Integer 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);
    }

    public void addAspect(MacroRef macro) {
        addAspect(aspects.size(), macro);
    }

    /**
     * Add aspect at provided index.
     * 
     * @param index
     *            the index
     * @param macroRef
     *            the macro mapped aspect
     * @return {@code true} add was successful, {@code false} add failed
     */
    private boolean addAspect(int index, MacroRef macroRef) {
        boolean result = false;

        if (macroRef != null && index >= 0 && aspects.size() < maxMacroMappedAspects) {

            aspects.add(index, macroRef);

            totalAspects = aspects.size();

            fireMacrosChanged();
            result = true;
        }
        else {
            LOGGER
                .warn("This macro is not added because the maximum macro size is reached, index: {}, macroRef: {}",
                    index, macroRef);
        }
        return result;
    }

    public void addAspectAfter(int index, MacroRef macro) {
        addAspect(index + 1, macro);
    }

    public void addAspectsAfter(int index, MacroRef[] macros) {
        LOGGER.debug("Add aspects after, index: {}, macros: {}", index, macros);
        if (macros != null) {
            for (MacroRef macro : macros) {
                index++;
                addAspect(index, macro);
            }
        }
    }

    public void addAspectBefore(int index, MacroRef macro) {
        addAspect(index, macro);
    }

    public void clearAspects() {
        aspects.clear();
        totalAspects = aspects.size();
        fireMacrosChanged();
    }

    public int getAspectCount() {
        return aspects.size();
    }

    /**
     * @return the aspects of the accessory
     */
    public List getAspects() {
        return Collections.unmodifiableList(aspects);
    }

    /**
     * Set the aspects.
     * 
     * @param aspects
     *            the aspects
     */
    public void setAspects(List aspects) {

        this.aspects.clear();

        this.aspects.addAll(aspects);
    }

    /**
     * Remove aspect at index.
     * 
     * @param index
     *            the index
     * @return the removed apect
     */
    public MacroRef removeAspect(int index) {
        MacroRef aspect = aspects.remove(index);
        totalAspects = aspects.size();

        fireMacrosChanged();

        return aspect;
    }

    /**
     * Set the aspect at the provided index.
     * 
     * @param index
     *            the index
     * @param macroId
     *            the mapped macro id
     */
    public void replaceAspectMacro(int index, int macroId) {

        MacroRef aspect = aspects.get(index);
        if (aspect != null) {
            aspect.setId(macroId);
            LOGGER.info("Replaced macro on aspect: {}, index: {}", aspect, index);
        }
        else {
            aspect = new MacroRef(macroId);
            LOGGER.info("Add new macro-mapped aspect: {}, index: {}", aspect, index);
            aspects.set(index, aspect);
        }

        fireMacrosChanged();
    }

    /**
     * @return the maximum number of macro-mapped accessories available for this accessory.
     */
    public int getMaximumMacroMappedAspects() {
        return maxMacroMappedAspects;
    }

    /**
     * Set the maximum number of aspects == macros that can be stored in an accessory.
     * 
     * @param maxMacroMappedAspects
     *            the maximum number of macro-mapped aspects for this accessory
     */
    public void setMaximumMacroMappedAspects(int maxMacroMappedAspects) {
        this.maxMacroMappedAspects = maxMacroMappedAspects;
        // if (this.aspects.size() > maxMacroMappedAspects) {
        // this.aspects.setSize(maxMacroMappedAspects);
        // }
    }

    /**
     * @return the accessory supports macro-mapped aspects.
     */
    public boolean isMacroMapped() {
        return (maxMacroMappedAspects > 0);
    }

    /**
     * @return the startupState
     */
    public Integer getStartupState() {
        return startupState;
    }

    /**
     * @param startupState
     *            the startupState to set
     */
    public void setStartupState(Integer startupState) {
        Integer oldValue = this.startupState;

        this.startupState = startupState;

        firePropertyChange(PROPERTY_STARTUP_STATE, oldValue, startupState);

        if (!Objects.equals(oldValue, startupState)) {
            firePendingChanges();
        }
    }

    /**
     * @return the switchTime
     */
    public Integer getSwitchTime() {
        return switchTime;
    }

    /**
     * @param switchTime
     *            the switchTime to set
     */
    public void setSwitchTime(Integer switchTime) {
        Integer oldValue = this.switchTime;
        this.switchTime = switchTime;

        firePropertyChange(PROPERTY_PARA_SWITCH_TIME, oldValue, switchTime);

        if (!Objects.equals(oldValue, switchTime)) {
            firePendingChanges();
        }
    }

    /**
     * @return the timeBaseUnit
     */
    public TimeBaseUnitEnum getTimeBaseUnit() {
        return timeBaseUnit;
    }

    /**
     * @param timeBaseUnit
     *            the timeBaseUnit to set
     */
    public void setTimeBaseUnit(TimeBaseUnitEnum timeBaseUnit) {
        TimeBaseUnitEnum oldValue = this.timeBaseUnit;
        this.timeBaseUnit = timeBaseUnit;

        firePropertyChange(PROPERTY_PARA_TIME_BASE_UNIT, oldValue, timeBaseUnit);

        if (!Objects.equals(oldValue, timeBaseUnit)) {
            firePendingChanges();
        }
    }

    private void firePendingChanges() {

        setAccessorySaveState(AccessorySaveState.PENDING_CHANGES);
    }

    /**
     * @return the hasEmergencyStop
     */
    public Boolean isHasEmergencyStop() {
        return hasEmergencyStop;
    }

    /**
     * @param hasEmergencyStop
     *            the hasEmergencyStop to set
     */
    public void setHasEmergencyStop(Boolean hasEmergencyStop) {
        Boolean oldValue = this.hasEmergencyStop;

        this.hasEmergencyStop = hasEmergencyStop;

        firePropertyChange(PROPERTY_PARA_HAS_EMERGENCY_STOP, oldValue, hasEmergencyStop);
    }

    /**
     * @return the usesStrings
     */
    public Boolean isUsesStrings() {
        return usesStrings;
    }

    /**
     * @param usesStrings
     *            the usesStrings to set
     */
    public void setUsesStrings(Boolean usesStrings) {
        Boolean oldValue = this.usesStrings;
        this.usesStrings = usesStrings;

        firePropertyChange(PROPERTY_PARA_USES_STRINGS, oldValue, usesStrings);
    }

    /**
     * @return the operationModeAccessoryNumber
     */
    public Integer getOperationModeAccessoryNumber() {
        return operationModeAccessoryNumber;
    }

    /**
     * @param operationModeAccessoryNumber
     *            the operationModeAccessoryNumber to set
     */
    public void setOperationModeAccessoryNumber(Integer operationModeAccessoryNumber) {
        Integer oldValue = this.operationModeAccessoryNumber;

        this.operationModeAccessoryNumber = operationModeAccessoryNumber;

        firePropertyChange(PROPERTY_PARA_OPERATION_MODE_ACCESSORY_NUMBER, oldValue, operationModeAccessoryNumber);
    }

    /**
     * @return the totalAspects
     */
    public int getTotalAspects() {
        return totalAspects;
    }

    /**
     * Set the total number of aspects for this accessory. The total number is only relevant for accessories that do not
     * support macro mapped aspects. In case of macro-mapped aspects the maximum number of aspects is relevant.
     * 
     * @param totalAspects
     *            the totalAspects to set
     */
    public void setTotalAspects(int totalAspects) {
        LOGGER.info("Set total number of aspects: {}", totalAspects);

        if (this.totalAspects != totalAspects) {
            this.totalAspects = totalAspects;

            if (!isMacroMapped()) {
                // signal the total number of aspects has changed
                fireTotalAspectsChanged(id);
            }
        }
    }

    public AccessoryExecutionState getAccessoryExecutionState() {
        return accessoryExecutionState;
    }

    public void setAccessoryExecutionState(final AccessoryExecutionState accessoryExecutionState, Integer aspect) {
        LOGGER.info("Set the new accessoryExecutionState: {}", accessoryExecutionState);
        AccessoryExecutionState oldAccessoryExecutionState = this.accessoryExecutionState;
        this.accessoryExecutionState = accessoryExecutionState;

        firePropertyChange(PROPERTY_ACCESSORY_EXECUTION_STATE, oldAccessoryExecutionState, accessoryExecutionState);

        fireAccessoryStateChanged(aspect);
    }

    public void setAccessoryState(
        final AccessoryState accessoryState, final AccessoryStateOptions accessoryStateOptions) {
        LOGGER.info("Set the new accessoryState: {}, accessoryStateOptions: {}", accessoryState, accessoryStateOptions);

        // update the accessory state
        AccessoryState oldAccessoryState = this.accessoryState;
        this.accessoryState = accessoryState;

        firePropertyChange(PROPERTY_ACCESSORY_STATE, oldAccessoryState, accessoryState);

        this.accessoryStateOptions = accessoryStateOptions;

        if (AccessoryConfigState.pending.equals(accessoryConfigState)) {

            LOGGER.info("The initial AccessoryState is delivered: {}", accessoryState);

            setTotalAspects(ByteUtils.getInt(accessoryState.getTotal()));

            accessoryConfigState = AccessoryConfigState.initialized;
        }
        else if (!isMacroMapped()) {
            LOGGER
                .info(
                    "The accessory is already initialized but not macro mapped. Check if we need to update the total aspect count.");
            setTotalAspects(ByteUtils.getInt(accessoryState.getTotal()));
        }
        else {
            LOGGER.info("The accessory is already initialized, do not update the total aspect count.");
        }

        final org.bidib.jbidibc.messages.logger.Logger logger = new org.bidib.jbidibc.messages.logger.Logger() {

            @Override
            public void warn(String format, Object... arguments) {
                LOGGER.warn(format, arguments);
            }

            @Override
            public void info(String format, Object... arguments) {
                LOGGER.info(format, arguments);
            }

            @Override
            public void debug(String format, Object... arguments) {
                LOGGER.debug(format, arguments);
            }

            @Override
            public void error(String format, Object... arguments) {
                LOGGER.error(format, arguments);
            }
        };

        AccessoryExecutionState accessoryExecutionState =
            AccessoryStateUtils
                .getExecutionState(accessoryState.getActiveAspect(), accessoryState.getExecute(), logger);
        LOGGER
            .debug("Set the new accessoryState: {}, accessoryExecutionState: {}", accessoryState,
                accessoryExecutionState);

        // prevent send state changed twice
        // LOGGER.info("Set the new accessoryExecutionState: {}", accessoryExecutionState);
        // this.accessoryExecutionState = accessoryExecutionState;
        setAccessoryExecutionState(accessoryExecutionState, accessoryState.getActiveAspect());
    }

    public AccessoryState getAccessoryState() {
        return accessoryState;
    }

    public AccessoryStateOptions getAccessoryStateOptions() {
        return accessoryStateOptions;
    }

    public boolean hasExecutionStateError() {
        return AccessoryExecutionState.ERROR == accessoryExecutionState;
    }

    public boolean hasError() {
        return AccessoryErrorState.error.equals(accessoryErrorState);
    }

    public void setError(boolean error) {
        boolean oldValue = hasError();

        if (error) {
            accessoryErrorState = AccessoryErrorState.error;
        }
        else {
            accessoryErrorState = AccessoryErrorState.valid;
        }
        firePropertyChange(PROPERTYNAME_ERROR, oldValue, hasError());
    }

    private void fireMacrosChanged() {
        // setPendingChanges(true);
        setAccessorySaveState(AccessorySaveState.PENDING_CHANGES);

        for (AccessoryListener l : listeners) {
            l.macrosChanged();
        }
    }

    private void fireAccessoryStateChanged(final Integer aspect) {
        for (AccessoryListener l : listeners) {
            l.accessoryStateChanged(getId(), aspect);
        }
    }

    private void fireTotalAspectsChanged(final Integer accessoryId) {

        LOGGER.info("Fire total number of aspects changed of accessory id: {}", accessoryId);
        firePropertyChange(PROPERTY_TOTAL_ASPECTS_OF_ACCESSORY, null, accessoryId);
    }

    /**
     * @return the accessorySaveState
     */
    public AccessorySaveState getAccessorySaveState() {
        return accessorySaveState;
    }

    /**
     * @param accessorySaveState
     *            the accessorySaveState to set
     */
    public void setAccessorySaveState(AccessorySaveState accessorySaveState) {
        AccessorySaveState oldValue = this.accessorySaveState;
        this.accessorySaveState = accessorySaveState;

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

    /**
     * @return the editable
     */
    public boolean isEditable() {
        return editable;
    }

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

    @Override
    public String getTooltipKey() {

        String tootip = null;
        switch (accessorySaveState) {
            case PENDING_CHANGES:
                tootip = "pending-changes";
                break;
            case PERMANENTLY_STORED_ON_NODE:
                tootip = "permanently-stored-on-node";
                break;
            default:
                LOGGER.error("Unknown accessory save state detected: {}", accessorySaveState);
                break;
        }
        return tootip;
    }

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

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

    public String getDebugString() {
        StringBuilder sb = new StringBuilder(getClass().getSimpleName());
        sb
            .append("[id=").append(id).append(",label=").append(label).append(",macros=").append(aspects)
            .append(",macroSize=").append(maxMacroMappedAspects).append(",startup=").append(startupState).append("]");
        return sb.toString();
    }

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

        if (StringUtils.isNotBlank(label)) {
            result = label;
        }
        return result;
    }

    public static class Builder {
        private final Integer id;

        private String label;

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

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

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

    public static Accessory cloneAccessoryData(final Accessory accessory) {

        // create a clone of the accessory
        final Accessory accessoryClone = new Accessory();
        accessoryClone.setId(accessory.getId());
        accessoryClone.setLabel(accessory.getLabel());

        accessoryClone.setMaximumMacroMappedAspects(accessory.getMaximumMacroMappedAspects());
        if (CollectionUtils.isNotEmpty(accessory.getAspects())) {
            MacroRef[] aspects = accessory.getAspects().toArray(new MacroRef[0]);
            LOGGER.debug("Current aspects: {}", new Object[] { aspects });

            accessoryClone.setAspects(Arrays.asList(aspects));
        }

        accessoryClone.setStartupState(accessory.getStartupState());
        accessoryClone.setSwitchTime(accessory.getSwitchTime());
        accessoryClone.setTimeBaseUnit(accessory.getTimeBaseUnit());

        return accessoryClone;
    }

    /**
     * Initialize the accessory with default values.
     */
    public void initialize() {
        clearAspects();
        setStartupState(BidibLibrary.ASPECT_PARAM_UNCHANGED);
        setError(false);
    }

    /**
     * Get the accessory param.
     * 
     * @param paramNumber
     *            the param number
     */
    public int[] getParam(int paramNumber) {
        int[] paramValue = null;
        try {
            switch (paramNumber) {
                case BidibLibrary.BIDIB_ACCESSORY_PARA_USES_STRINGS:
                    if (isUsesStrings() != null) {
                        paramValue = new int[] { isUsesStrings() != null && isUsesStrings().booleanValue() ? 1 : 0 };
                    }
                    else {
                        paramValue = new int[0];
                    }
                    break;
                case BidibLibrary.BIDIB_ACCESSORY_PARA_HAS_ESTOP:
                    if (isHasEmergencyStop() != null) {
                        paramValue =
                            new int[] { isHasEmergencyStop() != null && isHasEmergencyStop().booleanValue() ? 1 : 0 };
                    }
                    else {
                        paramValue = new int[0];
                    }
                    break;
                case BidibLibrary.BIDIB_ACCESSORY_PARA_OPMODE:
                    if (getOperationModeAccessoryNumber() != null) {
                        paramValue = new int[] { getOperationModeAccessoryNumber().intValue() };
                    }
                    else {
                        paramValue = new int[0];
                    }
                    break;
                case BidibLibrary.BIDIB_ACCESSORY_PARA_STARTUP:
                    if (getStartupState() != null) {
                        paramValue = new int[] { getStartupState() };
                    }
                    else {
                        paramValue = new int[0];
                    }
                    break;
                case BidibLibrary.BIDIB_ACCESSORY_SWITCH_TIME:
                    if (getTimeBaseUnit() != null && getSwitchTime() != null) {
                        int timeBaseUnit = ByteUtils.getInt(getTimeBaseUnit().getType());
                        paramValue = new int[] { getSwitchTime() | (timeBaseUnit << 7) };
                        LOGGER.info("Current accessory switch time: {}", paramValue[0]);
                    }
                    else {
                        paramValue = new int[0];
                    }
                    break;
                case BidibLibrary.BIDIB_ACCESSORY_PARA_MACROMAP:
                    if (aspects != null && aspects.size() > 0) {
                        List assignedAspects =
                            aspects.stream().filter(a -> a != null && a.getId() != null).collect(Collectors.toList());
                        if (CollectionUtils.isNotEmpty(assignedAspects)) {
                            paramValue = new int[assignedAspects.size()];
                            int index = 0;
                            for (MacroRef macroRef : assignedAspects) {
                                paramValue[index] = macroRef.getId();
                                index++;
                            }
                        }
                    }
                    else {
                        paramValue = new int[0];
                    }
                    break;
                default:
                    LOGGER.warn("Unknown paramNumber: {}", paramNumber);
                    paramValue = new int[0];
                    break;
            }
        }
        catch (Exception ex) {
            LOGGER.warn("Get the accessory param failed, paramNumber: {}", paramNumber, ex);
        }

        return paramValue;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy