package RFC::PSU;
#$Id: PSU.pm,v 1.116 2025/06/30 07:15:10 sqko Exp $

#################################################################
#
#
#      RFC::PSU
#
#  The PSU class (and the derived classes) defines the behaviour
#  of DC power supplies for the RFC control system. To obtain a
#  PSU instance, call one of the constructors like shown below:
#
#  $id = RFC::PSU->new($name,$rig);
#
#  $id = RFC::DefaultDelta->new($name,$rig);
#
#  $id = RFC::SM_15_100->new($name,$rig);
#
#  $id = RFC::SM_18_50->new($name,$rig);
#
#  $id = RFC::SM_60_100->new($name,$rig);
#
#  $id = RFC::SM_35_45->new($name,$rig);
#
#  $id = RFC::SM_52_30->new($name,$rig);
#
#  $id = RFC::SM_70_22->new($name,$rig);
#
#  $id = RFC::SM_120_13->new($name,$rig);
#
#  $id = RFC::SM_300_5->new($name,$rig);
#
#  $id = RFC::SM_30_200->new($name,$rig);
#
#  $id = RFC::ES015_10->new($name,$rig);
#
#  $id = RFC::ES030_5->new($name,$rig);
#
#  $id = RFC::ES075_2->new($name,$rig);
#
#  $id = RFC::ES0300_045->new($name,$rig);
#
#  $id = RFC::ManualPSU->new($name,$rig);
#
#  $id = RFC::BKPrecision->new($name,$rig);
#
#  $id = RFC::HMP->new($name,$rig);
#
#
#
#  The PSU class defines the follwing new functions.
#
# $id->voltage($)    Sets or returns the applied DC voltage
#                    If no argument is supplied, it simply returns 
#                    the last set value.
# $id->current($)    Sets or returns the applied DC current
#                    If no argument is supplied, it simply returns 
#                    the last set value.
# $id->RSD($)        Enables or disables DC output if such a device
#                    (relay) is attatched to the system.
# $id->minvoltage()  Returns the minimum voltage.
# $id->maxvoltage()  Returns the maximum voltage.
# $id->mincurrent()  Returns the minimum current.
# $id->maxcurrent()  Returns the maximum current.
# $id->idn()         Returns a hardware specific identifier string
# $id->measure_U()   Returns the measured voltage output (if the device
#                    implements this).
# $id->measure_I()   Returns the measured current output (if the device
#                    implements this).
# $id->get_I()       Returns the current setpoint (some devices returns
#                    the result of measure_I() instead).
# $id->get_U()       Returns the voltage setpoint (some devices returns
#                    the result of measure_U() instead).
# $id->set($)        Alias for set_voltage($) or set_current($) depending
#                    on the setting of the 'control_mode' variable.
# $id->read          Alias for measure_I. Any arguments to read is passed
#                    to measure_I.
#
#
# Note that the HMP PSU does not have the option of running RS232 Daisychain
# so only one HMP device can be connected to a given serial line (running RS232).
#
##################################################################

use strict;
use warnings;
use RFC::Header;
use RFC::BaseDevice;
use RFC::Cache;
our @ISA = qw(RFC::BaseDevice);

BEGIN {
    use Exporter ();
    our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
    $VERSION = 1.0;
    %EXPORT_TAGS = (
        'standard'=>[],
                    );
    Exporter::export_ok_tags('standard');
    Exporter::export_tags('standard');
    @ISA = qw(Exporter);
}

sub new()
{
    my $class = shift;
    my $self = $class->SUPER::new(@_);
    $self->{'_type'} = 'PSU';
    $self->{'_monitor_rows'} = '3,5';
    $self->{'_maxI'} = 0;
    $self->{'_maxU'} = 0;
    $self->{'_minI'} = 0;
    $self->{'_minU'} = 0;
    $self->{'_RSD_device'} = undef;
    $self->{'_setup_tags'} = ['mode','communication','tty','address','serial','parallel','external_RSD','RSD_name','RSD_type','RSD_tty','RSD_address','RSD_channel','control_mode','master_device','slave_current','slave_voltage','show_plot'];
    $self->{'_reload_tags'} = ['class','type','mode','external_RSD','RSD_name'];
    $self->{'_setup_tags_readonly'} = ['communication'];
    $self->{'_desc'} = {
	'mode' =>'Device control mode',
	'tty' => 'Communication device',
	'address' => 'Device address',
	'serial' => 'Number of devices in serial (master + slaves)',
	'parallel' => 'Number of devices in parallel (master + slaves)',
	'external_RSD' => 'Existence of external remote shut down device (usually an external relay)',
	'RSD_name' => 'Device name for remote shut down relay',
	'RSD_type' => 'Device type for remote shutdown relay, only used if no RSD name',
	'RSD_tty' => 'Communication device for remote shutdown relay, only used if no RSD name',
	'RSD_address' => 'Remote shutdown relay address, only used if no RSD name',
	'RSD_channel' => 'Remote shutdown relay channel, only used if no RSD name',
	'title' => 'Optional device title',
        'master_device' => 'Device name for the master device in case current device is a slave device (locked to have X percent of master device current)',
        'slave_current' => 'Percentage of master device current that this device is supposed to have',
        'slave_voltage' => 'Percentage of master device voltage that this device is supposed to have',
	'control_mode' => 'determines if the set() function should control voltage or current',
	'communication' => 'Device communication',
    };
    $self->{'_default'} = {
	'type' => 'SM_15_100',
	'mode' =>'Manual',
	'tty' => 'ttyS0',
	'address' => 0,
	'serial' => 1,
	'parallel' => 1,
	'external_RSD' => 'No',
	'RSD_type' => 'ICP',
	'RSD_tty' => 'ttyS0',
	'RSD_address' => 0,
	'RSD_channel' => 0,
	'show_plot'=>'Yes',
        'slave_current' => 100,
        'slave_voltage' => 100,
	'control_mode'=>'Current',
	'communication' => 'Serial',
    };
    $self->{'_set_fn_names'} = ['current','voltage','RSD']; # List of function names which can trigger a notify event
    $self->{'_values_external_RSD'} = ['Yes','No'];
    $self->{'_values_control_mode'} = ['Voltage','Current'];
    $self->{'_values_RSD_name'} = ['',($self->rig->avaliable_devices('relay'))];
    $self->{'_values_RSD_type'} = [(RFC::Device::list_RELAY())];
    $self->{'_read_names'} = ['read','measure_I','measure_U','get_I','get_U'];
    $self->{'_set_names'} = ['current','voltage','set','RSD'];
    $self->{'_U'} = undef;
    $self->{'_I'} = undef;
    $self->{'_RSD_device'} = undef;;
    $self->{'_section'} = 'PSU_'.$self->name;
    bless $self,$class;
    ### Specproc for backwards compatibility ###
    $self->{'_section'} = 'current' if (!$self->rig->config_section_exists($self->sectionname) && $self->name eq 'current');
    my @list = ();
    foreach my $n ($self->rig->avaliable_devices('PSU'))
    {
	next unless ($n);
        push @list,$n unless ($n eq $self->name);
    }
    $self->{'_values_master_device'} = ['',@list];
    $self->init();
    return $self;
}

sub readonly_value($)
{
    my $self = shift;
    my $tag = shift;
    return undef unless ($tag);
    return $self->{'_communication'} if ($tag eq 'communication');
    return undef;
}

sub setpoint
{
    my $self = shift;
    return $self->get_I(@_);
}

sub maxcurrent()
{
    my $self = shift;
    return $self->{'_maxI'} * $self->{'_parallel'};
}

sub mincurrent()
{
    my $self = shift;
    return $self->{'_minI'} * $self->{'_parallel'};
}

sub maxvoltage()
{
    my $self = shift;
    return $self->{'_maxU'} * $self->{'_serial'};
}

sub minvoltage()
{
    my $self = shift;
    return $self->{'_minU'} * $self->{'_serial'};
}

sub init()
{
    my $self = shift;
    foreach my $tag ($self->setup_tags)
    {
        $self->{'_'.$tag} = $self->get_default($tag);
        my $conf = $self->get_conf($tag);
        $self->{'_'.$tag} = $conf if (defined($conf) && $conf =~ /\w/);
    }
    foreach my $tag ('_serial','_parallel')
    {
	$self->{$tag} = 1 unless ($self->{$tag} && $self->{$tag} =~ /^\d+$/ && $self->{$tag} > 1);
    }
    unless ($self->{'_address'})
    {
	### Specproc for backwards compatibility ###
	my $tag = 'powersupply';
        my $conf = $self->get_conf($tag);
        $self->{'_'.$tag} = $conf if (defined($conf) && $conf =~ /\w/);
    }
    if ($self->{'_external_RSD'} && $self->{'_external_RSD'} =~ /yes/i)
    {
	my $dev = $self->rig->get_device('relay',$self->{'_RSD_name'});
	unless ($dev)
	{
	    $dev = $self->rig->init_device('relay',$self->{'_RSD_name'});
	    $dev->readstring_ignore(1) if ($dev);
	} 
	my $devname = $self->name.'_RSD';
	$dev = $self->rig->get_device('relay',$devname) unless ($dev);
	unless ($dev)
	{
	    $dev = RFC::Device::new_RELAY($self->rig,$devname,$self->{'_RSD_type'},$self->{'_RSD_tty'},$self->{'_RSD_address'},$self->{'_RSD_channel'});
	    $dev->readstring_ignore(1);
	    $self->rig->register_device('relay',$dev);
	}
	$self->{'_RSD_device'} = $dev;
    }
    ### Handle slave devices ##
    if ($self->{'_master_device'})
    {
        my $dev = $self->rig->init_device('PSU',$self->{'_master_device'});
        $self->die("Could not get a device instance for master device '".$self->{'_master_device'}."'!") unless ($dev);
        $self->die("A device can not be slave to an other slave device!") if ($dev->master);
        $self->{'_mdev'} = $dev;
        $dev->attatch_observer(\$self);
        $self->{'_readonly'} = 1; ### A slave device can not  be directly controlled, only indirectly ###
    }
    return $self;
}

sub get_version()
{
    my $self = shift;
    ### get the serial-server version ###
    return SocketClient::serial_client($self->{'_tty'},'version');
}


sub test()
{
    my $self = shift;
    return 0 if ($self->{'_RSD_device'} && !$self->{'_RSD_device'}->test);
    return 1;
}

sub test_string()
{
    my $self = shift;
    my $string = 'PSU '.$self->name."\n";
    $string .= "\n",$self->{'_RSD_device'}->test_string(@_) if ($self->{'_RSD_device'});
    return $string;
}

sub print_config()
{
    my $self = shift;
    my @arr = ();
    push @arr,'Name: '.$self->name;
    push @arr,'PSU type: '.$self->type;
    push @arr,'TTY: '.$self->{'_tty'};
    push @arr,'Address: '.$self->{'_address'};
    push @arr,'Parallel: '.$self->{'_parallel'};
    push @arr,'Serial: '.$self->{'_serial'};
    if ($self->{'_RSD_device'})
    {
        push @arr,$self->{'_RSD_device'}->print_config;
    }
    return join (', ',@arr);
}

sub RSD($)
{
    my $self = shift;
    my $status = shift;
    return undef unless ($self->{'_RSD_device'});
    $self->writedebug('Setting RSD device to '.$status) if ($self->debug > 1);
    if ($self->debug > 0)
    {
	return $self->{'_RSD_device'}->set($status);
    }
    else
    {
	return $self->{'_RSD_device'}->set_noinfo($status);
    }
    $self->notify('rsd',$status,@_);
}

sub voltage()
{
    my $self = shift;
    if (@_)
    {
	$self->{'_U'} = shift;
	$self->writedebug($self->{'_type'}.' is a base class and does not impelemnt id->voltage($), only the derived classes does so!',1);
	$self->proglog("Voltage for PSU ".$self->name." set to ".$self->{'_U'}." V",@_);
    }
    return $self->{'_U'};
}

sub current()
{
    my $self = shift;
    if (@_)
    {
	$self->{'_I'} = shift;
	$self->writedebug($self->{'_type'}.' is a base class and does not impelemnt id->current($), only the derived classes does so!',1);
	$self->proglog("Current for PSU ".$self->name." set to ".$self->{'_I'}." A",@_);
    }
    return $self->{'_I'};
}

sub set
{
    my $self = shift;
    if ($self->{'_control_mode'} && $self->{'_control_mode'} =~ /current/i)
    {
	return $self->current(@_);
    }
    else
    {
	return $self->voltage(@_);
    }
}

sub measure_U()
{
    my $self = shift;
    return undef;
}

sub measure_I()
{
    my $self = shift;
    return undef;
}

sub idn()
{
    my $self = shift;
    return '';
}

sub get_I()
{
    my $self = shift;
    return $self->{'_I'};
}

sub get_U()
{
    my $self = shift;
    return $self->{'_U'};
}


sub monitor()
{
    my $self = shift;
    my $data = undef;
    if (@_)
    {
        $data = $_[0];
    }
    else
    {
	$data = RFC::Cache->new();
	$data->debug($self->debug) if ($self->debug);
    }
    $self->check_cache($data);
    my $u = $self->get_U($data);
    $u = -32768 unless defined($u);
    my $i = $self->get_I($data);
    $i = -32768 unless defined($i);
    return join(' ','Current',$i,'Voltage',$u);
}

sub observe()
{
    my $self = shift;
    my $action = shift;
    if ($action && $action eq 'current' && $self->{'_mdev'})
    {
        my $flow = shift;
        $flow = $flow * $self->{'_slave_current'} / 100;
        return $self->current($flow,@_);
    }
    if ($action && $action eq 'voltage' && $self->{'_mdev'})
    {
        my $flow = shift;
        $flow = $flow * $self->{'_slave_voltage'} / 100;
        return $self->voltage($flow,@_);
    }
}

package RFC::ManualPSU;
our @ISA = qw(RFC::PSU);

sub new
{
    my $class = shift;
    my $name = shift;
    my $rig = shift;
    my $self = $class->SUPER::new($name,$rig);
    $self->{'_type'} = 'Manual PSU';
    $self->{'_mode'} = 'Manual';
    $self->{'_idn'} = undef;
    $self->{'_setup_tags'} = ['mode','external_RSD','RSD_name','RSD_type','RSD_tty','RSD_address','RSD_channel','control_mode','master_device','slave_current','slave_voltage','show_plot'];
    $self->{'_reload_tags'} = ['class','type','mode','external_RSD','RSD_name'];
    $self->{'_ui_fn_names'} = []; # List of function names which can be called from the device setup UI
    $self->{'_ui_fn_desc'} = {
    };
    $self->{'_default'} = {
	'mode' =>'Manual',
	'serial' => 1,
	'parallel' => 1,
	'external_RSD' => 'No',
	'RSD_type' => 'ICP',
	'RSD_tty' => 'ttyS0',
	'RSD_address' => 0,
	'RSD_channel' => 0,
	'show_plot'=>'Yes',
        'slave_current' => 100,
        'slave_voltage' => 100,
	'control_mode'=>'Current',
    };
    bless $self,$class;
    $self->{'_file'} = SemaforeFile->new($RFC::Header::userdir.'rig'.$self->rig->name.'/PSU_'.$self->name,'/home/celltest/Gas_rig'.$self->rig->name.'.lock');
    unless ($self->{'_file'}->exist)
    {
        ### Create file with rw for all           ###
        ### Yes I know 666 is the number of evil! ###
        $self->{'_file'}->lock_ex;
        $self->{'_file'}->writeline('0.00','0.00');
        chmod 0666, $self->{'_file'}->filename;
        $self->{'_file'}->unlock;
    }
    return $self;
}


sub get_version()
{
    my $self = shift;
    return 'Manual';
}


sub test()
{
    my $self = shift;
    return 0 if ($self->{'_RSD_device'} && !$self->{'_RSD_device'}->test);
    return 1;
}

sub test_string()
{
    my $self = shift;
    my $string = 'PSU '.$self->name."\n";
    $string .= "\n",$self->{'_RSD_device'}->test_string(@_) if ($self->{'_RSD_device'});
    return $string;
}

sub print_config()
{
    my $self = shift;
    my @arr = ();
    push @arr,'Name: '.$self->name;
    push @arr,'PSU type: '.$self->type;
    if ($self->{'_RSD_device'})
    {
        push @arr,$self->{'_RSD_device'}->print_config;
    }
    return join (', ',@arr);
}

sub voltage()
{
    my $self = shift;
    ($self->{'_U'},$self->{'_I'}) = $self->{'_file'}->readlines();
    if (@_)
    {
	$self->{'_U'} = shift;
	$self->{'_file'}->writeline($self->{'_U'},$self->{'_I'});
	$self->proglog("Voltage for PSU ".$self->name." set to ".$self->{'_U'}." V manually",@_);
    }
    return $self->{'_U'};
}

sub current()
{
    my $self = shift;
    ($self->{'_U'},$self->{'_I'}) = $self->{'_file'}->readlines();
    if (@_)
    {
	$self->{'_I'} = shift;
	$self->{'_file'}->writeline($self->{'_U'},$self->{'_I'});
	$self->proglog("Current for PSU ".$self->name." set to ".$self->{'_I'}." A manually",@_);
    }
    return $self->{'_I'};
}

sub set
{
    my $self = shift;
    if ($self->{'_control_mode'} && $self->{'_control_mode'} =~ /current/i)
    {
	return $self->current(@_);
    }
    else
    {
	return $self->voltage(@_);
    }
}

sub measure_U
{
    my $self = shift;
    return $self->voltage();
}

sub measure_I
{
    my $self = shift;
    return $self->current();
}

sub idn()
{
    my $self = shift;
    return 'Manual PSU';
}

sub get_I()
{
    my $self = shift;
    return $self->current();
}

sub get_U()
{
    my $self = shift;
    return $self->voltage();
}

sub readstring
{
    my $self = shift;
    return undef if ($self->readstring_ignore);
    my $data = undef;
    if (@_)
    {
        $data = $_[0];
    }
    else
    {
        $data = RFC::Cache->new();
        $data->debug($self->debug()) if ($self->debug);
    }
    $self->check_cache($data);
    my @t = $self->get_time();
    my $t = join(':',@t);
    my $res = join(' ',$self->name.'_current',$t,time,$self->measure_I($data),$self->name.'_voltage',$t,time,$self->measure_U($data));
    $self->writedebug($res) if ($self->debug > 1);
    return $res;
}


package RFC::BKPrecision;
our @ISA = qw(RFC::PSU);

sub new
{
    my $class = shift;
    my $name = shift;
    my $rig = shift;
    my $self = $class->SUPER::new($name,$rig);
    $self->{'_type'} = 'BK Precision';
    $self->{'_mode'} = 'Automatic';
    $self->{'_idn'} = undef;
    $self->{'_setup_tags'} = ['mode','communication','tty','external_RSD','RSD_name','control_mode','master_device','slave_current','slave_voltage','show_plot'];
    $self->{'_setup_tags_readonly'} = ['mode'];
    $self->{'_reload_tags'} = ['class','type','mode','communication','external_RSD','RSD_name'];
    $self->{'_default'} = {
	'communication' => 'USB',
	'tty' => 'ttyS0',
	'mode' =>'Automatic',
	'serial' => 1,
	'parallel' => 1,
	'external_RSD' => 'No',
	'show_plot'=>'Yes',
        'slave_current' => 100,
        'slave_voltage' => 100,
	'control_mode'=>'Current',
    };
    bless $self,$class;
    $self->load_conf();
    if ($self->{'_communication'} eq 'USB')
    {
	$self->{'_setup_tags'} = ['mode','communication','tty','external_RSD','RSD_name','control_mode','master_device','slave_current','slave_voltage','show_plot'];
	$self->{'_info_string'} = 'Note that the serial server handling USB communication must be set up to emulate a normal serial tty (e.x serial-socket-server-9.pl ttyUSB0 9600 ttyS10)';
    }
    if ($self->{'_communication'} eq 'RS232')
    {
	$self->{'_setup_tags'} = ['mode','communication','tty','external_RSD','RSD_name','control_mode','master_device','slave_current','slave_voltage','show_plot'];
    }
    # List of function names which can be called from the device setup UI
    $self->{'_ui_fn_names'} = ['get_slewrate_voltage','get_slewrate_current','set_slewrate_voltage','set_slewrate_current']; 
    $self->{'_ui_fn_args'} = {'set_slewrate_voltage' => 1,'set_slewrate_current' => 1};
    $self->{'_ui_fn_desc'} = {
	'get_slewrate_voltage' => 'Gets the voltage ramprate',
	'get_slewrate_current' => 'Gets the current ramprate',
	'set_slewrate_voltage' => 'Sets the voltage ramprate',
	'set_slewrate_current' => 'Sets the current ramprate',
    };
    $self->{'_values_communication'} = ['USB','RS232'];
    return $self;
}

sub readonly_value($)
{
    my $self = shift;
    my $tag = shift;
    return undef unless ($tag);
    return $self->{'_mode'} if ($tag eq 'mode');
    return undef;
}


sub maxcurrent()
{
    my $self = shift;
    return $self->sendcmd('OUT:MAX:CURR?') * $self->{'_parallel'};
}

sub mincurrent()
{
    my $self = shift;
    return $self->sendcmd('OUT:MIN:CURR?') * $self->{'_parallel'};
}

sub maxvoltage()
{
    my $self = shift;
    return $self->sendcmd('OUT:MAX:VOLT?')  * $self->{'_serial'};
}

sub minvoltage()
{
    my $self = shift;
    return $self->sendcmd('OUT:MIN:VOLT?') * $self->{'_serial'};
}

sub get_slewrate_voltage
{
    my $self = shift;
    return $self->sendcmd('OUT:SR:VOLT?')  * $self->{'_serial'};
}

sub get_slewrate_current
{
    my $self = shift;
    return $self->sendcmd('OUT:SR:CURR?') * $self->{'_parallel'};
}

sub set_slewrate_voltage
{
    my $self = shift;
    my $sr = shift;
    $self->sendcmd(sprintf('OUT:SR:VOLT %.2f',($sr/$self->{'_serial'})));
    return $self->sendcmd('OUT:SR:VOLT?');
}

sub set_slewrate_current
{
    my $self = shift;
    my $sr = shift;
    $self->sendcmd(sprintf('OUT:SR:CURR %.2f',($sr/$self->{'_parallel'})));
    return $self->sendcmd('OUT:SR:CURR?');
}

sub get_state
{
    my $self = shift;
    return $self->sendcmd('OUT:STATE?');
}

sub sendcmd
{
    my $self = shift;
    my $cmd = shift;
    my $res = undef;
    if ($self->{'_communication'} =~ /usb|rs232/i)
    {
	my $tty = $self->{'_tty'};
	$self->writedebug("Sending command 'bk_raw $cmd' through $tty for $self") if ($self->debug > 3);
	$res = SocketClient::serial_client_raw($tty,'bk_raw',$cmd);
    }
    $self->writedebug("Response for $self: $res") if ($self->debug > 3);
    $res =~ s/[\r\n]//g if (defined($res));
    return $res;
}

sub test
{
    my $self = shift;
    return 0 if ($self->{'_RSD_device'} && !$self->{'_RSD_device'}->test);
    return 1 if ($self->test_string);
    return 0;
}

sub test_string
{
    my $self = shift;
    my $str = $self->sendcmd('*idn?');
    $str .= "\n". $self->{'_RSD_device'}->test_string() if ($self->{'_RSD_device'});
    return $str;
}


sub print_config()
{
    my $self = shift;
    my @arr = ();
    push @arr,'Name: '.$self->name;
    push @arr,'PSU type: '.$self->type;
    push @arr,'Communication: '.$self->{'_communication'};
    if ($self->{'_communication'} =~ /usb|rs232/i)
    {
	push @arr,'TTY: '.$self->{'_tty'};
    }
    push @arr,'Parallel: '.$self->{'_parallel'};
    push @arr,'Serial: '.$self->{'_serial'};
    if ($self->{'_RSD_device'})
    {
        push @arr,$self->{'_RSD_device'}->print_config;
    }
    return join (', ',@arr);
}

sub voltage
{
    my $self = shift;
    if (@_)
    {
	my $volt = shift;
	my $v = $volt;
	$self->writedebug("Setting voltage for $self to $volt") if ($self->debug > 2);
	if ($volt =~ /ocv/i)
	{
	    $self->sendcmd('OUT OFF');
	    if ($self->{'_RSD_device'})
	    {
		$self->{'_RSD_device'}->set(0,@_);
	    }
	}
	else
	{
	    $volt = $volt / $self->{'_parallel'};
	    my $limit = $self->sendcmd('OUT:MAX:VOLT?');
	    if ($volt > $limit)
	    {
		$self->proglog("Warning: voltage ($volt) above maximum allowed ($limit) setting maximum!");
		$self->writedebug("Warning: voltage ($volt) above maximum allowed ($limit) setting maximum!");
		$volt = $limit;
	    }
	    $limit = $self->sendcmd('OUT:MIN:VOLT?');
	    if ($volt < $limit)
	    {
		$self->proglog("Warning: voltage ($volt) below minimum allowed ($limit) setting minimum!");
		$self->writedebug("Warning: voltage ($volt) below allowed ($limit) setting minimum!");
		$volt = $limit;
	    }
	    $self->sendcmd(sprintf('VOLT %.2f',$volt));
	    $self->sendcmd('OUT ON');
	    if ($self->{'_RSD_device'})
	    {
		$self->{'_RSD_device'}->set(1,@_);
	    }
	}
	$self->notify('voltage',$v,@_);
	$self->proglog("Voltage for PSU ".$self->name." set to ".$volt." V",@_);
    }
    return $self->sendcmd('VOLT?');
}


sub current
{
    my $self = shift;
    if (@_)
    {
	my $amp = shift;
	my $c = $amp;
	$self->writedebug("Setting current for $self to $amp") if ($self->debug > 2);
	if ($amp =~ /ocv/i)
	{
	    $self->sendcmd('OUT OFF');
	    if ($self->{'_RSD_device'})
	    {
		$self->{'_RSD_device'}->set(0,@_);
	    }
	}
	else
	{
	    $amp = $amp / $self->{'_parallel'};
	    my $limit = $self->sendcmd('OUT:MAX:CURR?');
	    if ($amp > $limit)
	    {
		$self->proglog("Warning: current ($amp) above maximum allowed ($limit) setting maximum!");
		$self->writedebug("Warning: current ($amp) above maximum allowed ($limit) setting maximum!");
		$amp = $limit;
	    }
	    $limit = $self->sendcmd('OUT:MIN:CURR?');
	    if ($amp < $limit)
	    {
		$self->proglog("Warning: current ($amp) below minimum allowed ($limit) setting minimum!");
		$self->writedebug("Warning: current ($amp) below allowed ($limit) setting minimum!");
		$amp = $limit;
	    }
	    $self->sendcmd(sprintf('CURR %.5f',$amp));
	    $self->sendcmd('OUT ON');
	    if ($self->{'_RSD_device'})
	    {
		$self->{'_RSD_device'}->set(1,@_);
	    }
	}
	$self->notify('current',$c,@_);
	$self->proglog("Current for PSU ".$self->name." set to ".$amp." A",@_);
	return $amp;
    }
    return $self->sendcmd('CURR?');
}


sub set
{
    my $self = shift;
    if ($self->{'_control_mode'} && $self->{'_control_mode'} =~ /current/i)
    {
	return $self->current(@_);
    }
    else
    {
	return $self->voltage(@_);
    }
}

sub measure_U
{
    my $self = shift;
    my $data = undef;
    if (@_)
    {
        $data = $_[0];
    }
    else
    {
        $data = RFC::Cache->new();
        $data->debug($self->debug()) if ($self->debug);
    }
    $self->check_cache($data);
    my $cached = $data->get($self,'U');
    if ($cached)
    {
        $self->writedebug("Using existing data from $self voltage") if ($self->debug > 3);
        return $cached;
    }
    else
    {
        $self->writedebug("Reading voltage from $self") if ($self->debug > 3);
	my $volt = $self->sendcmd('MEAS:VOLT?');
	$self->set_cache($self,'U',$volt);
	return $volt;
    }
}

sub measure_I
{
    my $self = shift;
    my $data = undef;
    if (@_)
    {
        $data = $_[0];
    }
    else
    {
        $data = RFC::Cache->new();
        $data->debug($self->debug()) if ($self->debug);
    }
    $self->check_cache($data);
    my $cached = $data->get($self,'I');
    if ($cached)
    {
        $self->writedebug("Using existing data from $self current") if ($self->debug > 3);
        return $cached;
    }
    else
    {
        $self->writedebug("Reading current from $self") if ($self->debug > 3);
	my $curr = $self->sendcmd('MEAS:CURR?');
	$self->set_cache($self,'I',$curr);
	return $curr;
    }
}

sub idn()
{
    my $self = shift;
    return $self->sendcmd('*IDN?');
}

sub get_I
{
    my $self = shift;
    return $self->current(@_);
}

sub get_U
{
    my $self = shift;
    return $self->voltage(@_);
}

sub readstring
{
    my $self = shift;
    return undef if ($self->readstring_ignore);
    my $data = undef;
    if (@_)
    {
        $data = $_[0];
    }
    else
    {
        $data = RFC::Cache->new();
        $data->debug($self->debug()) if ($self->debug);
    }
    $self->check_cache($data);
    my @t = $self->get_time();
    my $t = join(':',@t);
    my $res = join(' ',$self->name.'_current',$t,time,$self->measure_I($data),$self->name.'_voltage',$t,time,$self->measure_U($data));
    $self->writedebug($res) if ($self->debug > 1);
    return $res;
}

sub monitor()
{
    my $self = shift;
    my $data = undef;
    if (@_)
    {
        $data = $_[0];
    }
    else
    {
	$data = RFC::Cache->new();
	$data->debug($self->debug) if ($self->debug);
    }
    $self->check_cache($data);
    my $u = $self->measure_U($data);
    $u = -32768 unless defined($u);
    my $i = $self->measure_I($data);
    $i = -32768 unless defined($i);
    return join(' ','Current',$i,'Voltage',$u);
}


package RFC::Delta;
our @ISA = qw(RFC::PSU);

sub new()
{
    my $class = shift;
    my $name = shift;
    my $rig = shift;
    my $self = $class->SUPER::new($name,$rig);
    $self->{'_type'} = 'Generic Delta';
    $self->{'_mode'} = 'Automatic';
    $self->{'_default'}{'mode'} ='Automatic';
    $self->{'_idn'} = undef;
    $self->{'_ui_fn_names'} = ['initialise']; # List of function names which can be called from the device setup UI
    $self->{'_ui_fn_desc'} = {
	'initialise' => 'Initialises the PSU. This should only be performed once after power has been restored to the PSU.',
    };
    bless $self,$class;
    return $self;
}

sub initialise()
{
    my $self = shift;
    return SocketClient::serial_client($self->{'_tty'},'delta','init',$self->{'_address'});
}

sub monitor()
{
    my $self = shift;
    my $data = undef;
    if (@_)
    {
        $data = $_[0];
    }
    else
    {
	$data = RFC::Cache->new();
	$data->debug($self->debug) if ($self->debug);
    }
    $self->check_cache($data);
    my $u = $self->measure_U($data);
    $u = -32768 unless defined($u);
    my $i = $self->measure_I($data);
    $i = -32768 unless defined($i);
    return join(' ','Current',$i,'Voltage',$u);
}


sub test()
{
    my $self = shift;
    ### make sure that backwards compatibility works ###
    return 1 unless ($self->get_version);
    my $res = SocketClient::serial_client($self->{'_tty'},'delta','idn',$self->{'_address'});
    return 1 if ($res =~ /delta/i);
}

sub test_string()
{
    my $self = shift;
    my $string = SocketClient::serial_client($self->{'_tty'},'delta','idn',$self->{'_address'});
    $string .= "\n".$self->{'_RSD_device'}->test_string(@_) if ($self->{'_RSD_device'});
    return $string;
}


sub current
{
    my $self = shift;
    if (@_)
    {
	$self->{'_I'} = 'ocv';
	my $c = shift;
	if ($c =~ /([\d\.]+)/)
	{
	    $self->{'_I'} = $1 / $self->{'_parallel'};
	    $self->{'_I'} = $self->{'_minI'} if ($self->{'_I'} < $self->{'_minI'});
	    $self->{'_I'} = $self->{'_maxI'} if ($self->{'_I'} > $self->{'_maxI'});
	}
	if ($self->{'_I'} =~ /ocv/i)
	{
	    $self->writedebug('Setting current to OCV') if ($self->debug > 1);
	    $self->RSD(0);

	}
	else
	{
	    $self->writedebug('Setting current to '.$self->{'_I'}.' A') if ($self->debug > 1);
	    $self->RSD(1);
	}
	my $res = $self->send_current;
	$self->proglog("Current for PSU ".$self->name." set to ".$self->{'_I'}." A",@_);
	$self->notify('current',$c,@_);
	return $res;
    }
    else
    {
	return $self->get_I();
    }
}


sub voltage
{
    my $self = shift;
    if (@_)
    {
	$self->{'_U'} = 'ocv';
	my $c = shift;
	if ($c =~ /([\d\.]+)/)
	{
	    $self->{'_U'} = $1 / $self->{'_serial'};;
	    $self->{'_U'} = $self->{'_minU'} if ($self->{'_U'} < $self->{'_minU'});
	    $self->{'_U'} = $self->{'_maxU'} if ($self->{'_U'} > $self->{'_maxU'});
	}
	if ($self->{'_U'} =~ /ocv/i)
	{
	    $self->writedebug('Setting voltage to OCV') if ($self->debug > 1);
	    $self->RSD(0);

	}
	else
	{
	    $self->writedebug('Setting voltage to '.$self->{'_U'}.' V') if ($self->debug > 1);
	    $self->RSD(1);
	}
	my $res = $self->send_voltage;
	$self->proglog("Voltage for PSU ".$self->name." set to ".$self->{'_U'}." V",@_);
	$self->notify('voltage',$c,@_);
	return $res;
    }
    else
    {
	return $self->get_U();
    }
}

sub read()
{
    my $self = shift;
    return $self->measure_I(@_);
}

sub readstring
{
    my $self = shift;
    return undef if ($self->readstring_ignore);
    my $data = undef;
    if (@_)
    {
        $data = $_[0];
    }
    else
    {
        $data = RFC::Cache->new();
        $data->debug($self->debug()) if ($self->debug);
    }
    $self->check_cache($data);
    my @t = $self->get_time();
    my $t = join(':',@t);
    my $res = join(' ',$self->name.'_current',$t,time,$self->measure_I($data),$self->name.'_voltage',$t,time,$self->measure_U($data));
    $self->writedebug($res) if ($self->debug > 1);
    return $res;
}

sub measure_U()
{
    my $self = shift;
    my $data = undef;
    if (@_)
    {
        $data = $_[0];
    }
    else
    {
        $data = RFC::Cache->new();
        $data->debug($self->debug()) if ($self->debug);
    }
    $self->check_cache($data);
    my $cached = $data->get($self->{'_tty'},$self->{'_address'},'U');
    return $cached if (defined($cached));
    my $val = SocketClient::serial_client($self->{'_tty'},'delta','measure_volt',$self->{'_address'});
    if (defined($val) && $val =~ /([\d\.\+\-eE]+)/)
    {
	my $val = $1;
	my $res = -32768;
	if ($val =~ /\d/)
	{
	    $res = $val * $self->{'_maxU'} * $self->{'_serial'} / 100;
	} 
	$data->set($self->{'_tty'},$self->{'_address'},'U',$res);
	return $res;
    }
    else
    {
	return -32768;	
    }
}

sub measure_I()
{
    my $self = shift;
    my $data = undef;
    if (@_)
    {
        $data = $_[0];
    }
    else
    {
        $data = RFC::Cache->new();
        $data->debug($self->debug()) if ($self->debug);
    }
    $self->check_cache($data);
    my $cached = $data->get($self->{'_tty'},$self->{'_address'},'I');
    return $cached if (defined($cached));
    my $val = SocketClient::serial_client($self->{'_tty'},'delta','measure_current',$self->{'_address'});
    if (defined($val) && $val =~ /([\d\.\+\-eE]+)/)
    {
	my $val = $1;
	my $res = -32768;
	if ($val =~ /\d/)
	{
	    $res =  $val * $self->{'_maxI'} * $self->{'_parallel'} / 100; 
	}
	$data->set($self->{'_tty'},$self->{'_address'},'I',$res);
	return $res;
    }
    else
    {
	return -32768;	
    }
}

sub idn()
{
    my $self = shift;
    return $self->{'_idn'} if (defined($self->{'_idn'}) && length($self->{'_idn'}) > 5);
    $self->{'_idn'} = SocketClient::serial_client($self->{'_tty'},'delta','idn',$self->{'_address'});
    $self->{'_idn'} = '' unless (defined($self->{'_idn'}));
    chomp  $self->{'_idn'};
    return $self->{'_idn'}."\n";
}

sub get_I()
{
    my $self = shift;
    return $self->measure_I() if ($self->idn() =~ /PSC232\(P\sCARD\)/); ## Old coms box does not support SOURCE:CURR? ##
    return $self->measure_I() if ($self->idn() =~ /PSC232\sV2.2/); ## Old coms box does not support SOURCE:CURR? ##
    my $res = SocketClient::serial_client($self->{'_tty'},'delta','raw',$self->{'_address'},'SOURCE:CURR?');
    if (defined($res) && $res =~ /([\-\+\.\d]+)/)
    {
	$self->{'_I'} = $1 * $self->{'_maxI'} * $self->{'_parallel'} / 100; 
    }
    else
    {
	$self->{'_I'} = undef;; 
    }
    return $self->{'_I'};
}

sub get_U()
{
    my $self = shift;
    return $self->measure_U() if ($self->idn() =~ /PSC232\(P\sCARD\)/); ## Old coms box does not support SOURCE:VOLT? ##
    return $self->measure_U() if ($self->idn() =~ /PSC232\sV2.2/); ## Old coms box does not support SOURCE:CURR? ##
    my $res = SocketClient::serial_client($self->{'_tty'},'delta','raw',$self->{'_address'},'SOURCE:VOLT?');
    if (defined($res) && $res =~ /([\-\+\.\d]+)/)
    {
	$self->{'_U'} = $1 * $self->{'_maxU'} * $self->{'_serial'} / 100; 
    }
    else
    {
	$self->{'_U'} = undef;; 
    }
    return $self->{'_U'};
}

sub send_voltage()
{
    my $self = shift;
    my $volt = undef;
    $volt = $self->{'_U'} * 100 / $self->{'_maxU'} if (defined($self->{'_U'}) && $self->{'_U'} =~ /\d+/);
    if (defined($volt))
    {
	$self->writedebug("Setting voltage '$volt' on ".$self->type) if ($self->debug > 3);
    }
    else
    {
	$self->writedebug("Setting OCV on ".$self->type) if ($self->debug > 3);
    }
    my $res = undef;
    if ($self->get_version)
    {
	if (!defined($volt) || $volt !~ /\d+/i)
	{
	    $res = SocketClient::serial_client($self->{'_tty'},'delta','ocv',$self->{'_address'});
	}
	else
	{
	    $res = SocketClient::serial_client($self->{'_tty'},'delta','on',$self->{'_address'});
	}
	$self->writedebug("Setting RSD status on ".$self->type.", result: $res") if ($self->debug > 3);
	$res = SocketClient::serial_client($self->{'_tty'},'delta','volt',$self->{'_address'},$volt);
    }
    else
    {
	$res = SocketClient::serial_client($self->{'_tty'},'volt',31,$self->{'_address'},$volt);
    }
    $res = '' unless defined($res);
    $self->writedebug("Result of send volt '$res'") if ($self->debug > 3);
    return $res;
}

sub send_current()
{
    my $self = shift;
    my $current = undef;
    $current = $self->{'_I'} * 100 / $self->{'_maxI'} if (defined($self->{'_I'}) && $self->{'_I'} =~ /\d+/);
    if (defined($current))
    {
	$self->writedebug("Setting current '$current' on ".$self->type) if ($self->debug > 3);
    }
    else
    {
	$self->writedebug("Setting OCV on ".$self->type) if ($self->debug > 3);
    }
    my $res = undef;
    if ($self->get_version)
    {
	if (!defined($current) || $current !~ /\d+/)
	{
	    $res = SocketClient::serial_client($self->{'_tty'},'delta','ocv',$self->{'_address'});
	}
	else
	{
	    $res = SocketClient::serial_client($self->{'_tty'},'delta','on',$self->{'_address'});
	}
	$self->writedebug("Setting RSD status on ".$self->type.", result: $res") if ($self->debug > 3);
	$res = SocketClient::serial_client($self->{'_tty'},'delta','current',$self->{'_address'},$current);
    }
    else
    {
	$res = SocketClient::serial_client($self->{'_tty'},'current',31,$self->{'_address'},$current);
    }
    $res = '' unless defined($res);
    $self->writedebug("Result of send current '$res'") if ($self->debug > 3);
    return $res;
}

#------------------------------------------------------------------
# This is a generic HMP PSU class, which can be used for any HMP PSU
#------------------------------------------------------------------
package RFC::HMP;
our @ISA = qw(RFC::PSU);

sub new()
{
    my $class = shift;
    my $name = shift;
    my $rig = shift;
    my $channel = undef;
    my $self = $class->SUPER::new($name,$rig);
    $self->{'_type'} = 'HMP';
    $self->{'_setup_tags'} = ['mode','communication','tty','channel','master_device','show_plot'];
    #$self->{'_channel'} = $self->get_conf('channel');
    $self->{'_maxI'} = 10;
    $self->{'_maxU'} = 30;
    $self->{'_minI'} = 0;
    $self->{'_minU'} = 0;
    $self->{'_mode'} = 'Automatic';
    $self->{'_default'}{'mode'} ='Automatic';
    $self->{'_idn'} = undef;
    $self->{'_ui_fn_names'} = ['initialise']; # List of function names which can be called from the device setup UI
    $self->{'_ui_fn_desc'} = {
	'initialise' => 'Initialises the PSU. This should only be performed once after power has been restored to the PSU.',
    };
    
    $self->{'_desc'} = {
	'mode' =>'Device control mode',
	'tty' => 'Communication device',
	'address' => 'Device address',
	'serial' => 'Number of devices in serial (master + slaves)',
	'parallel' => 'Number of devices in parallel (master + slaves)',
	'channel' => 'Physical channel of the device',
	'title' => 'Optional device title',
        'master_device' => 'Device name for the master device in case current device is a slave device (locked to have X percent of master device current)',
        'slave_current' => 'Percentage of master device current that this device is supposed to have',
        'slave_voltage' => 'Percentage of master device voltage that this device is supposed to have',
	'control_mode' => 'determines if the set() function should control voltage or current',
	'communication' => 'Device communication',
    };
    $self->{'_default'} = {
	'type' => 'HMP',
	'mode' =>'Manual',
	'tty' => 'ttyS0',
	'serial' => 1,
	'parallel' => 1,
	'show_plot'=>'Yes',
        'slave_current' => 100,
        'slave_voltage' => 100,
	'control_mode'=>'Current',
	'communication' => 'Serial',
        'channel' => 1,
    };
    $self->init();
    $self->{'_values_channel'} = [1,2,3,4];
    bless $self,$class;
    return $self;


}

sub initialise()
{
    my $self = shift;
    return SocketClient::serial_client($self->{'_tty'},'hmp','init');
}

sub monitor()
{
    my $self = shift;
    my $data = undef;
    if (@_)
    {
        $data = $_[0];
    }
    else
    {
	$data = RFC::Cache->new();
	$data->debug($self->debug) if ($self->debug);
    }
    $self->check_cache($data);
    my $u = $self->measure_U($data);
    $u = -32768 unless defined($u);
    my $i = $self->measure_I($data);
    $i = -32768 unless defined($i);
    return join(' ','Current',$i,'Voltage',$u);
}


sub test()
{
    my $self = shift;
    ### make sure that backwards compatibility works ###
    return 1 unless ($self->get_version);
    my $res = SocketClient::serial_client($self->{'_tty'},'hmp','idn');
    return 1 if ($res =~ /hmp/i);
}

sub test_string()
{
    my $self = shift;
    my $string = SocketClient::serial_client($self->{'_tty'},'hmp','idn');
    $string .= "\n".$self->{'_RSD_device'}->test_string(@_) if ($self->{'_RSD_device'});
    return $string;
}


sub current
{
    my $self = shift;
    if (@_)
    {
	$self->{'_I'} = 'ocv';
	my $c = shift;
	if ($c =~ /([\d\.]+)/)
	{
	    $self->{'_I'} = $1 / $self->{'_parallel'};
	    $self->{'_I'} = $self->{'_minI'} if ($self->{'_I'} < $self->{'_minI'});
	    $self->{'_I'} = $self->{'_maxI'} if ($self->{'_I'} > $self->{'_maxI'});
	}
	if ($self->{'_I'} =~ /ocv/i)
	{
	    $self->writedebug('Setting current to OCV') if ($self->debug > 1);
	    #$self->RSD(0);
        my $val = SocketClient::serial_client($self->{'_tty'},'hmp','disable',$self->{'_channel'});


	}
	else
	{
	    $self->writedebug('Setting current to '.$self->{'_I'}.' A') if ($self->debug > 1);
	    my $val = SocketClient::serial_client($self->{'_tty'},'hmp','enable',$self->{'_channel'});
	}
	my $res = $self->send_current;
	$self->proglog("Current for PSU ".$self->name." set to ".$self->{'_I'}." A",@_);
	$self->notify('current',$c,@_);
	return $res;
    }
    else
    {
	return $self->get_I();
    }
}


sub voltage
{
    my $self = shift;
    if (@_)
    {
	$self->{'_U'} = 'ocv';
	my $c = shift;
	if ($c =~ /([\d\.]+)/)
	{
	    $self->{'_U'} = $1 / $self->{'_serial'};;
	    $self->{'_U'} = $self->{'_minU'} if ($self->{'_U'} < $self->{'_minU'});
	    $self->{'_U'} = $self->{'_maxU'} if ($self->{'_U'} > $self->{'_maxU'});
	}
	if ($self->{'_U'} =~ /ocv/i)
	{
	    $self->writedebug('Setting voltage to OCV') if ($self->debug > 1);
	    my $val = SocketClient::serial_client($self->{'_tty'},'hmp','disable',$self->{'_channel'});

	}
	else
	{
	    $self->writedebug('Setting voltage to '.$self->{'_U'}.' V') if ($self->debug > 1);
	    my $val = SocketClient::serial_client($self->{'_tty'},'hmp','enable',$self->{'_channel'});
	}
	my $res = $self->send_voltage;
	$self->proglog("Voltage for PSU ".$self->name." set to ".$self->{'_U'}." V",@_);
	$self->notify('voltage',$c,@_);
	return $res;
    }
    else
    {
	return $self->get_U();
    }
}

sub read()
{
    my $self = shift;
    return $self->measure_I(@_);
}

sub readstring
{
    my $self = shift;
    return undef if ($self->readstring_ignore);
    my $data = undef;
    if (@_)
    {
        $data = $_[0];
    }
    else
    {
        $data = RFC::Cache->new();
        $data->debug($self->debug()) if ($self->debug);
    }
    $self->check_cache($data);
    my @t = $self->get_time();
    my $t = join(':',@t);
    my $res = join(' ',$self->name.'_current',$t,time,$self->measure_I($data),$self->name.'_voltage',$t,time,$self->measure_U($data));
    $self->writedebug($res) if ($self->debug > 1);
    return $res;
}

sub measure_U()
{
    my $self = shift;
    my $data = undef;
    if (@_)
    {
        $data = $_[0];
    }
    else
    {
        $data = RFC::Cache->new();
        $data->debug($self->debug()) if ($self->debug);
    }
    $self->check_cache($data);
    my $cached = $data->get($self->{'_tty'},$self->{'_address'},'U');
    return $cached if (defined($cached));
    my $val = SocketClient::serial_client($self->{'_tty'},'hmp','measure_volt',$self->{'_channel'});

    if (defined($val) && $val =~ /([\d\.\+\-eE]+)/)
    {
	my $val = $1;
	my $res = -32768;
	if ($val =~ /\d/)
	{
	    $res = $val;
	} 
	$data->set($self->{'_tty'},$self->{'_address'},'U',$res);
	return $res;
    }
    else
    {
	return -32768;	
    }
}

sub measure_I()
{
    my $self = shift;
    my $data = undef;
    if (@_)
    {
        $data = $_[0];
    }
    else
    {
        $data = RFC::Cache->new();
        $data->debug($self->debug()) if ($self->debug);
    }
    $self->check_cache($data);
    my $cached = $data->get($self->{'_tty'},$self->{'_address'},'I');
    return $cached if (defined($cached));
    my $val = SocketClient::serial_client($self->{'_tty'},'hmp','measure_current',$self->{'_channel'});
    if (defined($val) && $val =~ /([\d\.\+\-eE]+)/)
    {
	my $val = $1;
	my $res = -32768;
	if ($val =~ /\d/)
	{
	    $res =  $val; 
	}
	$data->set($self->{'_tty'},$self->{'_address'},'I',$res);
	return $res;
    }
    else
    {
	return -32768;	
    }
}

sub idn()
{
    my $self = shift;
    return $self->{'_idn'} if (defined($self->{'_idn'}) && length($self->{'_idn'}) > 5);
    $self->{'_idn'} = SocketClient::serial_client($self->{'_tty'},'hmp','idn',);
    $self->{'_idn'} = '' unless (defined($self->{'_idn'}));
    chomp  $self->{'_idn'};
    return $self->{'_idn'}."\n";
}

sub get_I()
{
    my $self = shift;
    my $res = SocketClient::serial_client($self->{'_tty'},'hmp','current_setpoint',$self->{'_channel'});
    if (defined($res) && $res =~ /([\-\+\.\d]+)/)
    {
	$self->{'_I'} = $1; 
    }
    else
    {
	$self->{'_I'} = undef;; 
    }
    return $self->{'_I'};
}

sub get_U()
{
    my $self = shift;
    my $res = SocketClient::serial_client($self->{'_tty'},'hmp','volt_setpoint',$self->{'_channel'});
    if (defined($res) && $res =~ /([\-\+\.\d]+)/)
    {
	$self->{'_U'} = $1; 
    }
    else
    {
	$self->{'_U'} = undef;; 
    }
    return $self->{'_U'};
}

sub send_voltage()
{
    my $self = shift;
    my $volt = undef;
    $volt = $self->{'_U'};
    if (defined($volt))
    {
	$self->writedebug("Setting voltage '$volt' on ".$self->type) if ($self->debug > 3);
    }
    else
    {
	$self->writedebug("Setting OCV on ".$self->type) if ($self->debug > 3);
    }
    my $res = undef;
    if ($self->get_version)
    {
	if (!defined($volt) || $volt !~ /\d+/i)
	{
	    $res = SocketClient::serial_client($self->{'_tty'},'hmp','disable',$self->{'_channel'});
	}
	else
	{
	    $res = SocketClient::serial_client($self->{'_tty'},'hmp','enable',$self->{'_channel'});
	}
	$self->writedebug("Setting RSD status on ".$self->type.", result: $res") if ($self->debug > 3);
	$res = SocketClient::serial_client($self->{'_tty'},'hmp','volt',$self->{'_channel'},$volt);
    }
    else
    {
	$res = SocketClient::serial_client($self->{'_tty'},'hmp','volt',$self->{'_channel'},$volt);
    }
    $res = '' unless defined($res);
    $self->writedebug("Result of send volt '$res'") if ($self->debug > 3);
    return $res;
}

sub send_current()
{
    my $self = shift;
    my $current = undef;
    $current = $self->{'_I'} if (defined($self->{'_I'}) && $self->{'_I'} =~ /\d+/);
    
    if (defined($current))
    {
        $self->writedebug("Setting current '$current' on ".$self->type) if ($self->debug > 3);
    }

    else
    {
	    $self->writedebug("Setting OCV on ".$self->type) if ($self->debug > 3);
    }

    my $res = undef;

    $current = '0.0' unless defined $current;
	$res = SocketClient::serial_client($self->{'_tty'},'hmp','current',$self->{'_channel'},$current);
    
    $res = '' unless defined($res);
    $self->writedebug("Result of send current '$res'") if ($self->debug > 3);
    return $res;
}

package RFC::SM_15_100;
our @ISA = qw(RFC::Delta);

sub new()
{
    my $class = shift;
    my $name = shift;
    my $rig = shift;
    my $self = $class->SUPER::new($name,$rig);
    $self->{'_type'} = 'SM-15-100';
    $self->{'_maxI'} = 100;
    $self->{'_maxU'} = 15;
    bless $self,$class;
    return $self;
}

package RFC::SM_18_50;
our @ISA = qw(RFC::Delta);

sub new()
{
    my $class = shift;
    my $name = shift;
    my $rig = shift;
    my $self = $class->SUPER::new($name,$rig);
    $self->{'_type'} = 'SM-18-50';
    $self->{'_maxI'} = 50;
    $self->{'_maxU'} = 18;
    bless $self,$class;
    return $self;
}


package RFC::SM_60_100;
our @ISA = qw(RFC::Delta);

sub new()
{
    my $class = shift;
    my $name = shift;
    my $rig = shift;
    my $self = $class->SUPER::new($name,$rig);
    $self->{'_type'} = 'SM-60-100';
    $self->{'_maxI'} = 100;
    $self->{'_maxU'} = 60;
    bless $self,$class;
    return $self;
}

package RFC::DefaultDelta;
our @ISA = qw(RFC::SM_15_100);

sub init()
{
    my $self = shift;
    foreach my $tag ($self->setup_tags)
    {
        $self->{'_'.$tag} = $self->get_default($tag);
        my $conf = $self->rig->get_cv('current',$tag);
        $self->{'_'.$tag} = $conf if (defined($conf) && $conf =~ /\w/);
    }
    $self->{'_address'} = $self->rig->get_cv('current','powersupply') unless ($self->{'_address'});
    if ($self->{'_external_RSD'} && $self->{'_external_RSD'} =~ /yes/i)
    {
	$self->{'_RSD_device'} = RFC::Device::new_RELAY($self->rig,$self->name,$self->{'_RSD_type'},$self->{'_RSD_tty'},$self->{'_RSD_address'},$self->{'_RSD_channel'});
    }
    return $self;
}


package RFC::SM_35_45;
our @ISA = qw(RFC::Delta);

sub new()
{
    my $class = shift;
    my $name = shift;
    my $rig = shift;
    my $self = $class->SUPER::new($name,$rig);
    $self->{'_type'} = 'SM-35-45';
    $self->{'_maxI'} = 45;
    $self->{'_maxU'} = 35;
    bless $self,$class;
    return $self;
}

package RFC::SM_52_30;
our @ISA = qw(RFC::Delta);

sub new()
{
    my $class = shift;
    my $name = shift;
    my $rig = shift;
    my $self = $class->SUPER::new($name,$rig);
    $self->{'_type'} = 'SM-52-30';
    $self->{'_maxI'} = 30;
    $self->{'_maxU'} = 52;
    bless $self,$class;
    return $self;
}

package RFC::SM_70_22;
our @ISA = qw(RFC::Delta);

sub new()
{
    my $class = shift;
    my $name = shift;
    my $rig = shift;
    my $self = $class->SUPER::new($name,$rig);
    $self->{'_type'} = 'SM-70-22';
    $self->{'_maxI'} = 22;
    $self->{'_maxU'} = 70;
    bless $self,$class;
    return $self;
}

package RFC::SM_120_13;
our @ISA = qw(RFC::Delta);

sub new()
{
    my $class = shift;
    my $name = shift;
    my $rig = shift;
    my $self = $class->SUPER::new($name,$rig);
    $self->{'_type'} = 'SM-120-13';
    $self->{'_maxI'} = 13;
    $self->{'_maxU'} = 120;
    bless $self,$class;
    return $self;
}

package RFC::SM_300_5;
our @ISA = qw(RFC::Delta);

sub new()
{
    my $class = shift;
    my $name = shift;
    my $rig = shift;
    my $self = $class->SUPER::new($name,$rig);
    $self->{'_type'} = 'SM-300-5';
    $self->{'_maxI'} = 300;
    $self->{'_maxU'} = 5;
    bless $self,$class;
    return $self;
}

package RFC::SM_30_200;
our @ISA = qw(RFC::Delta);

sub new()
{
    my $class = shift;
    my $name = shift;
    my $rig = shift;
    my $self = $class->SUPER::new($name,$rig);
    $self->{'_type'} = 'SM-30-200';
    $self->{'_maxI'} = 200;
    $self->{'_maxU'} = 30;
    bless $self,$class;
    return $self;
}



package RFC::DeltaES;
our @ISA = qw(RFC::Delta);

sub new()
{
    my $class = shift;
    my $name = shift;
    my $rig = shift;
    my $self = $class->SUPER::new($name,$rig);
    $self->{'_type'} = 'Generic Delta ES series';
    $self->{'_maxI'} = 0;
    $self->{'_maxU'} = 0;
    bless $self,$class;
    return $self;
}


package RFC::ES015_10;
our @ISA = qw(RFC::DeltaES);

sub new()
{
    my $class = shift;
    my $name = shift;
    my $rig = shift;
    my $self = $class->SUPER::new($name,$rig);
    $self->{'_type'} = 'ES015-10';
    $self->{'_maxI'} = 10;
    $self->{'_maxU'} = 15;
    bless $self,$class;
    return $self;
}

package RFC::ES030_5;
our @ISA = qw(RFC::DeltaES);

sub new()
{
    my $class = shift;
    my $name = shift;
    my $rig = shift;
    my $self = $class->SUPER::new($name,$rig);
    $self->{'_type'} = 'ES030-5';
    $self->{'_maxI'} = 5;
    $self->{'_maxU'} = 30;
    bless $self,$class;
    return $self;
}

package RFC::ES075_2;
our @ISA = qw(RFC::DeltaES);

sub new()
{
    my $class = shift;
    my $name = shift;
    my $rig = shift;
    my $self = $class->SUPER::new($name,$rig);
    $self->{'_type'} = 'ES075-2';
    $self->{'_maxI'} = 2;
    $self->{'_maxU'} = 75;
    bless $self,$class;
    return $self;
}

package RFC::ES0300_045;
our @ISA = qw(RFC::DeltaES);

sub new()
{
    my $class = shift;
    my $name = shift;
    my $rig = shift;
    my $self = $class->SUPER::new($name,$rig);
    $self->{'_type'} = 'ES0300-045';
    $self->{'_maxI'} = 0.45;
    $self->{'_maxU'} = 300;
    bless $self,$class;
    return $self;
}


return 1;

