package RFC::MKS;
#$Id: MKS.pm,v 1.77 2018/03/02 08:04:55 sqko Exp $

####################################################################
#
#
#     RFC::MKS
#
# This class implements RFC::MFC for MKS mass flow controlers.
# To obtain a MKS instance call one of the constructors as shown below:
#
# $id = RFC::MKS->new($name,$rig);
#
# $id = RFC::MKSReadonly->new($name,$rig);
#
# The RFC::MKS class does not define any new public member functions
#
####################################################################

use strict;
use warnings;
use RFC::Main qw();
use RFC::Header;
use RFC::MFC;
use RFC::Cache;
use RFC::Spline;
our @ISA = qw(RFC::MFC);

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->{'_mode'} = 'Automatic';
    $self->{'_type'} = 'MKS';
    $self->{'_gasses'} = ['n2'];
    $self->{'_gas'} = 'n2';
    $self->{'_accuracy'} = 0.01;
    $self->{'_gas_change'} = 'Manual';
    $self->{'_multiplexer'} = undef;
    $self->{'_values_mode'} = ['Automatic'];
    $self->{'_setup_tags_readonly'} = ['maxflow','unit','calibrated_gas'];
    $self->{'_setup_tags'} = ['mode','tty','channel','calibrated_gas','maxflow','unit','gas','relay','relay_time','relay_name','output_control_relay','control_relay_name','gas_change','gas_multiplexer','gasses','setflow_factor','read_factor','title','show_plot','use_cache','persistent_settings'];
    $self->{'_desc'} = {
        'mode' => 'Device control mode',
	'controler_mode' => 'Controler mode of operation',
        'tty' => 'Communication device (ex. ttyS0)',
        'channel' => 'Device address (ex 021), NB must be 3 digits! Note that address 254 and 255 are broadcast addresses (all MKS units will listen on those, but will only respond on 254)!',
        'calibrated_gas' => 'The gas for which the maxflow is specified on the controler (usually nitrogen)',
        'maxflow' => 'Maximum flowrate for the selected gas (Note in L/hour!)',
        'unit' => 'Flow unit in which the controler reports the gas flow. MB This may be different than the unit the maxflow is given in!',
        'gas' => 'selected gas',
        'gas_change' => 'Gas change mode',
        'gas_multiplexer' => 'Name of gas multiplexer device if any',
        'gasses' => 'Avaliable gasses, Must contain a comma separated list of gas names (device name!) that the MFC can be used to control (in case of no multiplexer, just the single gas connected).',
        'title' => 'Optional title',
        'relay' => 'Bypass relay avaliable',
        'relay_name' => 'Bypass relay name',
        'relay_time' => 'bypass relay engage time (seconds)',
        'output_control_relay' => 'Switch controling if an external output control device is fitted (for instance a magnetic valve for forcing complete cutoff of gas)',
        'control_relay_name' => 'Cutoff relay name',
        'setflow_factor' => 'Callibration factor for settig flow',
        'read_factor' => 'Callibration factor for reading flow',
        'use_cache'=>'Determines if persisten caching of read values is allowed',
	'persistent_settings' => 'Determines wether or not settings such as callibrated gas should be cached (speeding up normal use) or queried each time'
    };
    $self->{'_default'} = {
        'mode' => 'Automatic',
	'controler_mode' => 'Flowrate',
        'channel' => '001',
        'tty' => 'ttyS0',
        'calibrated_gas' => 'n2',
        'calibrated_maxflow' => 100,
        'gas' => 'n2',
        'gasses' => 'n2',
        'gas_change' => 'Manual',
        'relay' => 'NO',
        'relay_name' => '',
        'relay_time' => 0,
        'control_relay_name' => '',
        'output_control_relay' => 'NO',
        'show_plot'=>'Yes',
        'setflow_factor' => 1,
        'read_factor' => 1,
        'use_cache' => 'No',
        'persistent_settings' => 'No',
    };
    $self->{'_status'} = {
	'C' => 'Valve closed',
	'CR' => 'Callibration recommended',
	'E' => 'System error',
	'H' => 'High alarm condition',
	'HH' => 'High-High alarm condition',
	'IP' => 'Insufficient inlet gas pressure',
	'L' => 'Low alarm condition',
	'LL' => 'Low-Low alarm codition',
	'M' => 'Memory (EEPROM) failure',
	'O' => 'OK, normal operation',
	'OC' => 'Unexpected change in operating conditions',
	'P' => 'Purge',
	'T' => 'Over temperature',
	'U' => 'Uncallibrated',
	'V' => 'Valve drive level alarm condition',
	'' => 'No response recieved from device!',
    };
    $self->{'_errcode'} = {
	'01' => 'Checksum error',
	'10' => 'Syntax error',
	'11' => 'Data length error',
	'12' => 'Invalid data',
	'13' => 'Invalid operating mode',
	'14' => 'Invalid action',
	'15' => 'Invalid gas',
	'16' => 'Invalid control mode',
	'17' => 'Invalid command',
	'24' => 'Callibration error',
	'25' => 'Flow too large',
	'27' => 'Too many gasses in tas table',
	'28' => 'Flow cal error; valve not open',
	'98' => 'Internal device error 98',
	'99' => 'Internal device error 99',
    };
    $self->{'_values_controler_mode'} = ['Flowrate'];
    $self->{'_ui_fn_names'} = ['query_settings'];
    $self->{'_ui_fn_desc'} = {
        'query_settings' => 'Get and store the settings which can be queried from the controler',
    };
    bless $self,$class;
    $self->init;
    $self->{'_controler_mode'} = 'Flowrate';
    return $self;
}


sub query_settings
{
    my $self = shift;
    my $u = '';
    my @list = ();
    $self->rig->begin();
    eval {
	### Get unit ##
	$u = $self->send('U?');
	CORE::die "Could not get unit from controler ".$self->name."\n" unless ($u);
	$u = 'L/min' unless ($u);
	$self->change_cv('unit',$u);
	push @list,"Unit: $u";
	### Get calibrated_gas ###
	my $nr = 0;
	$nr = $self->send('SGN?');
	$nr = $self->send('SGN?') unless ($nr);
	CORE::die "Could not get calibrated gas from controler ".$self->name."\n" unless ($nr);
	my $gas = $self->{'_gas_list'}{$nr};
	$self->change_cv('calibrated_gas',$gas);
	push @list,"Calibrated gas: ".$gas;
	### Get maxflow ###
	my $flow = $self->send('FS?');
	$flow = $self->send('FS?') unless ($flow);
	if ($u && $u eq 'SCCM')
	{
	    $flow = $flow * 60 / 1000;	
	}
	else
	{
	    $flow = $flow * 60;
	}
	CORE::die "Could not get maxflow from controler ".$self->name."\n" unless ($flow);
	$self->change_cv('maxflow',$flow);
	push @list,"Calibrated_maxflow: $flow";
        my $gas_ok = 0;
        my @l = RFC::Main::get_gasses();
        foreach my $g (@l)
        {
            next unless ($g eq $gas);
            $gas_ok = 1;
            last;
        }
	CORE::die("Gas $gas does not match any of the known gasses (".join(',',@list).")!\n") unless ($gas_ok);
	$self->rig->commit();
	push @list,'RELOAD';
    };
    if ($@)
    {
	$self->rig->rollback();
	push @list,'Error: '.$@;
    }
    return join("\n",@list);
}

sub print_config()
{
    my $self = shift;
    my @arr = ();
    push @arr,'Name: '.$self->name;
    push @arr,'MFC type: '.$self->type;
    push @arr,'Mode: '.$self->mode;
    push @arr,'Maxflow: '.$self->maxflow;
    if ($self->{'_relay'} && ref($self->{'_relay'}))
    {
        push @arr,$self->{'_relay'}->print_config;
        push @arr,'Relay_time: '.$self->{'_relay_time'};
    }
    if ($self->{'_control_relay'})
    {
        push @arr,$self->{'_control_relay'}->print_config;
    }
    if ($self->{'_multiplexer'})
    {
        push @arr,$self->{'_multiplexer'}->print_config;
    }
    return join (', ',@arr);
}

sub readonly_value($)
{
    my $self = shift;
    my $tag = shift;
    return undef unless ($tag);
    return $self->maxflow() if ($tag eq 'maxflow');
    return $self->cal_gas() if ($tag eq 'calibrated_gas');
    return $self->get_unit() if ($tag eq 'unit');
    return undef;
}

sub get_unit()
{
    my $self = shift;
    return $self->{'_unit'} if ($self->persistent && $self->get_conf('unit'));
    my $u = '';
    eval {
	$u = $self->send('U?');
    };
    $u = 'L/min' unless ($u);
    return $u;
}

sub get_status()
{
    my $self = shift;
    my $res = $self->send('T?');
    $res = '' unless ($res);
    return $self-> {'_status'}{uc($res)};
}


sub cal_gas()
{
    my $self = shift;
    if ($self->persistent && $self->get_conf('calibrated_gas'))
    {
	$self->{'_cal_gas'} = $self->get_conf('calibrated_gas'); 
	return $self->{'_cal_gas'};
    }
    unless ($self->{'_cal_gas'})
    {
	my $nr = 0;
	eval {
	    $nr = $self->send('SGN?');
	    $nr = $self->send('SGN?') unless ($nr);
	};
	if ($nr)
	{
	    $self->{'_cal_gas'} = $self->{'_gas_list'}{$nr};
	}
	else
	{
	    $self->{'_cal_gas'} = 'n2';
	    my $str = "Warning: Could not get name for calibrated gas for MKS controler ".$self->{'_channel'}.", no reply recieved, Using n2 as default!";
	    $self->rig->warning_mail($self,$str);

	}
    }
    my $gas_ok = 0;
    my @list = RFC::Main::get_gasses();
    foreach my $g (@list)
    {
	next unless (defined($g) && defined($self->{'_cal_gas'}) && $g eq $self->{'_cal_gas'});
	$gas_ok = 1;
	last;
    }
    ## Fallback to n2 if reported calibrated gas is not in list ###
    $self->{'_cal_gas'} = 'n2' unless ($gas_ok);
    return $self->{'_cal_gas'};
}

sub send()
{
    my $self = shift;
    my $cmd = shift;
    my $str = SocketClient::serial_client_raw($self->{'_tty'},'mks',$self->{'_channel'},$cmd);
    $self->writedebug("Result of command '$cmd' on address '".$self->{'_channel'}."': '$str'") if ($self->debug);
    if ($str && $str =~ /ACK([\w\s\,\.\+\-]+)\;/)
    {
	return $1;
    }
    elsif ($str && $str =~ /NAK(\d+)\;/)
    {
	my $errstr = $self->{'_errcode'}{$1};
	$self->writedebug("Error on command '$cmd': '$errstr'",1);
	$self->rig->errorlog("Error: MFC device '".$self->name."' on command '$cmd': '$errstr!'");
	return undef;
    }
    return undef;
}

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

sub read_nocache
{
    my $self = shift;
    my $flow = $self->flow(@_);
    my $str = time.' MFC_'.$self->name.' '.$flow;
    $self->rig->kanaldata($str) unless ($self->readstring_ignore);
    if ($self->{_multiplexer})
    { 
        my $mgas = $self->{'_multiplexer'}->gas_name(@_);
        unless ($mgas && $mgas eq $self->{'_gas'})
        {
            my $str = "Warning, Gas multiplexer not engaged as expected (wrong gas or no gas selected), gas flow possibly wrong! Setting flow to 0!";
            $self->writedebug($str);
            $flow = 0;
        }
    }
    $self->{'_last_read'} = Time::HiRes::time;
    return $flow;
}

sub flow()
{
    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('MKS',$self->name);
    if (defined($cached))
    {
	$self->writedebug("Using already cached flow value for MFC ".$self->name) if ($self->debug);
	return $cached;
    }
    my $flow = undef;
    eval {
	$flow = $self->send('F?');
	$flow = $self->send('F?') unless (defined $flow);
    };
    if (defined $flow)
    {
        $flow = $flow * $self->{'_read_factor'} * $self->maxflow() / 100;
    }
    else
    {
	$flow = -32768;
    }
    $data->set('MKS',$self->name,$flow);
    return $flow;
}


sub maxflow()
{
    my $self = shift;
    unless ($self->{'_maxflow'})
    {
	my $flow = 0;
	if ($self->persistent && $self->get_conf('maxflow'))
	{
	    $flow = $self->get_conf('maxflow');
	}
	else
	{
	    my $u = $self->get_unit(); 
	    eval {
		$flow = $self->send('FS?');
		$flow = $self->send('FS?') unless ($flow);
	    };
	    unless ($flow)
	    {
		my $str = "Warning: Could not get maxflow for MKS controler ".$self->{'_channel'}.", no reply recieved, Setting flow to 0!";
		$self->rig->warning_mail($self,$str);
		$flow = 0 
		}
	    if ($u && $u eq 'SCCM')
	    {
		$flow = $flow * 60 / 1000;	
	    }
	    else
	    {
		$flow = $flow * 60;
	    }
	}
	### Rescale for gasfactors if gas not equal to calibrated gas
	my $cfactor = RFC::Main::get_cv('gas',$self->cal_gas());
	my $factor = RFC::Main::get_cv('gas',$self->{'_gas'});
	eval {
	    my $d = $self->rig->get_device('gas',$self->{'_gas'});
	    $d = $self->rig->init_device('gas',$self->{'_gas'}) unless ($d);
	    $factor = RFC::Main::get_cv('gas',$d->gas());
	};
	if ($@)
	{
	    my $str = $@;
	    $self->rig->warning_mail($self,$str);
	    $self->writedebug($str);
	}
	if ($self->multiplexer)
	{
	    ### Check gas factor for selected gas if multiplexer has changed! ###
	    my $dev = $self->multiplexer();
	    $dev = $self->rig->get_device('multiplex',$self->multiplexer) unless (ref($dev) =~ /RFC::Multiplex/);
	    if ($dev)
	    {
		if ($dev->gas)
		{
		    $factor = RFC::Main::get_cv('gas',$dev->gas);
		    unless ($factor)
		    {
			$self->writedebug("Warning, no gas factor was found for gas '".$dev->gas."' reported by multiplexer '".$dev->name."', Setting gas factor to default gas for controler: '".$self->{'_gas'}."'");
		    }
		}
		else
		{
		    $self->writedebug("Warning, no gas was reported by multiplexer '".$dev->name."', Setting gas factor to default gas for controler: '".$self->{'_gas'}."'");
		}
		$factor = RFC::Main::get_cv('gas',$self->{'_gas'}) unless ($factor);
	    }
	}
	### Catch any divide by 0 error.. ###
	$self->die('No gas factor found for gas '.$self->{'_gas'}.'!') unless $factor;
	$self->die('No gas factor found for gas '.$self->{'_calibrated_gas'}.'!') unless $cfactor;
	$flow = $flow * $factor / $cfactor;
	$self->{'_maxflow'} = $flow;
    }
    return $self->{'_maxflow'};
}


sub send_flow($)
{
    my $self = shift;
    my $flow = shift;
    ### Send flow as percent of maxflow ###
    my $maxflow = $self->maxflow();
    if ($maxflow)
    {
	$self->send(sprintf("S!%.5f",$flow * 100 / $maxflow));
    }
    else
    {
	my $str = "Warning: Could not get maxflow for MKS controler ".$self->{'_channel'}.", no reply recieved, Not Setting flow!";
	$self->rig->warning_mail($self,$str);
    }
}

sub setflow
{
    my $self = shift;
    my $flow = shift;
    $flow = $flow * $self->{'_setflow_factor'};
    $flow = 0 unless (defined($flow));
    my $gdev = $self->rig->get_device('gas',$self->{'_gas'});
    my $cutoff = $gdev->cutoff_set;
    $cutoff = 0 unless (defined($cutoff) && $cutoff > 0);
    my $maxflow = $self->maxflow;
    $flow = $maxflow if ($flow > $maxflow);
    ### MKS flowcontrolers have default cutoff of 2 percent! ###
    my $mincut = $self->maxflow() / 50;
    $cutoff = $mincut if ($cutoff < $mincut);
    my $vo = 0;
    if ($flow < $cutoff)
    {
	$self->proglog("Gas flow for ".$self->{'_gas'}." is to be set below cutoff ($cutoff L/hour), setting 0 L/hour!",@_) unless ($flow == 0);
        $flow = 0;
        $vo = 2;
    }
    my $oldflow = $self->read;
    eval {
        if ($self->{'_relay'} && ref($self->{'_relay'}))
        {
            if ($flow && $oldflow < $cutoff)
            {
                $self->writedebug("Setting bypass relay for ".$self->{'_gas'}." to 1");
                $self->{'_relay'}->set(1,@_);
                sleep(1);
                if ($self->{'_control_relay'} && ref($self->{'_control_relay'}))
                {
                    my $status = 1;
                    $self->writedebug("Setting control relay for ".$self->{'_gas'}." to $status");
                    $self->{'_control_relay'}->set($status);
                }
                sleep(1);
                $self->writedebug("Setting flow to $flow for gas ".$self->{'_gas'}." using MFC ".$self->{'_channel'}." on tty ".$self->{'_tty'});
                $self->send('VO!NORMAL');
		$self->send_flow($flow);
                sleep($self->{'_relay_time'}-1);
                $self->writedebug("Setting bypass relay for ".$self->{'_gas'}." to 0");
                $self->{'_relay'}->set(0,@_);
            }
	}
	my $status = 0;
	if ($flow > 0)
	{
	    $status = 1;
	    $self->send('VO!NORMAL');
	}
	else
	{
	    $self->send('VO!FLOW_OFF');
	}
	if ($self->{'_control_relay'} && ref($self->{'_control_relay'}) && $self->{'_control_relay'}->read() != $status)
	{
	    $self->writedebug("Setting control relay for ".$self->{'_gas'}." to $status");
	    $self->{'_control_relay'}->set($status,@_);
	}
	$self->writedebug("Setting flow to $flow for gas ".$self->{'_gas'}." using MFC ".$self->{'_channel'}." on tty ".$self->{'_tty'});
	$self->send_flow($flow);
	$self->rig->unset_warning($self) if ($maxflow);
    };
    if ($@)
    {
        $self->rig->errorlog("Error: Gas flow for controler ".$self->name." (".$self->{'_gas'}.") could not be set to $flow! Error: ".$@,@_);
        return undef;
    }
    $self->proglog("Flow for gas ".$self->{'_gas'}." set to $flow L/hour",@_);
    $self->notify('setflow',$flow,@_);
    return $flow;
}


sub test()
{
    my $self = shift;
    my $fs = $self->send('SN?');
    return 0 unless ($fs);
    if ($self->{'_relay'} && ref($self->{'_relay'}))
    {
        return 0 unless( $self->{'_relay'}->test());
    }
    if ($self->{'_control_relay'})
    {
        return 0 unless($self->{'_control_relay'}->test());
    }
    if ($self->{'_multiplexer'})
    {
        return 0 unless($self->{'_multiplexer'}->test());
    }
    return 1;
}

sub test_string()
{
    my $self = shift;
    my $string = $self->send('SN?');
    $string = '' unless defined($string);
    return "Init failed for MFC ".$self->name().". Returned string: '$string'\n" unless ($string);
    my $flow = $self->read(@_);
    $string .= "\nFlow for controler ".$self->name." was $flow L/hour\n";
    $string .= $self->get_status()."\n";
    $string .= $self->{'_relay'}->test_string(@_)."\n" if (exists($self->{'_relay'}) && $self->{'_relay'} && ref($self->{'_relay'}));
    $string .= $self->{'_control_relay'}->test_string(@_)."\n" if (exists($self->{'_control_relay'}) && $self->{'_control_relay'});
    $string .= $self->{'_multiplexer'}->test_string(@_)."\n" if (exists($self->{'_multiplexer'}) && $self->{'_multiplexer'});
    return $string;
}

sub init()
{
    my $self = shift;
    my @hide_tags = ();
    $self->load_conf();
    $self->{'_default'}{'gasses'} = $self->{'_gas'};
    foreach my $tag ('gasses')
    {
        $self->{'_'.$tag} = $self->get_default($tag);
        my $conf = $self->get_conf($tag);
        $self->{'_'.$tag} = $conf if (defined($conf) && $conf =~ /\w/);
    }
    my $gasstr = $self->{'_gasses'};
    my @gasses = split(/\s*\,\s*/,$gasstr);
    $self->{'_gasses'} = \@gasses;
    my $relay = $self->get_conf('relay');
    if ($relay && $relay =~ /yes/i && $self->{'_relay_name'})
    {
	my $dev = $self->rig->get_device('relay',$self->{'_relay_name'});
	unless ($dev)
	{
	    $dev = $self->rig->init_device('relay',$self->{'_relay_name'});
	    $dev->readstring_ignore(1) if ($dev);
	}
	$self->die("No relay named '".$self->{'_relay_name'}."' returned by rig ".$self->rig->name) unless ($dev);
	$self->{'_relay'} = $dev;
    }
    else
    {
        push @hide_tags,'relay_name';
        $self->{'_relay'} = undef;
    }
    $relay = $self->get_conf('output_control_relay');
    if ($relay && $relay =~ /yes/i && $self->{'_control_relay_name'})
    {
        my $dev = $self->rig->get_device('relay',$self->{'_control_relay_name'});
        unless ($dev)
        {
            $dev = $self->rig->init_device('relay',$self->{'_control_relay_name'});
            $dev->readstring_ignore(1) if ($dev);
        }
        $self->die("No relay named '".$self->{'_output_relay_name'}."' returned by rig ".$self->rig->name) if (!$dev && $self->{'_output_relay_name'});
        $self->{'_control_relay'} = $dev;
    }
    else
    {
        push @hide_tags,'control_relay_name';
        $self->{'_control_relay'} = undef;
    }
    $self->{'_gas_change'} = $self->get_conf('gas_change');
    if ($self->{'_gas_change'} && $self->{'_gas_change'} =~ /auto/i)
    {
        ### Register Multiplexer from rig if present ###
        my $mult = $self->get_conf('gas_multiplexer');      
        $self->die("No multiplexer name specified for MFC ".$self->name()) unless ($mult);
	my $device = $self->rig->get_device('multiplex',$mult);
	unless ($device)
	{
	    $device = $self->rig->init_device('multiplex',$mult);
	    $device->readstring_ignore(1) if ($device);
	}
        $self->die("No multiplexer device returned by rig ".$self->rig->name." for MFC ".$self->name()) unless ($device);
	$self->{'_multiplexer'} = $device->attatch_controler($self);
    }
    $self->load_gas_list();
    $self->{'_maxflow'} = 0;
    $self->{'_hide_tags'} = \@hide_tags;
    return $self;
}



sub load_gas_list()
{
    my $self = shift;
    ### Loads all known gases based on list supplied in 'G-Series RS485 Supplement.pdf' from MKS, Appendix C table C1  ### 

    my $str = <<END;
2,2-Dichloro-1,1,1-Trifluoro-ethane C2HCl2F3 186
4-Methyl-1-Pentene C6H12 178
Acetone C3H6O 184
Acetonitrile C2H3N 173
Acetylene C2H2 42
Air Air 8
Allene C3H4 66
Ammonia NH3 29
Argon Ar 4
ArsenicPentafluoride AsF5 96
Arsine AsH3 35
Boron Tribromide BBr3 79
Boron Trichloride BCl3 70
Boron Trifluoride BF3 48
Bromine Br2 21
Bromine Pentafluoride BrF5 116
Bromine Trifluoride BrF3 76
Bromotrifluoroethylene C2BrF3 105
Bromotrifluoromethane CBrF3 80
Butadiene C4H6 100
Butane C4H10 117
Butene C4H8 104
Carbon Dioxide CO2 25
Carbon Disulfide CS2 40
Carbon Monoxide CO 9
Carbon Tetrachloride CCl4 101
Carbon Tetrafluoride CF4 63
Carbonyl Fluoride CF2O 46
Carbonyl Sulfide COS 34
Chlorine Cl2 19
Chlorine Trifluoride ClF3 77
Chlorobenzene C6H5Cl 172
Chlorodifluoromethane CHClF2 57
Chloroform CHCl3 71
Chloropentafluoroethane C2ClF5 119
Chlorotrifluoromethane CClF3 74
Cisbutene C4H8 107
Cyanogen C2N2 59
Cyanogen Chloride ClCN 37
Cyclopropane C3H6 61
Deuterium D2 14
Diborane B2H6 58
Dibromotetrafluoroethane C2Br2F4 130
Dichlorodifluoromethane CCl2F2 84
Dichlorofluoromethane CHCl2F 65
Dichlorosilane SiH2Cl2 67
Dichlorotetrafluoroethane C2Cl2F4 125
Diethysilane C4H12Si 154
Difluorochloroethane C2H3ClF2 103
Difluoroethane C2H4F2 82
Difluoroethylene C2H2F2 64
Difluorosilane SiH2F2 134
Difluoromethane CH2F2 160
Dimethyl Ether C2H6O 73
Dimethylaluminumhydride C2H7Al 164
Dimethylamine C2H7N 85
Dimethylethylaminealane C4H14NAl 166
Dimethylpropane C5H12 122
Dimethylzinc C2H6Zn 135
Disilane Si2H6 97
Ethane C2H6 54
Ethanol C2H6O 136
Ethyl Acetylene C4H6 93
Ethyl Chloride C2H5Cl 75
Ethylbenzene C8H10 174
Ethylene C2H4 38
Ethylene Oxide C2H4O 45
Ethyleneglycol C2H6O2 
Fluorine F2 18
Fluoroform CHF3 49
Germane GeH4 43
Germanium Tetrachloride GeCl4 113
Germanium Tetrafluoride GeF4 99
Halothane C2HBrClF3 137
Helium He 1
Hexafluorethane C2F6 118
Hexafluoropropylene C3F6 138
Hexamethyldisilane C6H18Si2 139
Hexane C6H14 127
Hexanediol-1,6 C6H14O2 170
Hydrazine N2H4 50
Hydrocyanic Acid CHN 175
Hydrogen H2 7
Hydrogen Bromide HBr 10
Hydrogen Chloride HCl 11
Hydrogen Cyanide HCN 24
Hydrogen Fluoride HF 12
Hydrogen Iodide HI 17
Hydrogen Selenide H2Se 23
Hydrogen Sulfide H2S 22
Iodine Pentafluoride IF5 115
Isobutane C4H10 111
Isobutene C4H8 106
Krypton Kr 5
Methane CH4 28
Methanol CH4O 176
Methyl Acetylene C3H4 68
Methyl Bromide CH3Br 44
Methyl Chloride CH3Cl 36
Methyl Fluoride CH3F 33
Methyl Mercaptan CH4S 47
Methyl Vinyl Ether C3H6O 81
Methylamine CH5N 52
Methylbutene C5H10 120
Methylcyclohexane C7H14 177
Methylsilane CH6Si 185
Methyltrichlorosilane CH3Cl3Si 183
Molybdenum Hexafluoride MoF6 124
Neon Ne 2
Nickel Carbonyl NiC4O4 140
Nitric Acid HNO3 167
Nitric Oxide NO 16
Nitrogen N2 13
Nitrogen Dioxide NO2 26
Nitrogen Tetroxide N2O4 95
Nitrogen Trifluoride NF3 53
Nitrogen Trioxide N2O3 78
Nitrosyl Chloride NOCl 141
Nitrous Oxide N2O 27
o-Xylene C8H10 179
Octafluorocyclobutane C4F8 129
Oxygen O2 15
Oxygen Difluoride OF2 41
Ozone O3 30
Pentaborane B5H9 142
Pentafluorethane C2HF5 155
Perchloryl Fluoride ClO3F 72
Perfluoropropane C3F8 128
Phenol C6H6O 180
Phosgene CCl2O 60
Phosphine PH3 31
Phosphorous Oxychloride POCl3 102
Phosphorous Pentafluoride PF5 143
Phosphorous Trifluoride PF3 62
Propane C3H8 89
Propylene C3H6 69
Radon Rn 3
Silane SiH4 39
Silicon Tetrachloride SiCl4 108
Silicon Tetrafluoride SiF4 88
Sulfur Dioxide SO2 32
Sulfur Hexafluoride SF6 110
Sulfur Tetrafluoride SF4 86
Sulfuric Acid H2SO4 171
Sulfuryl Fluoride SO2F2 87
Tertiarybutylarsine C4H11As 161
Tertiarybutylphosphine C4H11P 162
Tetrachloroethylene C2Cl4 168
Tetraethoxysilane C8H20O4Si 144
Tetrafluorohydrazine N2F4 157
Tetrafluoroethylene C2F4 94
Tetrafluoroethane C2H2F4 156
Tetrahydrofuran C4H8O 182
Tetramethylcyclotetra-siloxane C4H16Si4O4 158
Tin Tetrachloride SnCl4 145
Titanium Tetrachloride TiCl4 114
Toluene C7H8 181
Transbutene C4H8 98
Tribromomethane CHBr3 83
Tributylaluminum C12H27Al 146
Trichloroethane C2H3Cl3 112
Trichlorofluoromethane CCl3F 91
Trichlorosilane SiHCl3 147
Trichlorotrifluoroethane C2Cl3F3 126
Triethylborate C6H15O3B 163
Triethylgallium C6H15Ga 148
Trimethoxyborine C3H9BO3 131
Trimethylaluminum C3H9Al 149
Trimethylamine C3H9N 109
Trimethylamineallane C3H12AlN 165
Trimethylantimony C3H9Sb 150
Trimethylarsenic C3H9As 151
Trimethylgallium C3H9Ga 152
Trimethylindium C3H9In 153
Trimethylphosphite C3H9PO3 133
Trimethylphosphorous C3H9P 132
Tritium T2 159
Tungsten Hexafluoride WF6 121
Uranium Hexafluoride UF6 123
Vinyl Bromide C2H3Br 56
Vinyl Chloride C2H3Cl 55
Vinyl Fluoride C2H3F 51
Water Vapor H2O 20
Xenon Xe 6

END

foreach my $item (split(/\n/,$str))
{
    next unless ($item =~ /\w+/);
    my @l = split(/\s+/,$item);
    next unless (@l > 2);
    my $nr = pop @l;
    my $abr = pop @l;
    $self->{'_gas_list'}{$nr} = lc($abr);
    $self->{'_gas_list_name'}{$nr} = join(' ',@l);
}
}



package RFC::MKSReadonly;
our @ISA = qw(RFC::MKS);

sub new()
{
    my $class = shift;
    my $self = $class->SUPER::new(@_);
    $self->{'_readonly'} = 1;
    $self->{'_type'} = 'MKSReadonly';
    $self->{'_setup_tags_readonly'} = ['maxflow','unit','calibrated_gas'];
    $self->{'_setup_tags'} = ['mode','tty','channel','calibrated_gas','maxflow','unit','gas','gas_change','gas_multiplexer','gasses','setflow_factor','read_factor','title','show_plot','use_cache','persistent_settings'];
    $self->{'_relay'} = undef;
    $self->{'_control_relay'} = undef;
    bless $self,$class;
    return $self;
}


return 1;

