/*
 * Copyright (c) 2014 Paul Scherrer Institute. All rights reserved.
 */
import ch.psi.pshell.device.*;
import ch.psi.pshell.epics.*;
import ch.psi.utils.BitMask;
import java.io.IOException;

/**
 */
public class PowerSupply extends DeviceBase implements  BitMask {

    public final ChannelDouble moduleVoltageRamp, moduleCurrentRamp, 
            
                               moduleTemperature, modulePower5, modulePower12, modulePower12N, moduleVoltageRampRb, 
                               moduleCurrentRampRb, moduleVoltageMax, moduleCurrentMax,channelActualVoltage,channelActualCurrent;
    
    public final ChannelInteger moduleRestartDelay, moduleAdcSamples, moduleDigFilter, moduleIlkCommand,
                                moduleEvMaskSet, moduleEvMaskClr, moduleChMaskSet, moduleChMaskClr, 
                                
                                moduleFirmware, moduleSerial, modulePlacedChannels, moduleNoChannels,moduleRestartDelayRb, 
                                moduleCommandRb, moduleEvMaskRb, moduleChMaskRb, moduleIlkCommandRb,moduleStatus, 
                                moduleIlkStatus, moduleEvStatus, moduleChStatus, moduleAdcSamplesRb, moduleDigFilterRb;
    public final ChannelString moduleCommand;
    
    public final ChannelDouble channelVoltageNominalSetpoint, channelCurrentNominalSetpoint, channelDesiredVoltageSetpoint, 
                               channelVoltageIlkMax, channelVoltageIlkMin, channelDesiredCurrentSetpoint, 
                               channelCurrentIlkMax, channelCurrentIlkMin,
                              
                               channelVoltageMax, channelCurrentMax, channelVoltageNominalReadback,channelCurrentNominalReadback,
                               channelDesiredVoltageReadback,channelDesiredCurrentReadback,channelVoltageIlkMaxReadback,
                               channelVoltageIlkMinReadback, channelCurrentIlkMaxReadback,channelCurrentIlkMinReadback;
    
    public final ChannelInteger channelEvMaskSet, channelEvMaskClr,
            
                                channelCommandReadback, channelEventMaskReadback, channelStatus, channelEvents;
    public final ChannelString channelCommand;
    
    
    public final String prefix;
    

    public PowerSupply(String name, String prefix) {
        super(name);
        this.prefix=prefix;
        
        moduleVoltageRamp = new ChannelDouble(name + " moduleVoltageRamp", prefix + ":RAMP:2");        
        moduleCurrentRamp = new ChannelDouble(name + " moduleCurrentRamp", prefix + ":IRAMP:2");   
        
        moduleTemperature = new ChannelDouble(name + " moduleTemperature", prefix + ":TMPE:2", RegisterAccessType.Read);
        modulePower5 = new ChannelDouble(name + " modulePower5", prefix + ":VP5:2", RegisterAccessType.Read);
        modulePower12 = new ChannelDouble(name + " modulePower12", prefix + ":VP12:2", RegisterAccessType.Read);
        modulePower12N = new ChannelDouble(name + " modulePower12N", prefix + ":VN12:2", RegisterAccessType.Read);
        moduleVoltageRampRb = new ChannelDouble(name + " moduleVoltageRampRb", prefix + ":RAMPRB:2", RegisterAccessType.Read);
        moduleCurrentRampRb = new ChannelDouble(name + " moduleCurrentRampRb", prefix + ":IRAMPRB:2", RegisterAccessType.Read);
        moduleVoltageMax = new ChannelDouble(name + " moduleVoltageMax", prefix + ":VMAX:2", RegisterAccessType.Read);
        moduleCurrentMax = new ChannelDouble(name + " moduleCurrentMax", prefix + ":IMAX:2", RegisterAccessType.Read);
        channelActualVoltage = new ChannelDouble(name + " channelActualVoltage", prefix + ":IST:2", RegisterAccessType.Read);
        channelActualCurrent = new ChannelDouble(name + " channelActualCurrent", prefix + ":STR:1", RegisterAccessType.Read);      
        
        moduleRestartDelay = new ChannelInteger(name + " moduleRestartDelay", prefix + ":RTAR:2");     
        moduleAdcSamples = new ChannelInteger(name + " moduleAdcSamples", prefix + ":NADC:2"); 
        moduleDigFilter = new ChannelInteger(name + " moduleDigFilter", prefix + ":NDF:2"); 
        moduleIlkCommand = new ChannelInteger(name + " moduleIlkCommand", prefix + ":ICOM:2"); 
        moduleEvMaskSet = new ChannelInteger(name + " moduleEvMaskSet", prefix + ":MMEMSKSET:2"); 
        moduleEvMaskClr = new ChannelInteger(name + " moduleEvMaskClr", prefix + ":MMEMSKCLR:2");         
        moduleChMaskSet = new ChannelInteger(name + " moduleChMaskSet", prefix + ":MCEMSKSET:2"); 
        moduleChMaskClr = new ChannelInteger(name + " moduleChMaskClr", prefix + ":MCEMSKCLR:2"); 
        
        moduleFirmware = new ChannelInteger(name + " moduleFirmware", prefix + ":FWNR:1", RegisterAccessType.Read); 
        moduleSerial = new ChannelInteger(name + " moduleSerial", prefix + ":SRNR:1", RegisterAccessType.Read); 
        modulePlacedChannels = new ChannelInteger(name + " modulePlacedChannels", prefix + ":PLCH:1", RegisterAccessType.Read); 
        moduleNoChannels = new ChannelInteger(name + " moduleNoChannels", prefix + ":NRCH:2", RegisterAccessType.Read); 
        moduleRestartDelayRb = new ChannelInteger(name + " moduleRestartDelayRb", prefix + ":RTARRB:2", RegisterAccessType.Read); 
        moduleCommandRb = new ChannelInteger(name + " moduleCommandRb", prefix + ":COMXRB:1", RegisterAccessType.Read); 
        moduleEvMaskRb = new ChannelInteger(name + " moduleEvMaskRb", prefix + ":MMEMSKRB:1", RegisterAccessType.Read); 
        moduleChMaskRb = new ChannelInteger(name + " moduleChMaskRb", prefix + ":MCEMSKRB:1", RegisterAccessType.Read); 
        moduleIlkCommandRb = new ChannelInteger(name + " moduleIlkCommandRb", prefix + ":ICOMRB:1", RegisterAccessType.Read); 
        moduleStatus = new ChannelInteger(name + " moduleStatus", prefix + ":STAX:1", RegisterAccessType.Read); 
        moduleIlkStatus = new ChannelInteger(name + " moduleIlkStatus", prefix + ":STA1:1", RegisterAccessType.Read); 
        moduleEvStatus = new ChannelInteger(name + " moduleEvStatus", prefix + ":MMESTA:1", RegisterAccessType.Read); 
        moduleChStatus = new ChannelInteger(name + " moduleChStatus", prefix + ":MCESTA:1", RegisterAccessType.Read); 
        moduleAdcSamplesRb = new ChannelInteger(name + " moduleAdcSamplesRb", prefix + ":NADCRB:1", RegisterAccessType.Read); 
        moduleDigFilterRb= new ChannelInteger(name + " moduleDigFilterRb", prefix + ":NDFRB:2", RegisterAccessType.Read); 
        
        moduleCommand = new ChannelString(name + " moduleCommand", prefix + ":COMX:2");                 
        
        channelVoltageNominalSetpoint = new ChannelDouble(name + " channelVoltageNominalSetpoint", prefix + ":VNOM:2");        
        channelCurrentNominalSetpoint = new ChannelDouble(name + " channelCurrentNominalSetpoint", prefix + ":INOM:2");        
        channelDesiredVoltageSetpoint = new ChannelDouble(name + " channelDesiredVoltageSetpoint", prefix + ":SOL:2");        
        channelVoltageIlkMax = new ChannelDouble(name + " channelVoltageIlkMax", prefix + ":VIMX:2");        
        channelVoltageIlkMin = new ChannelDouble(name + " channelVoltageIlkMin", prefix + ":VIMN:2");        
        channelDesiredCurrentSetpoint = new ChannelDouble(name + " channelDesiredCurrentSetpoint", prefix + ":STRR:2");        
        channelCurrentIlkMax = new ChannelDouble(name + " channelCurrentIlkMax", prefix + ":IIMX:2");        
        channelCurrentIlkMin = new ChannelDouble(name + " channelCurrentIlkMin", prefix + ":IIMN:2");        
                              
        channelVoltageMax = new ChannelDouble(name + " channelVoltageMax", prefix + ":VSMX:2", RegisterAccessType.Read);
        channelCurrentMax = new ChannelDouble(name + " channelCurrentMax", prefix + ":ISMX:2", RegisterAccessType.Read);
        channelVoltageNominalReadback = new ChannelDouble(name + " channelVoltageNominalReadback", prefix + ":VNOMRB:2", RegisterAccessType.Read);
        channelCurrentNominalReadback = new ChannelDouble(name + " channelCurrentNominalReadback", prefix + ":INOMRB:2", RegisterAccessType.Read);
        channelDesiredVoltageReadback = new ChannelDouble(name + " channelDesiredVoltageReadback", prefix + ":SOLRB:2", RegisterAccessType.Read);
        channelDesiredCurrentReadback = new ChannelDouble(name + " channelDesiredCurrentReadback", prefix + ":STRRRB:2", RegisterAccessType.Read);
        channelVoltageIlkMaxReadback = new ChannelDouble(name + " channelVoltageIlkMaxReadback", prefix + ":VIMXRB:2", RegisterAccessType.Read);
        channelVoltageIlkMinReadback = new ChannelDouble(name + " channelVoltageIlkMinReadback", prefix + ":VIMNRB:2", RegisterAccessType.Read);
        channelCurrentIlkMaxReadback = new ChannelDouble(name + " channelCurrentIlkMaxReadback", prefix + ":IIMXRB:2", RegisterAccessType.Read);
        channelCurrentIlkMinReadback = new ChannelDouble(name + " channelCurrentIlkMinReadback", prefix + ":IIMNRB:2", RegisterAccessType.Read);                               
        
        channelEvMaskSet = new ChannelInteger(name + " channelEvMaskSet", prefix + ":CEMSKSET:2"); 
        channelEvMaskClr = new ChannelInteger(name + " channelEvMaskClr", prefix + ":CEMSKCLR:2"); 
        
        channelCommandReadback = new ChannelInteger(name + " channelCommandReadback", prefix + ":COMRB:1", RegisterAccessType.Read); 
        channelEventMaskReadback = new ChannelInteger(name + " channelEventMaskReadback", prefix + ":CEMSKRB:1", RegisterAccessType.Read); 
        channelStatus = new ChannelInteger(name + " channelStatus", prefix + ":STA:1", RegisterAccessType.Read); 
        channelEvents = new ChannelInteger(name + " channelEvents", prefix + ":CESTA:1", RegisterAccessType.Read); 
        
        
        channelCommand = new ChannelString(name + " channelCommand", prefix + ":COM:2"); 
        
        
        setChildren(new Device[]{moduleVoltageRamp, moduleCurrentRamp,             
                moduleTemperature, modulePower5, modulePower12, modulePower12N, moduleVoltageRampRb, 
                moduleCurrentRampRb, moduleVoltageMax, moduleCurrentMax,channelActualVoltage,channelActualCurrent,
                moduleRestartDelay, moduleAdcSamples, moduleDigFilter, moduleIlkCommand,
                moduleEvMaskSet, moduleEvMaskClr, moduleChMaskSet, moduleChMaskClr, 
                moduleFirmware, moduleSerial, modulePlacedChannels, moduleNoChannels,moduleRestartDelayRb, 
                moduleCommandRb, moduleEvMaskRb, moduleChMaskRb, moduleIlkCommandRb,moduleStatus, 
                moduleIlkStatus, moduleEvStatus, moduleChStatus, moduleAdcSamplesRb, moduleDigFilterRb,
                moduleCommand,
                channelVoltageNominalSetpoint, channelCurrentNominalSetpoint, channelDesiredVoltageSetpoint, 
                channelVoltageIlkMax, channelVoltageIlkMin, channelDesiredCurrentSetpoint, 
                channelCurrentIlkMax, channelCurrentIlkMin,                              
                channelVoltageMax, channelCurrentMax, channelVoltageNominalReadback,channelCurrentNominalReadback,
                channelDesiredVoltageReadback,channelDesiredCurrentReadback,channelVoltageIlkMaxReadback,
                channelVoltageIlkMinReadback, channelCurrentIlkMaxReadback,channelCurrentIlkMinReadback,    
                channelEvMaskSet, channelEvMaskClr,
                channelCommandReadback, channelEventMaskReadback, channelStatus, channelEvents,
                channelCommand }
        );

    }
    
        @Override
        protected void doUpdate() throws InterruptedException, IOException {
            super.doUpdate();
            try {
                
                channelActualVoltage.update();
                channelActualCurrent.update();
                setCache(new double[]{channelActualVoltage.getValue(), channelActualCurrent.getValue()});
            } catch (Exception ex) {
                throw new DeviceException(ex);
            }
        }

        @Override
        protected void doSetMonitored(boolean value) {
            super.doSetMonitored(value);
            for (Device dev: getChildren()){
                dev.setMonitored(value);
            }
        }        
    
  

    String toHexString(Integer value){
        return  String.format("0x%04X",value);         
    }

    public String getFirmware() throws InterruptedException, IOException {
        //return toHexString(moduleFirmware.getValue());
        int value = moduleFirmware.getValue();
        return String.valueOf((value&0xFF0000)>>16) + "." + String.valueOf((value&0xFF00)>>8) + String.valueOf((value&0xFF));
    }

    public String getSerial() throws InterruptedException, IOException {
        //return toHexString(moduleSerial.getValue());
        return String.valueOf(moduleSerial.getValue());
    }

    public Integer getNumberOfChannels() throws InterruptedException, IOException {
        return moduleNoChannels.getValue();
    }

    public Integer getPlacedChannels() throws InterruptedException, IOException {
        return modulePlacedChannels.getValue();
    }

    public Double getTemperature() throws InterruptedException, IOException {
        return moduleTemperature.getValue();
    }

    public Double getPower5() throws InterruptedException, IOException {
        return modulePower5.getValue();
    }

    public Double getPower12() throws InterruptedException, IOException {
        return modulePower12.getValue();
    }

    public Double getPower12N() throws InterruptedException, IOException {
        return modulePower12N.getValue();
    }

    public Double getVoltageRampReadback() throws InterruptedException, IOException {
        return moduleVoltageRampRb.getValue();
    }

    public Double getCurrentRampReadback() throws InterruptedException, IOException {
        return moduleCurrentRampRb.getValue();
    }

    public Double getCurrentMax() throws InterruptedException, IOException {
        return moduleCurrentMax.getValue();
    }

    public Double getVoltageMax() throws InterruptedException, IOException {
        return moduleVoltageMax.getValue();
    }

    public Integer getRestartDelayReadback() throws InterruptedException, IOException {
        return moduleRestartDelayRb.getValue();
    }

    public Integer getAdcSamplesReadback() throws InterruptedException, IOException {
        return moduleAdcSamplesRb.getValue();
    }

    public Integer getDigFilterReadback() throws InterruptedException, IOException {
        return moduleDigFilterRb.getValue();
    }

    public int getModuleControl() throws InterruptedException, IOException {
        return moduleCommandRb.getValue();
    }

    public String getModuleControlStr() throws InterruptedException, IOException {
        return toHexString(getModuleControl());
    }

    public int getEventMask() throws InterruptedException, IOException {
        return moduleEvMaskRb.getValue();
    }

    public String getEventMaskStr() throws InterruptedException, IOException {
        return toHexString(getEventMask());
    }

    public int getEventChannelMask() throws InterruptedException, IOException {
        return moduleChMaskRb.getValue();
    }

    public String getEventChannelMaskStr() throws InterruptedException, IOException {
        return toHexString(getEventChannelMask());
    }

    public int getChannelIlkCommand() throws InterruptedException, IOException {
        return moduleIlkCommandRb.getValue();
    }

    public String getChannelIlkCommandStr() throws InterruptedException, IOException {
        return toHexString(getChannelIlkCommand());
    }

    public static class ModuleStatus {

        int mask;
        boolean fineAdjAct;
        boolean ilkOutActive;
        boolean moduleIsOff;
        boolean needService;
        boolean inputError;
        boolean inSpecialMode;
        boolean commandComplete;
        boolean noModuleErr;
        boolean noChanRamping;
        boolean saftyLoopOk;
        boolean eventActive;
        boolean moduleStateOk;
        boolean powerSupplyOk;
        boolean temperatureOk;
        boolean killEnabled;
    }

    public ModuleStatus getStatus(int mask) throws InterruptedException, IOException {
        ModuleStatus ret = new ModuleStatus();
        ret.mask = mask;
        ret.fineAdjAct = ((mask & BIT0) != 0);
        ret.ilkOutActive = ((mask & BIT1) != 0);
        ret.moduleIsOff = ((mask & BIT2) != 0);
        ret.needService = ((mask & BIT4) != 0);
        ret.inputError = ((mask & BIT5) != 0);
        ret.inSpecialMode = ((mask & BIT6) != 0);
        ret.commandComplete = ((mask & BIT7) != 0);
        ret.noModuleErr = ((mask & BIT8) != 0);
        ret.noChanRamping = ((mask & BIT9) != 0);
        ret.saftyLoopOk = ((mask & BIT10) != 0);
        ret.eventActive = ((mask & BIT11) != 0);
        ret.moduleStateOk = ((mask & BIT12) != 0);
        ret.powerSupplyOk = ((mask & BIT13) != 0);
        ret.temperatureOk = ((mask & BIT14) != 0);
        ret.killEnabled = ((mask & BIT15) != 0);
        return ret;
    }
    
    public ModuleStatus getStatus() throws InterruptedException, IOException {
        Integer mask = moduleStatus.getValue();
        return getStatus(mask);
    }
    public static class ModuleEventStatus {

        int mask;
        boolean restarted;
        boolean needService;
        boolean inputError;
        boolean saftyLoopOpen;
        boolean noGood;
        boolean tGreater55C;

    }

    public ModuleEventStatus getEventStatus() throws InterruptedException, IOException {
        Integer mask = moduleEvStatus.getValue();
        ModuleEventStatus ret = new ModuleEventStatus();
        ret.mask = mask;
        ret.restarted = ((mask & BIT1) != 0);
        ret.needService = ((mask & BIT4) != 0);
        ret.inputError = ((mask & BIT5) != 0);
        ret.saftyLoopOpen = ((mask & BIT10) != 0);
        ret.noGood = ((mask & BIT13) != 0);
        ret.tGreater55C = ((mask & BIT14) != 0);
        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////
    //Public Interface - Module config
    ////////////////////////////////////////////////////////////////////////////        
    public Double getVoltageRampSetpoint() throws InterruptedException, IOException {
        return moduleVoltageRamp.getValue();
    }

    public Double getCurrentRampSetpoint() throws InterruptedException, IOException {
        return moduleCurrentRamp.getValue();
    }

    public Integer getAdcSamplesSetpoint() throws InterruptedException, IOException {
        return moduleAdcSamples.getValue();
    }

    public void setAdcSamplesSetpoint(Integer value) throws InterruptedException, IOException {
        moduleAdcSamples.write(value);
    }

    public Integer getDigFilterSetpoint() throws InterruptedException, IOException {
        return moduleDigFilter.getValue();
    }

    public void setDigFilterSetpoint(Integer value) throws InterruptedException, IOException {
        moduleDigFilter.write(value);
    }

    public Integer getRestartDelaySetpoint() throws InterruptedException, IOException {
        return moduleRestartDelay.getValue();
    }

    public void setRestartDelaySetpoint(Integer value) throws InterruptedException, IOException {
        moduleRestartDelay.write(value);
    }

    public void setVoltageRampSetpoint(Double value) throws InterruptedException, IOException {
        moduleVoltageRamp.write(value);
    }

    public void setCurrentRampSetpoint(Double value) throws InterruptedException, IOException {
        moduleCurrentRamp.write(value);
    }

    public enum ModuleCommand {

        SAVE,
        RECALL,
        STP_ON,
        STP_OFF,
        AON_ON,
        AON_OFF,
        ADJ_ON,
        ADJ_OFF,
        KIL_ON,
        KIL_OFF,
        CLEAR
    }

    public void setModuleCommand(ModuleCommand cmd) throws InterruptedException, IOException {
        moduleCommand.write(cmd.toString().replaceAll("_", " "));
    }

    ////////////////////////////////////////////////////////////////////////////
    //Public Interface - Channel Readback
    ////////////////////////////////////////////////////////////////////////////        
    public static class ChannelStatus {

        int mask;
        boolean inputDataErr;
        boolean isOn;
        boolean isRamping;
        boolean isEmergencyOff;
        boolean isIControlled;
        boolean isVControlled;
        boolean iOutOfBounds;
        boolean vOutOfBounds;
        boolean isExtInhibit;
        boolean isTripSet;
        boolean hwILimExc;
        boolean hwVLimExc;

        String getMessageStatus() {
            if (!isOn) {
                return "Off";
            }
            if (isRamping) {
                return "Ramping";
            }
            if (isEmergencyOff) {
                return "Emergency";
            }
            if (vOutOfBounds || iOutOfBounds) {
                return "Out of Bounds";
            }
            if (hwILimExc || hwILimExc) {
                return "Lim Exc";
            }
            if (isExtInhibit) {
                return "Ext Inhibit";
            }
            if (isRamping) {
                return "Ramping";
            }
            if (isVControlled) {
                return "Voltage Control";
            }
            if (isIControlled) {
                return "Current Control";
            }
            if (inputDataErr) {
                return "Input Data Error";
            }
            return "Unknown";
        }
    }
    
    public static ChannelStatus getChannelStatus(int mask) throws InterruptedException, IOException {
        ChannelStatus ret = new ChannelStatus();
        ret.mask = mask;
        ret.inputDataErr = ((mask & BIT2) != 0);
        ret.isOn = ((mask & BIT3) != 0);
        ret.isRamping = ((mask & BIT4) != 0);
        ret.isEmergencyOff = ((mask & BIT5) != 0);
        ret.isIControlled = ((mask & BIT6) != 0);
        ret.isVControlled = ((mask & BIT7) != 0);
        ret.iOutOfBounds = ((mask & BIT10) != 0);
        ret.vOutOfBounds = ((mask & BIT11) != 0);
        ret.isExtInhibit = ((mask & BIT12) != 0);
        ret.isTripSet = ((mask & BIT13) != 0);
        ret.hwILimExc = ((mask & BIT14) != 0);
        ret.hwVLimExc = ((mask & BIT15) != 0);
        return ret;
    }
    

    public ChannelStatus getChannelStatus() throws InterruptedException, IOException {
        Integer mask = channelStatus.getValue();
        return getChannelStatus(mask);
    }

    public ChannelStatus getChannelEventStatus() throws InterruptedException, IOException {
        Integer mask = channelEvents.getValue();
        ChannelStatus ret = new ChannelStatus();
        ret.mask = mask;
        ret.inputDataErr = ((mask & BIT2) != 0);
        ret.isOn = ((mask & BIT3) != 0);
        ret.isRamping = ((mask & BIT4) != 0);
        ret.isEmergencyOff = ((mask & BIT5) != 0);
        ret.isIControlled = ((mask & BIT6) != 0);
        ret.isVControlled = ((mask & BIT7) != 0);
        ret.iOutOfBounds = ((mask & BIT10) != 0);
        ret.vOutOfBounds = ((mask & BIT11) != 0);
        ret.isExtInhibit = ((mask & BIT12) != 0);
        ret.isTripSet = ((mask & BIT13) != 0);
        ret.hwILimExc = ((mask & BIT14) != 0);
        ret.hwVLimExc = ((mask & BIT15) != 0);
        return ret;
    }

    public int getChannelEventMask() throws InterruptedException, IOException {
        return channelEventMaskReadback.getValue();
    }

    public Double getChannelVoltageMax() throws InterruptedException, IOException {
        return channelVoltageMax.getValue();
    }

    public Double getChannelVoltageNominalReadback() throws InterruptedException, IOException {
        return channelVoltageNominalReadback.getValue();
    }
    
    public Double getChannelActualVoltage() throws InterruptedException, IOException {
        return channelActualVoltage.getValue();
    }
            
    public Double getChannelCurrentMax() throws InterruptedException, IOException {
        return channelCurrentMax.getValue();
    }
    
    public Double getChannelActualCurrent() throws InterruptedException, IOException {
        return channelActualCurrent.getValue();
    }

    public Double getChannelCurrentNominalReadback() throws InterruptedException, IOException {
        return channelCurrentNominalReadback.getValue();
    }

    public Double getChannelDesiredVoltageReadback() throws InterruptedException, IOException {
        return channelDesiredVoltageReadback.getValue();
    }

    public Double getChannelDesiredCurrentReadback() throws InterruptedException, IOException {
        return channelDesiredCurrentReadback.getValue();
    }

    public Double getChannelVoltageIlkMaxReadback() throws InterruptedException, IOException {
        return channelVoltageIlkMaxReadback.getValue();
    }

    public Double getChannelVoltageIlkMinReadback() throws InterruptedException, IOException {
        return channelVoltageIlkMinReadback.getValue();
    }

    public Double getChannelCurrentIlkMaxReadback() throws InterruptedException, IOException {
        return channelCurrentIlkMaxReadback.getValue();
    }

    public Double getChannelCurrentIlkMinReadback() throws InterruptedException, IOException {
        return channelCurrentIlkMinReadback.getValue();
    }

    public int getChannelControlReadback() throws InterruptedException, IOException {
        return channelCommandReadback.getValue();
    }

    public String getChannelControlReadbackStr() throws InterruptedException, IOException {
        return toHexString(getModuleControl());
    }

    ////////////////////////////////////////////////////////////////////////////
    //Public Interface - Channel Config
    ////////////////////////////////////////////////////////////////////////////        
    public Double getChannelDesiredVoltage() throws InterruptedException, IOException {
        return channelDesiredVoltageSetpoint.getValue();
    }

    public Double getChannelDesiredCurrent() throws InterruptedException, IOException {
        return channelDesiredCurrentSetpoint.getValue();
    }

    public void setChannelDesiredVoltage(Double value) throws InterruptedException, IOException {
        channelDesiredVoltageSetpoint.write(value);
    }

    public void setChannelDesiredCurrent(Double value) throws InterruptedException, IOException {
        channelDesiredCurrentSetpoint.write(value);
    }

    public Double getChannelVoltageIlkMax() throws InterruptedException, IOException {
        return channelVoltageIlkMax.getValue();
    }

    public Double getChannelVoltageIlkMin() throws InterruptedException, IOException {
        return channelVoltageIlkMin.getValue();
    }

    public Double getChannelCurrentIlkMax() throws InterruptedException, IOException {
        return channelCurrentIlkMax.getValue();
    }

    public Double getChannelCurrentIlkMin() throws InterruptedException, IOException {
        return channelCurrentIlkMin.getValue();
    }

    public void setChannelVoltageIlkMax(Double value) throws InterruptedException, IOException {
        channelVoltageIlkMax.write(value);
    }

    public void setChannelVoltageIlkMin(Double value) throws InterruptedException, IOException {
        channelVoltageIlkMin.write(value);
    }

    public void setChannelCurrentIlkMax(Double value) throws InterruptedException, IOException {
        channelCurrentIlkMax.write(value);
    }

    public void setChannelCurrentIlkMin(Double value) throws InterruptedException, IOException {
        channelCurrentIlkMin.write(value);
    }

    public Double getChannelVoltageNominal() throws InterruptedException, IOException {
        return channelVoltageNominalSetpoint.getValue();
    }

    public Double getChannelCurrentNominal() throws InterruptedException, IOException {
        return channelCurrentNominalSetpoint.getValue();
    }

    public void setChannelVoltageNominal(Double value) throws InterruptedException, IOException {
        channelVoltageNominalSetpoint.write(value);
    }

    public void setChannelCurrentNominal(Double value) throws InterruptedException, IOException {
        channelCurrentNominalSetpoint.write(value);
    }

    public enum ChannelCommand {
        ON,
        OFF,
        EMCY_ON,
        EMCY_OFF,
        AVBND_ON,
        AVBND_OFF,
        AIBND_ON,
        AIBND_OFF
    }

    public void setChannelCommand(ChannelCommand cmd) throws InterruptedException, IOException {
        channelCommand.write(cmd.toString().replaceAll("_", " "));
    }
}
