package RFC::Gas;
#$Id: Gas.pm,v 1.148 2023/02/07 12:21:17 sqko Exp $

#######################################################################
#
#
#      RFC::Gas
#
#
#  This calss defines the behaviour of gas flow logging. 
#  To obtain a gas instance call the constructor as described below:
#  my $id = RFC::Gas->new($name,$rig)
#  The rig argument must be a RFC::Rig instance or a similar object honoring
#  the get_cv() member function.
#
#
#  All Gas objects have the following member functions (excluding those
#  derived from RFC::BaseDevce and Debug.pm):
#
#
# $id->setflow($[$][$]) Sets the gas flow for the gas to the specified
#                       value (note all gas flows for RFC devices must
#                       be specified in nL/hour).
#                       If an additional argument is specified, it is assumed to be
#                       the username of the controlling user and that name is appended
#                       to the string appended to the rigs proglog.
#                       an exception to this is if a further argument is specified in 
#                       which case no proglog entry is appended! 
# $id->cutoff_set()     Returns the minimum flow for the gas below which
#                       the gas flow is defined to be 0. Somne controlers require special
#                       commands for completely closing which is the reason
#                       for this to be implemented.
# $id->cutoff_report()  Returns the minimum flow for the gas below which
#                       the gas flow is defined to be 0 even if the controler may
#                       report a positive value. 
#                       In case of a manual gas, this value can be set freely, 
#                       however in case of a gas controlled by a MFC or similar,
#                       the default mimimum value is 0.75 times the accuracy of of the
#                       control device (it can be manually overridden to a higher
#                       value).
# $id->maxflow()        Returns the maximum flow rate possible.
# $id->maxflow_set()    Returns the maximum allowable flow rate.
# $id->accuracy()       Returns the device accuracy (usually forwarded from the
#                       controling device e.g MFC).
# $id->control_device() Returns the device instance of the control relay (if any).
# $id->control_value()  Returns the control value. this is to be compared to the 
#                       read response from the control device to determine if the
#                       gas is engaged or not (used in fast switching applications
#                       where a cross-over valve determines if gas is supplied to
#                       the device under test or not).
# $id->controler()      Returns the device instance used for controling the gas 
#                       flow rate. Usually an instance of the RFC::MFC class.
# $id->attatch_multiplex($)
#                       Attatches a multiplexer device (of type RFC::Multiplex)
#                       to the gas device. The multiplex device is used for 
#                       determining if the gas flow reported by teh controler
#                       is for this gas.
# $id->gas()            Returns the name of the gas controlled by this device.
# $id->read_cutoff([]$) Returns the gas flow similar to read(), but if the flow is 
#                       below the cutoff_report value, it returns 0.
# $id->set($[$][$])     Alias for the setflow function.
# $id->setpoint()       Returns the last set setpoint for the gas (in case of manual 
#                       gasses this will be the same as a normal read).
# $id->is_pressure()    Returns 1 if the gas device is configured to be automatically 
#                       controlled by a pressure controler. Returns 0 otherwise.
# $id->include_tally()  Returns true if the gas line can be used to totalise gas usage
#                       
#
#
# The RFC::Gas module overloads the bool() function to return 1 (ture) only if the actual 
# flow is above the cutoff_report() value (the bool() returns 0 (false) otherwise.
#
# The RFC::Gas module also allows slaving of one gas to an other. A slaved gas is in the
# setup specicfied to have a flow which is proportional to the flow rate of the master
# gas with the proportionality facter defined in the setup. To facility this, the
# RFC::Gas slave device attatches itself to the master device durring initialisation and 
# uses the observer interface derived from BaseDevice to set the flow to the correct value
# whenever the flow of the master device is changed. This is achieved by overloading the 
# observe() function.
#
# As a safety precaution, a gas can not be a slave of an other slaveed gas.
# This is to ensure that no infinite loops are created. To facilitate this, the following member
# function is alos defined:
#
# $id->master()         Returns the device instance of the master gas device. returns undef
#                       if the gas device is a master, thus is only true for slave devices.
#
#######################################################################

use strict;
use warnings;
use RFC::Header;
use RFC::BaseDevice;
use RFC::Cache;
our @ISA = qw(RFC::BaseDevice);
my $cf = RFC::Main::config();

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'} = 'Gas';
    $self->{'_tty'} = 'ttyS0';
    $self->{'_setup_tags'} = ['class','gas','mode','controler','maxflow','maxflow_set','cutoff_set','cutoff_report','link','title','show_plot','control_name','control_value','master_device','slave_flow','use_cache','include_tally'];
    $self->{'_reload_tags'} = ['class','gas','mode','controler'];
    $self->{'_desc'} = {
	'class' => 'Device type',
	'gas' => 'Name of gas controlled by this device. Only rarely should this be set to anything other than the device name! If set to something else, problems may arise with gas multiplexer control if the control name (name of gas to be controled) is not the same as the eventuel gas name (intermediate device names may dffer however!)',
        'mode' => 'Device control mode',
	'maxflow' => 'Maximum flow rate (Note L/hour), only used for manual gasses',
	'controler' => 'Name of controler device (if any)',
	'maxflow_set' => 'Maximum allowable flow rate (l/hour)',
	'cutoff_set' => 'Cutoff flow rate (force close if set below this value)',
	'cutoff_report' => 'Cutoff flow rate for report generation, flows below this level is treated as 0',
	'link' => "Optional gas device name for linking purposes, If a gas (parent) links to one or more other gas devices (childs) and the flow for one of the child devices is above the cutoff value, the flow rate reported by the parent device is set to 0 irrespectively of actual / assumed flow rate.\nNotice that by using links it is possible to configure circular references which must be awoided!",
	'control_value' => 'Setting used for controling enabled/disabled status for gasses combined in a gas group',
	'title' => 'Optional title',
	'control_name' => 'Name of control valve device (relay) if any, used mainly if gas is part of a gas group',
	'control_value' => 'Control valve relay status for allowing flow, used mainly if gas is part of a gas group',
	'master_device' => 'Device name for the master device in case current device is a slave device (locked to have X percent of master device flow rate)',
	'slave_flow' => 'Percentage of master device flow rate that current device is supposed to have',
        'use_cache'=>'Determines if persisten caching of read values is allowed',
	'include_tally' => 'Indicates if the flow of this gas is to be included in any taly of gas use',
    };
    $self->{'_default'} = {
	'class' => 'Normal',
        'mode' => 'Manual',
	'maxflow' => 1,
	'controler' => 0,
	'maxflow_set' => 0,
	'cutoff_set' => 0,
	'cutoff_report' => 0,
	'link' => '',
	'control_value' => 0,
	'slave_flow' => 100,
        'use_cache'=>'No',
	'include_tally' => 'Yes',
    };
    bless $self,$class;
    $self->{'_html_template'} = 'Gas.tt';
    $self->{'_set_fn_names'} = ['setflow','set']; # List of function names which can trigger a notify event
    $self->{'_section'} = 'gas_'.$self->name;
    $self->{'_read_names'} = ['read','setpoint'];
    $self->{'_set_names'} = ['setflow','set'];
    $self->{'_values_class'} = [(RFC::Device::list_GAS())];
    $self->{'_values_gas'} = [$cf->get_keys('gas')];
    $self->{'_values_mode'} = ['Manual','Automatic'];
    $self->{'_values_include_tally'} = ['Yes','No'];
    $self->{'_values_controler'} = [0,($self->rig->avaliable_devices('MFC'))];
    $self->{'_values_control_name'} = ['',$self->rig->avaliable_devices('relay')];
    my @list = ();
    foreach my $n ($self->rig->avaliable_devices('gas'))
    {
	push @list,$n unless ($n eq $self->name);
    }
    $self->{'_values_master_device'} = ['',@list];
    $self->{'_file'} = SemaforeFile->new($userdir.'rig'.$self->rig->name.'/gas_'.$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');
	chmod 0666, $self->{'_file'}->filename;
	$self->{'_file'}->unlock;
    }
    $self->{'_linkdevices'} = [];
    return $self->init;
}

sub include_tally()
{
    my $self = shift;
    return 0 if ($self->{'_include_tally'} && $self->{'_include_tally'} =~ /no/i);
    return 1;
}

sub DESTROY
{
    my $self = shift;
    $self->{'_file'}->DESTROY() if($self->{'_file'});
    return undef;
}

sub setpoint()
{
    my $self = shift;
    my $val = ($self->{'_file'}->readlines())[0];
    return $val if (defined($val) && $val =~ /\d/);
    return $self->read();
}

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

sub gas()
{
    my $self = shift;
    return $self->{'_gas'};
}

sub accuracy()
{
    my $self = shift;
    if ($self->controler)
    {
	return $self->{'_controler'}->accuracy(@_);
    }
    return 0;
}

sub maxflow()
{
    my $self = shift;
    if ($self->controler)
    {
	return $self->{'_controler'}->maxflow(@_);
    }
    return $self->{'_maxflow'};
}

sub maxflow_set()
{
    my $self = shift;
    return $self->{'_maxflow_set'} if ($self->{'_maxflow_set'} && $self->{'_maxflow_set'} < $self->maxflow);
    return $self->maxflow;
}

sub control_device()
{
    my $self = shift;
    return $self->{'_control_device'};
}

sub control_value()
{
    my $self = shift;
    return $self->{'_control_value'};
}

sub controler()
{
    my $self = shift;
    return $self->{'_controler'};
}

sub attatch_multiplex($)
{
    my $self = shift;
    $self->die("Error: More than one gas multiplexer was attempted to be attatched to gas $self") if ($self->{'_multiplex'});
    $self->{'_multiplex'} = shift;
}

sub Accept
{
    my $self = shift;
    my $visitor = shift;
    return undef if ($self->{'_seen_visitors'}{$visitor});
    if ($self->mode =~ /auto/i)
    {
	$self->{'_controler'}->Accept($visitor);
	$visitor->VisitComplex($self);
    }
    else
    {
	$visitor->VisitSimple($self);
    }
    $self->{'_seen_visitors'}{$visitor} = 1;
    return undef;
}

sub test()
{
    my $self = shift;
    if ($self->mode =~ /auto/i)
    {
	return $self->{'_controler'}->test;
    }
    else
    {
	eval {
	    my @lines = $self->{'_file'}->readlines;
	    my $flow = 0;
	    if (@lines)
	    {
		$flow = $lines[0] 
	    }	    
	};
	if ($@)
	{
	    return 0;
	}
	return 1;
    }
}

sub test_string()
{
    my $self = shift;
    my $string = 'Gas '.$self->name.' Flow: '.$self->read(@_)."\n";
    if ($self->mode =~ /auto/i)
    {
	$string .= "$self is autmoatically controlled from:\n".$self->{'_controler'}->test_string(@_);
    }
    else
    {
	$string .= "$self is manually controlled\n";
    }
    $string .= $self->{'_control_device'}->test_string(@_) if ($self->{'_control_device'});
    if ($self->{'_link'})
    {
	$string .= "Link information:\n";
	$self->load_link unless (@{$self->{'_linkdevices'}});
	foreach my $dev (@{$self->{'_linkdevices'}})
	{
	    $string .= $dev->test_string();
	}
    }    
    return $string;
}

sub cutoff_set()
{
    my $self = shift;
    if ($self->controler)
    {
	my $contr = $self->{'_controler'}->accuracy(@_);
	return $self->{'_cutoff_set'} if ($self->{'_cutoff_set'} > $contr);
	return $contr;
    }
    return $self->{'_cutoff_set'};
}

sub cutoff_report()
{
    my $self = shift;
    return $self->{'_cutoff_report'};
}

sub read_cutoff
{
    my $self = shift;
    my $flow = $self->read(@_);
    $flow = 0 if ($self->cutoff_report() && $flow < $self->cutoff_report());
    return $flow;
}

sub bool
{
    my $self = shift;
    my $flow = $self->read(@_);
    return 0 if ($flow <= 0 || ($self->cutoff_report() && $flow < $self->cutoff_report()));
    return 1;
}

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/);
    }
    unless ($self->{'_gas'})
    {
	foreach ($cf->get_keys('gas'))
	{
	    if ($self->name eq $_)
	    {
		$self->{'_gas'} = $_;
		$self->{'_default'}{'gas'} = $_;
		last;
	    }
	}
	unless ($self->{'_gas'})
	{
	    $self->{'_gas'} = 'n2';
	    $self->{'_default'}{'gas'} = 'n2';
	}
    }
    if ($self->{'_mode'} =~ /auto/i || $self->{'_controler'})
    {
	### Register MFC from rig ###
	my $contr = $self->rig->get_device('MFC',$self->{'_controler'});
	unless ($contr)
	{
	    $contr = $self->rig->init_device('MFC',$self->{'_controler'});
	    $contr->readstring_ignore(1);
	}
	unless ($contr)
	{
	    $contr = RFC::Device::new_MFC($self->rig,$self->{'_controler'});
	    $self->rig->register_device('MFC',$contr);
	    $contr->readstring_ignore(1);
	}
	$self->die("No controler device returned for controler: ".$self->{'_controler'}) unless ($contr);
	push @{$self->{'_setup_tags_readonly'}},'maxflow';
	$self->{'_controler'} = $contr;
	$self->{'_readonly'} = 1 if ($contr->readonly);
	my $dev = $contr->multiplexer();
	$self->{'_multiplex'} = $dev if ($dev);
    }
    $self->{'_cutoff_set'} = 0 unless ($self->{'_cutoff_set'} && $self->{'_cutoff_set'} > 0);
    $self->{'_control_device'} = $self->rig->relay($self->{'_control_name'});
    $self->{'_mdev'} = undef;
    if ($self->{'_master_device'})
    {
	my $dev = $self->rig->init_device('gas',$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 ###
    }
    $self->{'_setup_tags_readonly'} = ['maxflow'] if ($self->{'_controler'});
    return $self;
}

sub setup_tags()
{
    my $self = shift;
    ### Fix problem with deep recursion as device accuracy may not be avaliable before complete rig initialisation is over ###
    ### Furthermore, if a tag is readonly or not only makes a difference for the UI....                                    ###
    $self->{'_setup_tags_readonly'} = ['maxflow','cutoff_set'] if ($self->{'_controler'} && $self->{'_controler'}->accuracy());
    return $self->SUPER::setup_tags();
}

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

sub master()
{
    my $self = shift;
    return $self->{'_mdev'};
}

sub debug()
{
    my $self = shift;
    $self->SUPER::debug(@_);
    if (@_)
    {
        #### Pass on debug level to all objects honoring this ###
        $self->{'_file'}->debug($self->{'_debug'});
    }
    return $self->{'_debug'};
}


sub is_pressure()
{
    my $self = shift;
    if ($self->{'_mode'} =~ /auto/i )
    {
	return 1 if ($self->{'_controler'} && $self->{'_controler'}->controler_mode() =~ /Pressure/i);
    }
    return 0;
}

sub setflow()
{
    my $self = shift;
    my $flow = shift;
    ### Reinitialise instance in case configuration has changed ###
    $self->init();
    if ($self->is_pressure)
    {
	if ($flow > $self->maxflow_set)
	{
	    $self->writedebug("Pressure to be set for $self was above maximum, setting maximum pressure");
	    $self->proglog("Pressure to be set for $self was above maximum, setting maximum pressure",@_);
	    $flow = $self->maxflow_set;
	}
	if ($flow < $self->{'_controler'}->minP)
	{
	    $flow = $self->{'_controler'}->minP;
	    $self->proglog("Pressure to be set for $self was above minimum, setting minimum pressure",@_);
	}
    }
    else
    {
	if ($flow > $self->maxflow_set)
	{
	    $self->writedebug("Flow to be set for $self was above maximum, setting maximum flow");
	    $self->proglog("Flow to be set for $self was above maximum, setting maximum flow",@_);
	    $flow = $self->maxflow_set;
	}
    }
    $self->writedebug("Setting flow for $self to $flow");
    if ($self->{'_mode'} =~ /auto/i )
    {
	if (!$self->is_pressure && $self->cutoff_set && $flow < $self->cutoff_set)
	{
	    $self->writedebug("Flow to be set for $self was below cutoff (".$self->cutoff_set." L/h), setting 0 L/h");
	    $self->proglog("Flow for $self was to be set to below the cutoff limit (".$self->cutoff_set." L/h), setting 0 L/hour",@_) unless ($flow == 0);
	    $flow = 0;
	}
	### Set setpoint in file for later read by setpoint() ###
	$self->{'_file'}->writeline(sprintf("%.4f",$flow));
	$flow = $self->{'_controler'}->setflow($flow,@_);
	return undef unless (defined($flow));  ## If controler could not set flow, use undefined to indicate error ###
    }
    else
    {
	$self->{'_file'}->writeline(sprintf("%.4f",$flow));
	$self->proglog("Flow for $self set to $flow L/hour manually",@_);
    }
    my @response = $self->notify('setflow',$flow,@_);
    return $flow unless (@response);
    my $str = $flow.': Slave devices set to: '.join(', ',@response).' L/hour respectively';
    return $str;
}

sub set
{
    my $self = shift;
    my $flow = $self->setflow(@_);
    ### Make sure that the 'set' command is passed on (althiough the setflow command passes a setflow command on as well)
    my @response = $self->notify('set',@_);
    return $flow.join(', ',@response);
}

sub readstring()
{
    my $self = shift;
    return undef if ($self->readstring_ignore);
    my $res = undef;
    my $data = undef;
    if (@_)
    {
        $data = $_[0];
    }
    else
    {
	$data = RFC::Cache->new();
	$data->debug($self->debug) if ($self->debug);
    }
    $self->check_cache($data);
    if ($self->{'_mode'} =~ /auto/i || $self->{'_multiplex'})
    {
	my $str = $self->{'_controler'}->readstring($data);
	if ($str)
	{
            my @l = split(/\s+/,$str);
            my $dev = $self->{'_multiplex'};
            my $flow = 0;
            if ($self->{'_controler'}->read_raw($data) > 0 && $dev && $dev->gas_name ne $self->name)
            {
                $res = join(' ',$self->name,$l[1],$l[2],'0.0000');
            }
            else
            {
                $flow = $self->{'_controler'}->read_raw($data);
                $res = join(' ',$self->name,$l[1],$l[2],sprintf("%.5f",$flow));
            }
	    if ($self->{'_link'})
	    {
		$self->load_link unless (@{$self->{'_linkdevices'}});
		foreach my $dev (@{$self->{'_linkdevices'}})
		{
		    my $cutoff = $dev->cutoff_set($data);
		    my $linkflow = $dev->read($data);
		    $flow = 0 if ($linkflow > $cutoff);
		}
	    }
            my $str = time .' '.$self->name.' '.$flow;
            $self->rig->kanaldata($str) unless ($self->readstring_ignore);
	}
	elsif(defined($self->{'_controler'}->read($data)))
	{
            my @t = $self->get_time();
            my $t = join(':',@t);
            my $flow = 0;
            my $dev = $self->{'_multiplex'};
            if ($self->{'_controler'}->read_raw($data) > 0 && $dev && (!$dev->gas_name() || $dev->gas_name() ne $self->name))
            {
                $res = join(' ',$self->name,$t,time,'0.0000');
            }
            else
            {
                $flow = $self->{'_controler'}->read_raw($data);
                $res = join(' ',$self->name,$t,time,sprintf("%.5f",$flow));
            }
	    if ($self->{'_link'})
	    {
		$self->load_link unless (@{$self->{'_linkdevices'}});
		foreach my $dev (@{$self->{'_linkdevices'}})
		{
		    my $cutoff = $dev->cutoff_set($data);
		    my $linkflow = $dev->read($data);
		    $flow = 0 if ($linkflow > $cutoff);
		}
	    }
            my $str = time .' '.$self->name.' '.$flow;
            $self->rig->kanaldata($str) unless ($self->readstring_ignore);
	}
	else
	{
	    my @t = $self->get_time();
	    my $t = join(':',@t);
            my $str = time .' '.$self->name.' -32768';
            $self->rig->kanaldata($str) unless ($self->readstring_ignore);
            $res = join(' ',$self->name,$t,time,-32768);
	}
    }
    else
    { 
	my @t = $self->get_time();
	my $t = join(':',@t);
	my @lines = $self->{'_file'}->readlines;
	my $val = 0;
	$val = $lines[0] if (@lines && $lines[0] =~ /\d/);
	if ($self->{'_link'})
	{
	    $self->load_link unless (@{$self->{'_linkdevices'}});
	    foreach my $dev (@{$self->{'_linkdevices'}})
	    {
		my $cutoff = $dev->cutoff_set($data);
		my $linkflow = $dev->read($data);
		$val = 0 if ($linkflow > $cutoff);
	    }
	}
        my $str = time .' '.$self->name.' '.$val;
        $self->rig->kanaldata($str) unless ($self->readstring_ignore);
	$val = -32768 unless (defined( $val) && $val =~ /\d/);
	$res = join(' ',$self->name,$t,time,sprintf("%.5f",$val));
    }
    $self->writedebug($res) if ($self->debug > 1);
    return $res;
}

sub read_nocache
{
    my $self = shift;
    my $flow = 0;
    my $data = undef;
    if (@_)
    {
        $data = $_[0];
    }
    else
    {
	$data = RFC::Cache->new();
	$data->debug($self->debug) if ($self->debug);
    }
    $self->check_cache($data);
    if ($self->{'_mode'} =~ /auto/i || $self->{'_multiplex'})
    {
        ### Use read raw and do multiplexer control check locally ###
        $flow = $self->{'_controler'}->read_raw($data);
    }
    else
    {
	$data->debug($self->debug()) if ($self->debug);
	my $cached = $data->get('manual_gas',$self->name);
	if (defined($cached))
	{
	    $self->writedebug("Using already cached flow value for $self") if ($self->debug);
	    $flow = $cached;
	}
	else
	{
	    my @lines = $self->{'_file'}->readlines;
	    $flow = $lines[0] if (@lines);
	    $data->set('manual_gas',$self->name,$flow);
	}
    }
    if ($self->{'_link'})
    {
        $self->load_link unless (@{$self->{'_linkdevices'}});
        foreach my $dev (@{$self->{'_linkdevices'}})
        {
            my $cutoff = $dev->cutoff_set;
            my $linkflow = $dev->read($data);
            $flow = 0 if ($linkflow > $cutoff);
        }
    }
    my $dev = $self->{'_multiplex'};
    if ($dev && $dev->gas($data) && $dev->gas_name($data) ne $self->name)
    {
        $flow = 0;
    }
    my $str = time .' '.$self->name.' '.$flow;
    $self->rig->kanaldata($str) unless ($self->readstring_ignore);
    $self->writedebug("Flow for $self was $flow");
    $self->set_cache($flow);
    $self->{'_last_read'} = Time::HiRes::time;
    return $flow;
}


sub print_config()
{
    my $self = shift;
    my @arr = ();
    push @arr,'Name: '.$self->name;
    push @arr,'Mode: '.$self->mode;
    if ($self->mode =~ /auto/i)
    {
	push @arr,$self->{'_controler'}->print_config;
    }
    push @arr,$self->{'_multiplex'}->print_config if ($self->{'_multiplex'});
    return join (', ',@arr);
}

sub load_link
{
    my $self = shift;
    if ($self->{'_link'})
    {
	my @list = $self->get_conf('link');
	foreach my $name (@list)
	{
	    my $dev = $self->rig->get_device('gas',$name);
	    unless ($dev)
	    {
		$dev = RFC::Device::new_GAS($self->rig,$dev);
		$self->rig->register_device('gas',$dev) if ($dev);
	    }
	    $self->die("Error: Could not get device for link gas $name!") unless ($dev);
	    push @{$self->{'_linkdevices'}},$dev;
	}
    }
}

return 1;
