package RFC::Water;
#$Id: Water.pm,v 1.67 2023/02/08 14:34:27 sqko Exp $

###########################################################################
#
#
#     RFC::Water
#
# This class manages a water bubler used to moisten a gas by bubbling it 
# thorugh water.
# To obtain an instance call the constructor as ahown below:
#
# $id = RFC::Water->new($name,$rig)
#
# Apart from the functions defined in RFC::BaseDevice and Debug.pm, the class 
# defines the follwoing member functions:
#
# $id->setstatus($)         Sets the status of the gas stream (that is is the gas
#                           going through the bubler or not ).
# $id->readstatus()         Returns the status of the control relay device
#                           It behaves similar as the read() function in that it
#                           accepts an optional argumen (see the definition of 
#                           read() ).
#
############################################################################


use strict;
use warnings;
use RFC::Header;
use RFC::Main qw();
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 $name = shift;
    my $rig = shift;
    my $self = $class->SUPER::new($name,$rig);
    bless $self,$class;
    $self->{'_html_template'} = 'Water.tt';
    $self->{'_section'} = 'water_'.$self->name;
    $self->{'_title'} = $self->get_conf('title');
    $self->{'_mode'} = $self->get_conf('mode');
    $self->{'_mode'} = 'Manual' unless ($self->{'_mode'});
    $self->{'_setup_tags'} = ['mode','channel_name','gas_stream','type','lead_res_name','lead_res_value','control_relay_name','show_plot','use_cache'];
    $self->{'_legacy_tags'} = ['mode','channel_name','channel','channel_input_type','channel_tty','channel_address','gas_stream','type','lead_res_name','lead_res','lead_res_type','lead_res_tty','lead_res_address','lead_res_value','control_relay_name','control_type','tty','control_address','control_channel','show_plot','use_cache'];
    $self->{'_reload_tags'} = ['class','type','mode','channel_name','lead_res_name','control_relay_name','channel_input_type','lead_res_type','control_type'];
    $self->{'_desc'} = {
        'mode' => 'Device control mode',
	'gas_stream' => 'Name (label) of gas stream the device is attatched to, Used only for display / UI purposes',
	'channel_name' => 'Input device name, may be either a simple channel name or a temperature log device name (temperature log device recomended, will be prefixed with "Tlog_" in list)',
        'channel' => 'Input channel (if no channel name)',
	'channel_input_type' => 'Input channel type (if no channel name)',
	'channel_tty' => 'Input communication device (ex. ttyS0), used only if channel type is not Keithley',
	'channel_address' => 'Input device address (if input type is not Keithley)',
	'type' => 'Device type',
	'lead_res_name' => 'Name of lead resistance input device',
	'lead_res' => 'Lead resistance', 
	'lead_res_type' =>  'Lead resistance input type (if no lead resistance name)',
	'lead_res_tty' =>  'Lead resistance communication device (If no lead resistance name and lead resistance type is not Keithley)',
	'lead_res_address' => 'Lead resistance device address (If no lead resistance name and lead resistance type is not Keithley)',
	'lead_res_value' => 'Value (if fixed) for lead resistance',
	'control_relay_name' => 'Device name for control relay (Used for controling if Water bubler device is enabled or bypassed)',
	'control_type' => 'Device type for control relay (if no control relay name)',
	'tty' => 'Serial device for contro relay communication (ex. ttyS0)',
	'control_address' => 'Control relay device address, only used if no control relay name',
	'control_channel' => 'Control relay device channel, only used if no control relay name',
	'title' => 'Optional device title',
        'use_cache'=>'Determines if persisten caching of read values is allowed',
    };
    $self->{'_default'} = {
        'mode' => 'Manual',
        'channel' => '1:101',
	'type' => 'pt100',
	'channel_input_type' => 'Keithley',
	'channel_tty' => 'ttyS0',
	'channel_address' => 1,
	'channel_' => '1:101',
	'lead_res_type' =>  'Fixed_value',
	'lead_res' =>  '1:101',
	'lead_res_value' => 0,
	'lead_res_tty' => 'ttyS0',
	'lead_res_address' => 1,
	'control_type' => 'ICP',
	'control_address' => 1,
	'control_channel' => 0,
        'show_plot'=>'Yes',
        'use_cache'=>'No',
    };
    my @hide_tags;
    $self->{'_set_fn_names'} = ['setstatus']; # List of function names which can trigger a notify event
    $self->{'_values_type'} = ['pt100','pt1000'];
    $self->{'_values_channel_input_type'} = [(RFC::Device::list_SimpleChannel())];
    $self->{'_values_lead_res_type'} = ['fixed_value',(RFC::Device::list_SimpleChannel())];
    $self->{'_values_lead_res_name'} = [('',$self->{'_rig'}->avaliable_devices('simplechannel'))];
    $self->{'_values_channel_name'} = [('',$self->{'_rig'}->avaliable_devices('simplechannel'))];
    foreach my $n ($self->{'_rig'}->avaliable_devices('templog'))
    {
	push @{$self->{'_values_channel_name'}}, 'Tlog_'.$n;
    }
    $self->{'_values_control_relay_name'} = [('',$self->{'_rig'}->avaliable_devices('relay'))];
    $self->{'_values_control_type'} = [(RFC::Device::list_RELAY())];
    foreach my $tag ($self->setup_tags_legacy())
    {
        $self->{'_'.$tag} = $self->get_default($tag);
        my $conf = $self->get_conf($tag);
        $self->{'_'.$tag} = $conf if (defined($conf) && $conf =~ /\w/);
    }
    $self->{'_channel_type'} = $self->{'_type'};
    $self->{'_type'} = 'Humidifier';
    $self->{'_channel_device'} = undef;
    $self->{'_lead_res_device'} = undef;
    if ($self->{'_channel_name'} && $self->{'_channel_name'} =~ /Tlog_(\w+)/)
    {
	my $name = $1;
	my $dev = $self->rig->init_device('templog',$name);
	$self->die("No temperature log device with name $name was found durring initialisation of device $self") unless ($dev);
	$self->{'_channel_device'} = $dev;
	push @hide_tags,'channel_input_type','channel_address','channel_tty','lead_res_name','lead_resistance','lead_res_type','lead_res_value';
    }
    else
    {
	my $dev = $self->rig->get_device('simplechannel',$self->{'_channel_name'});
	unless ($dev || !$self->{'_channel_name'})
	{
	    $dev = $self->rig->init_device('simplechannel',$self->{'_channel_name'});
	    $dev->readstring_ignore(1) if ($dev);
	    push @hide_tags,'channel_input_type','channel_tty','channel_address';
	}
	$self->die("Error: No simplechannel device with name '".$self->{'_channel_name'}."' returned from rig ".$self->rig->name) if (!$dev && $self->{'_channel_name'});
	my $devname = 'measure_channel_'.$self->name;
	$dev = $self->rig->get_device('simplechannel',$devname) unless ($dev);
	unless ($dev)
	{
	    if ($self->{'_channel_input_type'} !~ /Keithley/i)
	    {
		my $tty = $self->get_conf('channel_tty');
		my $addr = $self->get_conf('channel_address');
		$dev = RFC::Device::new_SimpleChannel($self->rig,$devname,$self->{'_channel_input_type'},$tty,$addr,$self->{'_channel'});
	    }
	    else
	    {
		push @hide_tags,'channel_tty';
		$dev = RFC::Device::new_SimpleChannel($self->rig,$devname,$self->{'_channel_input_type'},$self->{'_channel'});
	    }
	    $dev->readstring_ignore(1);
	    $self->rig->register_device('simplechannel',$dev);
	}
	$self->{'_channel_device'} = $dev;
	$dev = undef;
	$dev = $self->rig->get_device('simplechannel',$self->{'_lead_res_name'});
	unless ($dev || !$self->{'_lead_res_name'})
	{
	    $dev = $self->rig->init_device('simplechannel',$self->{'_lead_res_name'});
	    $dev->readstring_ignore(1) if ($dev);
	    push @hide_tags,'lead_res_type','lead_res_tty','lead_res_address','lead_res_value';
	}
	$self->die("Error: No simplechannel device with name '".$self->{'_lead_res_name'}."' returned from rig ".$self->rig->name) if (!$dev && $self->{'_lead_res_name'});
	$devname = 'lead_resistance_'.$self->name;
	$dev = $self->rig->get_device('simplechannel',$devname) unless ($dev);
	unless ($dev  || $self->{'_lead_res_type'} =~ /fixed_value/i)
	{
	    if ($self->{'_lead_res_type'} !~ /Keithley/i)
	    {
		my $tty = $self->get_conf('lead_res_tty');
		my $addr = $self->get_conf('lead_res_address');
		$dev = RFC::Device::new_SimpleChannel($self->rig,$devname,$self->{'_lead_res_type'},$tty,$addr,$self->{'_lead_res'});
	    }
	    else
	    {
		push @hide_tags,'lead_res_tty';
		$dev = RFC::Device::new_SimpleChannel($self->rig,$devname,$self->{'_lead_res_type'},$self->{'_lead_res'});
	    }
	    $dev->readstring_ignore(1);
	    $self->rig->register_device('simplechannel',$dev);
	}
	$self->{'_lead_res_device'} = $dev;
	push @hide_tags,'lead_res_value';
    }
    $self->{'_control_device'} = undef;
    if ($self->mode =~ /auto/i)
    {
	my $dev = $self->rig->get_device('relay',$self->{'_control_relay_name'});
	push @hide_tags,'control_type','tty','control_address','control_channel' if ($self->{'_control_relay_name'});
	unless ($dev || !$self->{'_control_relay_name'})
	{
	    $dev = $self->rig->init_device('relay',$self->{'_control_relay_name'});
	    $dev->readstring_ignore(1) if ($dev);
	}
	$self->die("Error: No relay device with name '".$self->{'_control_relay_name'}."' returned from rig ".$self->rig->name) if (!$dev && $self->{'_control_relay_name'});
	my $devname = $self->name.'_control';
	$dev = $self->rig->get_device('relay',$devname) unless ($dev);
	unless ($dev)
	{
	    my $type = $self->get_conf('control_type');
	    $type = 'ICP' unless $type;
	    $self->{'_tty'} = $self->get_conf('tty');
	    $self->{'_address'} = $self->get_conf('control_address');
	    $self->{'_control_channel'} = $self->get_conf('control_channel');
	    $dev = RFC::Device::new_RELAY($self->rig,$devname,$type,$self->{'_tty'},$self->{'_address'},$self->{'_control_channel'});
	    $self->rig->register_device('relay',$dev);
	    $dev->readstring_ignore(1) if ($dev);
	}
	$self->{'_control_device'} = $dev;
    }
    else
    {
	my $type = 'ManualRelay';
	my $devname = $self->name.'_enabled';
	my $dev = RFC::Device::new_RELAY($self->rig,$devname,$type);
	$self->rig->register_device('relay',$dev);
	$dev->readstring_ignore(1) if ($dev);
	$self->{'_control_device'} = $dev;
    }
    $self->{'_hide_tags'} = \@hide_tags;
    return $self;
}

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

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

sub Accept
{
    my $self = shift;
    my $visitor= shift;
    return undef if ($self->{'_seen_visitors'}{$visitor});
    $self->{'_channel_device'}->Accept($visitor);
    $self->{'_lead_res_device'}->Accept($visitor) if ($self->{'_lead_res_device'});
    $self->{'_control_device'}->Accept($visitor) if ($self->{'_control_device'});
    $visitor->VisitComplex($self);
    $self->{'_seen_visitors'}{$visitor} = 1;
    return undef;
}

sub test_string()
{
    my $self = shift;
    my $data = RFC::Cache->new();
    my $string = 'Water bubler '.$self->name.' temperature is '.$self->read($data).'C'."\n";
    $string .= 'Raw resistance: '.$self->{'_channel_device'}->test_string($data);
    $string .= 'Lead resistance: '.$self->{'_lead_res_device'}->test_string($data) if ($self->{'_lead_res_device'});
    $string .= 'Control device: '.$self->{'_control_device'}->test_string($data) if ($self->{'_control_device'});
    return $string."\n";
}

sub readstring()
{
    my $self = shift;
    return undef if ($self->readstring_ignore);
    ### Only works with data structure as argument,        ###
    ### so if no is specified, invent a dummy and use that ###
    my $data = undef;
    if (@_)
    {
        $data = $_[0];
    }
    else
    {
        $data = RFC::Cache->new();
        $data->debug($self->debug()) if ($self->debug);
    }
    $self->check_cache($data);
    my $ignore = $self->{'_channel_device'}->readstring_ignore;
    $self->{'_channel_device'}->readstring_ignore(0);
    my $str = $self->{'_channel_device'}->readstring($data);
    $self->{'_channel_device'}->readstring_ignore($ignore);
    my @l = split(/\s+/,$str);
    my $res = join(' ',$self->name,$l[1],$l[2],sprintf("%.3f",$self->read($data)),$self->name.'_enabled',$l[1],$l[2],$self->readstatus($data));
    $self->writedebug($res) if ($self->debug > 2);
    return $res;
}

sub read_nocache
{
    my $self = shift;
    my $temp = -32768;
    if (ref($self->{'_channel_device'}) =~ /Templog/)
    {
	$temp = $self->{'_channel_device'}->read(@_);
    }
    else
    {
	my $res;
	my $lead_res = $self->{'_lead_res_value'};
	$lead_res = $self->{'_lead_res_device'}->read(@_) if ($self->{'_lead_res_device'});
	my $deltaohm = $self->{'_channel_device'}->read(@_) - $lead_res;
	$deltaohm = $deltaohm / 10.0 if ($self->{'_channel_type'} eq 'pt1000');
        $self->writedebug("PT-100 corrected for ".$self->name." was: $deltaohm") if ($self->debug > 2);
	$temp = RFC::Main::spline('pt100-res-to-temp',$deltaohm);
	$temp = -32768 unless ($temp);
    }
    $self->writedebug("Temperature for ".$self->name." was $temp") if ($self->debug > 2);
    my $str = time .' '.$self->name.' '.$temp;
    $self->rig->kanaldata($str) unless ($self->readstring_ignore);
    $self->set_cache($temp);
    $self->{'_last_read'} = Time::HiRes::time;
    return $temp;
}

sub setstatus()
{
    my $self = shift;
    my $status = shift;
    $self->writedebug("Setting status for $self to $status") if ($self->debug > 2);
    if (@_)
    {
	return $self->{'_control_device'}->set($status,@_);
    }
    else
    {
	return $self->{'_control_device'}->set($status);
    }
    $self->notify('set',$status,@_);
}

sub readstatus()
{
    my $self = shift;
    my $res = undef;
    if (@_)
    {
	$res = $self->{'_control_device'}->read(@_);
    }
    else
    {
	$res = $self->{'_control_device'}->read();
    }
    $self->writedebug("Status for $self read to $res") if ($self->debug > 2);
    return $res;
}


sub print_config()
{
    my $self = shift;
    my @arr = ();
    push @arr,'Type: '.$self->type;
    push @arr,'Name: '.$self->name;
    push @arr,$self->{'_channel_device'}->print_config;
    if ($self->{'_lead_res_device'})
    {
	push @arr,$self->{'_lead_res_device'}->print_config;
    }
    else
    {
	push @arr,'Lead resistance value: '.$self->{'_lead_res_value'};
    }
    push @arr,'Resistance type: '.$self->{'_channel_type'};
    if ($self->mode =~ /auto/i)
    {
	push @arr,$self->{'_control_device'}->print_config;
    }
    return join(', ',@arr);
}
