#!/usr/bin/perl -w
# NAME: fgtelnet.pl
# AIM: Get a TELNET connection to fgfs on host port
use strict;
use warnings;
use Time::HiRes qw( gettimeofday tv_interval );
use Time::gmtime;
use IO::Socket;
use Math::Trig;     # atan() etc
use Term::ReadKey;

my $os = $^O;
my $perl_dir = '/home/geoff/bin';
my $PATH_SEP = '/';
if ($os =~ /win/i) {
    $perl_dir = 'C:\GTools\perl';
    $PATH_SEP = "\\";
}
unshift(@INC, $perl_dir);
require 'lib_utils.pl' or die "Unable to load 'lib_utils.pl'! Check location and \@INC content.\n";
require 'fg_wsg84.pl' or die "Unable to load 'fg_wsg84.pl' ...\n";
require 'Bucket2.pm' or die "Unable to load 'Bucket2.pm' ...\n";

### runtime
my $HOST = "192.168.1.105";
#my $HOST = "localhost";
my $PORT = 5555;
my $TIMEOUT = 3;  # seconds to wait for a connect.
my ($FGFS_IO);
my $send_run_exit = 0;
###my $load_log = 0;
my $set_bin_mode = 0;   # tried this, but seemed no to do anything
my $add_metar = 1;      # can NOT seem to get metar string
my $try_metar_delay = 0;
my $try_metar_repeat = 0;   # this BLOCKED ;=((
my $try_metar_second = 0;

# DEBUG ONLY STUFF
my @intervals = ();
my @warnings = ();

END {
    if (defined $FGFS_IO) {
        prt("WARNING: End with socket open... closing connection...\n");
        fgfs_send("run exit") if ($send_run_exit);
        close $FGFS_IO;
        undef $FGFS_IO;
    }
    #prt("END\n");
    ReadMode(0);    # restore normal keyboard mode
}

### constants
my $SGD_PI = 3.1415926535;
my $SGD_DEGREES_TO_RADIANS = $SGD_PI / 180.0;
my $SGD_RADIANS_TO_DEGREES = 180.0 / $SGD_PI;
my $DEF_GS = 3;
my $ATAN3 = atan( $DEF_GS * $SGD_DEGREES_TO_RADIANS );
# /** Feet to Meters */
my $SG_FEET_TO_METER = 0.3048;
# /** Meters to Feet */
my $SG_METER_TO_FEET = 3.28083989501312335958;
my $SG_NM_TO_METER = 1852;
my $SG_METER_TO_NM = 0.0005399568034557235;

# locations
my $a_gil_lat = -31.697287500;
my $a_gil_lon = 148.636942500;
my $a_dub_lat = -32.2174865;
my $a_dub_lon = 148.57727;
my $targ_ygil = 1;

my $last_show_time = 0;
my $begin_show_time = 0;
my $show_interval = 10;

### ==============================================
my $min_fly_speed = 30; # Knots
my $min_upd_position = 5 * 60;  # update if older than 5 minutes
my $bug_in_bug = 1;
my $min_apt_distance_m = 25 * $SG_NM_TO_METER;  # interested if within 25 nautical miles
my $degs_to_rwy = 35;
my $min_agl_height = 500;
my $short_time_stg = 1;
my $min_targ_dist =1000;    # at airport if less than this

my $curr_target = '';
my $head_target = 0;
my $prev_target = 0;
my $requested_hb = 0;
my $begin_hb = 0;
my $bgn_turn_tm = 0;
my $chk_turn_done = 0;

my $last_wind_info = '';    # metar info at last update
my $mag_deviation = 0; # = difference ($curr_hdg - $curr_mag) at ast update
my $mag_variation = 0; # from /environment/magnetic-variation-deg

# current hashes - at last update
my %m_curr_engine = ();
my %m_curr_klocks = ();
my %m_curr_posit = ();
my %m_curr_env = ();
my %m_curr_comms = ();
my %m_curr_consumables = ();
my %m_curr_gps = ();
my %m_curr_sim = ();
my %m_curr_gear = ();

# fetch the above global - which should not be referred to directly
sub get_curr_posit() { return \%m_curr_posit; }
sub get_curr_env() { return \%m_curr_env; }
sub get_curr_comms() { return \%m_curr_comms; }
sub get_curr_consumables() { return \%m_curr_consumables; }
sub get_curr_gps() { return \%m_curr_gps; }
sub get_curr_engine() { return \%m_curr_engine; }
sub get_curr_Klocks() { return \%m_curr_klocks; }
sub get_curr_sim() { return \%m_curr_sim; }
sub get_curr_gear() { return \%m_curr_gear; }

sub local_get_hhmmss_UTC {
    my ($t) = shift;
    # sec, min, hour, mday, mon, year, wday, yday, and isdst.
    my $tm = gmtime($t);
    my $m = sprintf( "%02d:%02d:%02d",
        $tm->hour(), $tm->min(), $tm->sec());
    return $m;
}

sub local_prt($) {
    print shift;
}
sub prtt($) {
    my $txt = shift;
    prt(lu_get_hhmmss_UTC(time()).": $txt");
}

sub show_warnings($) {
    my ($val) = @_;
    if (@warnings) {
        prt( "\nGot ".scalar @warnings." WARNINGS...\n" );
        foreach my $itm (@warnings) {
           prt("$itm\n");
        }
        prt("\n");
    } else {
        #prt( "\nNo warnings issued.\n\n" );
    }
}

sub pgm_exit($$) {
    my ($val,$msg) = @_;
    if (length($msg)) {
        $msg .= "\n" if (!($msg =~ /\n$/));
        prt($msg);
    }
    show_warnings($val);
    close_fgfs_io();
    ###close_log($outfile,$load_log);
    ReadMode(0);    # restore normal mode
    exit($val);
}


sub give_help() {
    prt("Run '$0 HOST PORT' on the command line... like\n");
    prt("fgtelnet.pl $HOST $PORT\n");
    exit(1);
}

sub got_keyboard($) {
    my ($rc) = shift;   # -1 =  Perform a non-blocked read
    # when set to 'cbreak' mode (4)
    if (defined (my $char = ReadKey(-1)) ) {
        # input was waiting and it was $char
        ${$rc} = $char;
        return 1;
    }
    return 0;
}

sub sleep_ms($) {
    my $usecs = shift; # = $INTERVAL
    if ($usecs > 0) {
        my $secs = $usecs / 1000;
        select(undef,undef,undef,$secs);
        #usleep($usecs);    # sampling interval
    }
}

sub is_numeric($) {
    my $num  = shift;
    return 1 if ($num =~ /^(\d|\.|-|\+)+$/);
    return 0;
}

sub significant_diff($$$) {
    my ($val1,$val2,$sig) = @_;
    my $diff = abs($val1 - $val2);
    return 1 if ($diff > $sig);
    return 0;
}

sub get_exit($) {
    my ($val) = shift;
    pgm_exit($val,"fgfs get FAILED!\n");
}

# helpers for display
sub set_decimal1_stg($) {
    my $r = shift;
    if (is_numeric(${$r})) {
        ${$r} =  int((${$r} + 0.05) * 10) / 10;
        ${$r} = "0.0" if (${$r} == 0);
        ${$r} .= ".0" if !(${$r} =~ /\./);
    }
}

sub set_dist_stg($) {
    my ($rd) = @_;
    my $dist = ${$rd};
    my ($sc);
    if ($dist < 1000) {
        $dist = int($dist);
        $sc = 'm';
    } else {
        $dist = (int(($dist / 1000) * 10) / 10);
        $sc = 'KM';
    }
    ${$rd} = "$dist$sc";
}

sub set_hdg_stg1($) {
    my ($rh) = @_;
    my $hdg = ${$rh};
    $hdg = (int(($hdg+0.05) * 10) / 10);
    ${$rh} = $hdg;
}

sub set_lat_stg($) {
    my ($rl) = @_;
    ${$rl} = sprintf("%2.7f",${$rl});
}
sub set_lon_stg($) {
    my ($rl) = @_;
    ${$rl} = sprintf("%3.7f",${$rl});
}

sub set_hdg_stg($) {
    my ($rh) = @_;
    my $hdg = ${$rh};
    $hdg = 360 if ($hdg == 0); # always replace 000 with 360 ;=))
    $hdg = sprintf("%03d",int($hdg+0.5));
    ${$rh} = $hdg;
}

sub set_int_stg($) {
    my $r = shift;
    ${$r} =  int(${$r} + 0.5);
}

sub get_dist_stg_nm($) {
    my ($dist) = @_;
    my $nm = $dist * $SG_METER_TO_NM;
    set_decimal1_stg(\$nm);
    $nm .= "nm";
    return $nm;
}

# =============================================================
sub get_hdg_in_range($) {
    my $rh = shift;
    if (${$rh} < 0) {
        ${$rh} += 360;
    } elsif (${$rh} > 360) {
        ${$rh} -= 360;
    }
}

sub get_mag_hdg_from_true($) {
    my $hdg = shift;
    $hdg -= $mag_deviation;
    get_hdg_in_range(\$hdg);
    return $hdg;
}

sub secs_HHMMSS2($) {
    my ($secs) = @_;
    my ($mins,$hrs,$stg);
    $mins = int($secs / 60);
    $secs -= ($mins * 60);
    $hrs = int($mins / 60);
    $mins -= ($hrs * 60);
    $stg = sprintf("%02d:%02d:%02d", $hrs, $mins, $secs);
    if ($short_time_stg) {
        $stg =~ s/^00:// if ($stg =~ /^00:/);
        $stg =~ s/^00:// if ($stg =~ /^00:/);
        $stg .= 's' if (length($stg) == 2); # Add seconds if just seconds
    }
    return $stg;
}

# =============================================================
# PATHS
my $get_V_ground_speed = "/velocities/groundspeed-kt"; # * $KNOTS_TO_FTS;
my $get_V_calibrated_kts = "/velocities/airspeed-kt";


# TELNET STUFF
sub fgfs_connect($$$) {
    my ($host,$port,$timeout) = @_;
    my $socket;
    #STDOUT->autoflush(1);
    prtt("Connect $host, $port, timeout $timeout secs ");
    while ($timeout--) {
        if ($socket = IO::Socket::INET->new(
                Proto => 'tcp',
                PeerAddr => $host,
                PeerPort => $port)) {
            prt(" done.\n");
            #$socket->autoflush(1);
            sleep 1;
            return $socket;
        }   
        prt(".");
        sleep(1);
    }
    prt(" FAILED!\n");
    return 0;
}

sub close_fgfs_io {
    if (defined $FGFS_IO) {
        prtt("Closing connection...\n");
        fgfs_send("run exit") if ($send_run_exit);
        close $FGFS_IO;
        undef $FGFS_IO;
    }
}

sub fgfs_send($) {
    print $FGFS_IO shift, "\015\012";
}

sub fgfs_set($$) {
    my ($node,$val) = @_;
    fgfs_send("set $node $val");
    return 1;
}

sub fgfs_get($$) {
    my ($txt,$rval) = @_;
    my $tb = [gettimeofday];
    fgfs_send("get $txt");
    eof $FGFS_IO and return 0;
    ${$rval} = <$FGFS_IO>;
    my $elapsed = tv_interval ( $tb, [gettimeofday]);
    push(@intervals,$elapsed);
    ${$rval} =~ s/\015?\012$//;
    ${$rval} =~ /^-ERR (.*)/ and (prt("WARNING: $1\n") and return 0);
    return 1;
}

sub fgfs_get_with_delay($$$) {
    my ($txt,$rval,$ms) = @_;
    #my $tb = [gettimeofday];
    fgfs_send("get $txt");
    sleep_ms($ms);
    eof $FGFS_IO and return 0;
    ${$rval} = <$FGFS_IO>;
    #my $elapsed = tv_interval ( $tb, [gettimeofday]);
    #push(@intervals,$elapsed);
    ${$rval} =~ s/\015?\012$//;
    ${$rval} =~ /^-ERR (.*)/ and (prt("WARNING: $1\n") and return 0);
    return 1;
}

sub fgfs_get_repeat($$) {
    my ($txt,$rval) = @_;
    #my $tb = [gettimeofday];
    fgfs_send("get $txt");
    eof $FGFS_IO and return 0;
    my $val = <$FGFS_IO>;
    $val =~ s/\015?\012$//;
    $val =~ /^-ERR (.*)/ and (prt("WARNING: $1\n") and return 0);
    # while (! eof $FGFS_IO) { # UGH - this BLOCKED
    my ($line); # try this
    while (defined($line = <$FGFS_IO>)) {
        $val .= ' ';
        $val .= $line;
    }
    ${$rval} = $val;
    return 1;
}

sub fgfs_get_no_clean($$) {
    my ($txt,$rval) = @_;
    fgfs_send("get $txt");
    eof $FGFS_IO and return 0;
    ${$rval} = <$FGFS_IO>;
    # ${$rval} =~ s/\015?\012$//;
    ${$rval} =~ /^-ERR (.*)/ and (prt("WARNING: $1\n") and return 0);
    return 1;
}

# ==========================================================================

sub fgfs_get_coord($$) {
    my ($rlon,$rlat) = @_;
    fgfs_get("/position/longitude-deg", $rlon) or get_exit(-2);
    fgfs_get("/position/latitude-deg", $rlat) or get_exit(-2);
    return 1;
}

sub fgfs_get_alt($) {
    my $ref_alt = shift;
    fgfs_get("/position/altitude-ft", $ref_alt) or get_exit(-2);
    return 1;
}

sub fgfs_get_hdg_true($) {
    my $ref_hdg = shift;
    fgfs_get("/orientation/heading-deg", $ref_hdg) or get_exit(-2);
    return 1;
}
sub fgfs_get_hdg_mag($) {
    my $ref_hdg = shift;
    fgfs_get("/orientation/heading-magnetic-deg", $ref_hdg) or get_exit(-2);
    return 1;
}

sub fgfs_get_hdg_bug($) {
    my $ref_hb = shift;
    fgfs_get("/autopilot/settings/heading-bug-deg", $ref_hb) or get_exit(-2);
    return 1;
}

sub fgfs_set_hdg_bug($) {
    my $val = shift;
    fgfs_set("/autopilot/settings/heading-bug-deg", $val) or get_exit(-2);
    return 1;
}

sub fgfs_get_agl($) {
    my $ref_alt = shift;
    fgfs_get("/position/altitude-agl-ft", $ref_alt) or get_exit(-2);
    return 1;
}

# "/velocities/airspeed-kt";
sub fgfs_get_aspd_kts($) {
    my $ref = shift;
    fgfs_get($get_V_calibrated_kts, $ref) or get_exit(-2);
    return 1;
}

# "/velocities/groundspeed-kt"; # * $KNOTS_TO_FTS;
sub fgfs_get_gspd_kts($) {
    my $ref = shift;
    fgfs_get($get_V_ground_speed, $ref) or get_exit(-2);
    return 1;
}

sub fgfs_get_position() {
    #my ($lon,$lat,$alt,$hdg,$agl,$hb,$mag);
    my ($curr_lat,$curr_lon,$curr_alt,$curr_hdg,$curr_mag,$curr_hb,$curr_agl);
    my ($curr_aspd,$curr_gspd);
    fgfs_get_coord(\$curr_lon,\$curr_lat);
    fgfs_get_alt(\$curr_alt);
    fgfs_get_hdg_true(\$curr_hdg);
    fgfs_get_hdg_mag(\$curr_mag);
    fgfs_get_agl(\$curr_agl);
    fgfs_get_hdg_bug(\$curr_hb);
    fgfs_get_aspd_kts(\$curr_aspd);
    fgfs_get_gspd_kts(\$curr_gspd);
    my $rc = get_curr_posit();
    my $gps = get_curr_gps();
    my $tm = time();
    ${$rc}{'gps-update'} = 0;
#    if (defined ${$rc}{'time'} && defined ${$gps}{'time'}) {
#        my $ptm = ${$rc}{'time'};
#        my $gtm = ${$gps}{'time'};
#        if ($ptm > $gtm) {
#            my $diff = $ptm - $gtm; # get seconds different
#            if ($diff > $gps_next_time) {
#                prtt("Adding a GPS update... Next in ".int($gps_next_time / 60)." mins...\n");
#                $gps = fgfs_get_gps(); # get the GPS position of things, check and compare...
#                ${$rc}{'gps-update'} = 1; # maybe in display, show difference, if ANY...
#            }
#        }
#    } elsif (defined ${$rc}{'time'}) {
#        prtt("Initial GPS update... next in ".int($gps_next_time / 60)." mins\n");
#        $gps = fgfs_get_gps(); # get the GPS position of things, check and compare...
#        ${$rc}{'gps-update'} = 1; # maybe in display, show difference, if ANY...
#    }

    ${$rc}{'time'} = $tm;
    ${$rc}{'lon'} = $curr_lon;
    ${$rc}{'lat'} = $curr_lat;
    ${$rc}{'alt'} = $curr_alt;
    ${$rc}{'hdg'} = $curr_hdg;
    ${$rc}{'mag'} = $curr_mag;
    ${$rc}{'bug'} = $curr_hb;
    ${$rc}{'agl'} = $curr_agl;
    ${$rc}{'aspd'} = $curr_aspd; # Knots
    ${$rc}{'gspd'} = $curr_gspd; # Knots
    $mag_deviation = ($curr_hdg - $curr_mag);
#    if ($chk_turn_done) {
#       if (abs($requested_hb - $curr_mag) <= 1) {
#            my $ctm = $tm - $bgn_turn_tm;
#            my $angle = int(abs($requested_hb - $begin_hb));
#            my $mag = int($curr_mag + 0.5);
#            my $dps = '';
#            if ($ctm > 0) {
#                $dps = (int((($angle / $ctm) + 0.05) * 10) / 10).'DPS';
#            }
#            $requested_hb = int($requested_hb + 0.5);
#            prtt("Completed TURN $angle degrees to $requested_hb($mag) in $ctm seconds... $dps\n");
#            $chk_turn_done = 0;
#        }
#    }
    return $rc;
}

sub show_position($) {
    my ($rp) = @_;
    return if (!defined ${$rp}{'time'});
    my $ctm = lu_get_hhmmss_UTC(${$rp}{'time'});
    my ($lon,$lat,$alt,$hdg,$agl,$hb,$mag,$aspd,$gspd,$cpos);
    $lon  = ${$rp}{'lon'};
    $lat  = ${$rp}{'lat'};
    $alt  = ${$rp}{'alt'};
    $hdg  = ${$rp}{'hdg'};
    $agl  = ${$rp}{'agl'};
    $hb   = ${$rp}{'bug'};
    $mag  = ${$rp}{'mag'};
    $aspd = ${$rp}{'aspd'}; # Knots
    $gspd = ${$rp}{'gspd'}; # Knots

    my $msg = '';
    my $eta = '';
    #show_YGIL_route();
#    if (${$rp}{'gps-update'} == 1) { # maybe in display, show difference, if ANY...
#        my $rg = get_curr_gps();
#        if (defined ${$rg}{'time'}) {
#            my $glon = ${$rg}{'lon'};
#            my $glat = ${$rg}{'lat'};
#            my $galt = ${$rg}{'alt'};
#            my $ghdg = ${$rg}{'hdg'};
#            # $agl = ${$rp}{'agl'};
#            # $hb  = ${$rp}{'bug'};
#            my $gmag = ${$rg}{'mag'};
#            fgfs_get_mag_var(\$mag_variation);
#
#            # display mess
#            set_lat_stg(\$glat);
#            set_lon_stg(\$glon);
#            set_hdg_stg(\$ghdg);
#            set_hdg_stg(\$gmag);
#            $galt = int($galt + 0.5);
#            prt("$ctm: GPS $glat,$glon,$galt ft, hdg=${ghdg}T/${gmag}M \n");
#            show_environ(fgfs_get_environ());
#        }
#    }

    $msg = '';
    my ($tlat,$tlon,$az1,$az2,$dist,$rrwys,$targ,$diff);
    if ($targ_ygil) {
        $tlat = $a_gil_lat;
        $tlon = $a_gil_lon;
        $targ = "YGIL";
    } else { # target Dubbo
        $tlat = $a_dub_lat;
        $tlon = $a_dub_lon;
        $targ = "YSDU";
    }
    fg_geo_inverse_wgs_84 ($lat,$lon,$tlat,$tlon,\$az1,\$az2,\$dist);
    $az2   = get_mag_hdg_from_true($az1); # - $mag_deviation;
    $diff = int($az2 - $mag+0.5);
    if ($dist < $min_targ_dist) {
        set_decimal1_stg(\$dist);
        $msg = " at $targ $dist meters.";
    } else {
        # with GRD SPEED (Knots), and Distance (meters), calculate an ETA (secs)
        my $secs = int(( $dist / (($gspd * $SG_NM_TO_METER) / 3600)) + 0.5);
        $eta = "ETA:".secs_HHMMSS2($secs); # display as hh:mm:ss
        $dist = get_dist_stg_nm($dist);
        set_hdg_stg(\$az1);
        set_hdg_stg(\$az2);
        $msg = " to $targ $dist ${az1}T/${az2}M cc=$diff";
    }
#    if (headed_to_target()) {
#        my $rl = get_locations();
#        $tlat  = ${$rl}{$curr_target}[$OL_LAT];
#        $tlon  = ${$rl}{$curr_target}[$OL_LON];
#        $rrwys = ${$rl}{$curr_target}[$OL_RWY];
#        fg_geo_inverse_wgs_84 ($lat,$lon,$tlat,$tlon,\$az1,\$az2,\$dist);
#        $az2   = get_mag_hdg_from_true($az1); # - $mag_deviation;
#        if ( got_flying_speed() ) {
#            # with GRD SPEED (Knots), and Distance (meters), calculate an ETA (secs)
#            my $secs = int(( $dist / (($gspd * $SG_NM_TO_METER) / 3600)) + 0.5);
#            $eta = "ETA:".secs_HHMMSS2($secs); # display as hh:mm:ss
#            # Hmmm, Dist, AGL, and GrdSpd would allow assessment of a standard 3 degree Glide SLope (GS)
#            # my $min_dist = $agl / $ATAN3;
#            # if (($dist > 0) && ($dist < ($min_dist * 2)))
#            if (($agl > 0) && ($dist > 0) && ($dist < $min_apt_distance_m)) {
#                # we are WITHIN min apt distance - get glide from here
#                $eta .= " *";
#                $eta .= "@" if (abs($az1 - $hdg) < 15); # within 15 degrees of the heading to the runway
#                if ( ($dist > 0) && ($agl > 0) && ( track_to_within_degs($rp,$degs_to_rwy,$az1,$rrwys) ) ) {
#                    $eta .= "+";
#                    my $fpm = get_feet_per_min($aspd,$dist/1000,$agl,0);
#                    if ($fpm > 2000) {
#                        $fpm = ">2000fpm";
#                    } else {
#                        set_int_stg(\$fpm);
#                        $fpm .= "fpm";
#                    }
#                    $eta .= " $fpm";
#                    #my $gs = atan2($agl,$dist) * $SGD_RADIANS_TO_DEGREES;
#                    #set_int_stg(\$gs);
#                    #$eta .= " ${gs}gs";
#                    $eta .= ${$rp}{'s_hdg-diff'};
#                }
#            }
#        }
#        # display messing
#        #set_dist_stg(\$dist);
#        $dist = get_dist_stg_nm($dist);
#        set_hdg_stg(\$az1);
#        set_hdg_stg(\$az2);
#        $msg = "hm:$curr_target $dist ${az1}T/${az2}M";
#    }

    # display stuff - note destroys values - local now only for display
    # =================================================================
    set_hdg_stg(\$hdg);
    set_hdg_stg(\$mag);
    # had some trouble with this BUG - seems not initialized!!! until you move it...
    if ($hb && ($hb =~ /^-*(\d|\.)+$/)) {
        set_hdg_stg(\$hb);
    } elsif ((defined $hb) && length($hb)) {
        $hb = "?$hb?!";
    } else {
        fgfs_set_hdg_bug($mag);
    }
    set_int_stg(\$alt);
    set_lat_stg(\$lat);
    set_lon_stg(\$lon);
    $cpos = "$lat,$lon,$alt";
    if ($aspd < $min_fly_speed) {
        $agl = "OnGrd $cpos";
    } else {
        #if ($agl > $min_agl_height) {
        #    $agl = '';
        #} else {
            $agl = 'alt '.int($agl + 0.5)." $cpos";
        #}
    }
    if ($aspd < $min_fly_speed) {
        if (fgfs_get_parking_brake()) {
            $msg .= " parking break on."
        } else {
            if ($aspd > 1) {
                set_decimal1_stg(\$aspd);
                $msg .= " taxiing $aspd"
            } else {
                $msg .= " stopped."
            }
        }
    } else {
        $aspd = int($aspd + 0.5);
        if (is_numeric($gspd)) {
            $gspd = int($gspd + 0.5);
        }
        $msg .= " spd=$aspd/${gspd}Kt";
    }
    prt("$ctm: $agl hdg=${hdg}T/${mag}M,b=$hb $msg $eta\n");
}

# ===========================================
sub fgfs_get_eng_running($) {
    my $ref = shift;
    fgfs_get("/engines/engine/running", $ref) or get_exit(-2); # bool true/false
    return 1;
}
sub fgfs_get_eng_rpm($) {
    my $ref = shift;
    fgfs_get("/engines/engine/rpm", $ref) or get_exit(-2);  # double
    return 1;
}
sub fgfs_get_eng_throttle($) {  # range 0 to 1
    my $ref = shift;
    fgfs_get("/controls/engines/engine/throttle", $ref) or get_exit(-2);
    return 1;
}

sub fgfs_get_engine() {
    my $re = get_curr_engine();
    my ($running);
    fgfs_get_eng_running(\$running);
    ${$re}{'running'} = $running;
    my ($rpm);
    fgfs_get_eng_rpm(\$rpm);
    ${$re}{'rpm'} = $rpm;
    my ($throt);
    fgfs_get_eng_throttle(\$throt);
    ${$re}{'throttle'} = $throt;
    ${$re}{'time'} = time();
    return $re;
}

sub show_engine($) {
    my $re = shift;
    my $tm = lu_get_hhmmss_UTC(${$re}{'time'});
    my $throt = ${$re}{'throttle'};
    my $rpm = ${$re}{'rpm'};
    my $run = ${$re}{'running'};
    prt("$tm Engine: Run $run, rpm $rpm, throt $throt\n");
}

sub show_engine_change() {
    my $ret = 0;
    my $re = get_curr_engine();
    if (defined ${$re}{'time'}) {
        my %curr = %{$re};
        $re = fgfs_get_engine();
        # my $chg = 0;
        #if ($curr{'running'} ne ${$re}{'running'}) {
        #    prt("Change in 'running' - ".$curr{'running'}.' vs '.${$re}{'running'}."\n");
        #    $chg++;
        #}
        #if ( significant_diff($curr{'rpm'},${$re}{'rpm'},1)) {
        #    prt("Change in 'rpm' - ".$curr{'rpm'}.' vs '.${$re}{'rpm'}."\n");
        #    $chg++;
        #}
        #if (significant_diff($curr{'throttle'},${$re}{'throttle'},0.1)) {
        #    prt("Change in 'throttle' - ".$curr{'throttle'}.' vs '.${$re}{'throttle'}."\n");
        #    $chg++;
        #}
        #if ($chg > 0) {
        #    show_engine($re);
        #}
        if (($curr{'running'} ne ${$re}{'running'})||
            (significant_diff($curr{'rpm'},${$re}{'rpm'},1))||
            (significant_diff($curr{'throttle'},${$re}{'throttle'},0.1))) {
            show_engine($re);
            $ret = 1;
        } else {
            %{$re} = %curr; # restore previous values - avoid creeping change
        }
    } else {
        #prt("Engine: time not defined\n");
        show_engine(fgfs_get_engine());
        $ret = 1;
    }
    return $ret;
}

# ===============================================
# gear
my $gear = "/controls/gear";
sub fgfs_get_brake_left($) {
    my $ref = shift;
    fgfs_get("$gear/brake-left", $ref) or get_exit(-2); # double
    return 1;
}
sub fgfs_get_brake_right($) {
    my $ref = shift;
    fgfs_get("$gear/brake-right", $ref) or get_exit(-2); # double
    return 1;
}
sub fgfs_get_brake_parking($) {
    my $ref = shift;
    fgfs_get("$gear/brake-parking", $ref) or get_exit(-2); # double
    return 1;
}

sub fgfs_get_brakes() {
    my $rg = get_curr_gear();
    my ($bl,$br,$bp);
    fgfs_get_brake_left(\$bl);
    fgfs_get_brake_right(\$br);
    fgfs_get_brake_parking(\$bp);
    ${$rg}{'brake-left'} = $bl;
    ${$rg}{'brake-right'} = $br;
    ${$rg}{'brake-parking'} = $bp;
    ${$rg}{'time'} = time();
    #prt("brakes: L [$bl], R [$br], P [$bp]\n");
    return $rg;
}

sub fgfs_get_parking_brake() {
    my ($pb);
    fgfs_get_brake_parking(\$pb);
    return $pb;
}

sub show_brakes($) {
    my $rg = shift;
    my ($tm,$bl,$br,$bp);
    # my @arr = keys %{$rg};
    # prt("Keys: ".join(" ",@arr)."\n");
    #foreach my $key (@arr) {
    #    prt(" $key ".${$rg}{$key});
    #}
    #prt("\n");
    $tm = lu_get_hhmmss_UTC(${$rg}{'time'});
    $bl = ${$rg}{'brake-left'};
    $br = ${$rg}{'brake-right'};
    $bp = ${$rg}{'brake-parking'};
    prt("$tm Brakes: Left $bl, Right $br, parking $bp\n");
}

sub show_brakes_change() {
    my $ret = 0;
    my $rg = get_curr_gear();
    if (defined ${$rg}{'time'}) {
        my %curr = %{$rg}; # copy hash
        $rg = fgfs_get_brakes();
        if (($curr{'brake-left'} != ${$rg}{'brake-left'})||
            ($curr{'brake-right'} != ${$rg}{'brake-right'})||
            ($curr{'brake-parking'} != ${$rg}{'brake-parking'})) {
            show_brakes($rg);
            $ret = 1;
        } else {
            %{$rg} = %curr; # avoid creep
        }
    } else {
        show_brakes(fgfs_get_brakes());
        $ret = 1;
    }
    return $ret;
}


# ===============================================
sub fgfs_get_aero($) {
    my $ref = shift;    # \$aero
    fgfs_get("/sim/aero", $ref) or get_exit(-2); # string
    return 1;
}
sub fgfs_get_fdm($) {
    my $ref = shift;    # \$aero
    fgfs_get("/sim/flight-model", $ref) or get_exit(-2); # string
    return 1;
}
sub fgfs_get_root($) {
    my $ref = shift;    # \$aero
    fgfs_get("/sim/fg-root", $ref) or get_exit(-2); # string
    return 1;
}
sub fgfs_get_desc($) {
    my $ref = shift;    # \$aero
    fgfs_get("/sim/decription", $ref) or get_exit(-2); # string
    return 1;
}

sub fgfs_get_sim_info() {
    my ($aero,$fdm,$root,$desc);
    fgfs_get_aero(\$aero);
    fgfs_get_fdm(\$fdm);
    fgfs_get_root(\$root);
    fgfs_get_desc(\$desc);
    my $rs = get_curr_sim();
    ${$rs}{'aero'} = $aero;
    ${$rs}{'fdm'} = $fdm;
    ${$rs}{'root'} = $root;
    ${$rs}{'desc'} = $desc;
    ${$rs}{'time'} = time();
    return $rs;
}

sub show_sim_info($) {
    my $rs = shift;
    my ($ctm,$aero,$fdm,$root,$desc);
    $aero = ${$rs}{'aero'};
    $fdm = ${$rs}{'fdm'};
    $root = ${$rs}{'root'};
    $desc = ${$rs}{'desc'};
    $ctm = lu_get_hhmmss_UTC(${$rs}{'time'});
    prt("$ctm: FDM=[$fdm] aero=[$aero] fg-root=[$root]\n");
    prt("$ctm: Desc=$desc\n") if ((defined $desc) && length($desc));
}

sub show_sim_info_change() {
    my $ret = 0;
    my $rs = get_curr_sim();
    if (defined ${$rs}{'time'}) {
        my %curr = %{$rs};
        $rs = fgfs_get_sim_info();
        if (($curr{'aero'} ne ${$rs}{'aero'})||
            ($curr{'fdm'} ne ${$rs}{'fdm'})||
            ($curr{'root'} ne ${$rs}{'root'})||
            ($curr{'desc'} ne ${$rs}{'desc'})) {
            show_sim_info($rs);
            $ret = 1;
        }
    
    } else {
        show_sim_info(fgfs_get_sim_info());
        $ret = 1;
    }
    return $ret;
}

# ===============================================
sub fgfs_get_fuel1_imp($) {
    my $ref = shift;
    fgfs_get("/consumables/fuel/tank/level-gal_imp", $ref) or get_exit(-2); # double
    return 1;
}
sub fgfs_get_fuel1_us($) {
    my $ref = shift;
    fgfs_get("/consumables/fuel/tank/level-gal_us", $ref) or get_exit(-2); # double
    return 1;
}
sub fgfs_get_fuel2_imp($) {
    my $ref = shift;
    fgfs_get("/consumables/fuel/tank[1]/level-gal_imp", $ref) or get_exit(-2); # double
    return 1;
}
sub fgfs_get_fuel2_us($) {
    my $ref = shift;
    fgfs_get("/consumables/fuel/tank[1]/level-gal_us", $ref) or get_exit(-2); # double
    return 1;
}
sub fgfs_get_fuel_lbs($) {
    my $ref = shift;
    fgfs_get("/consumables/fuel/total-fuel-lbs", $ref) or get_exit(-2); # double
    return 1;
}
sub fgfs_get_fuel_kgs($) {
    my $ref = shift;
    fgfs_get("/consumables/fuel/total-fuel-kg", $ref) or get_exit(-2); # double
    return 1;
}

sub fgfs_get_consumables() {
    my ($t1gi,$t1gus,$t2gi,$t2gus);
    my ($tlbs,$tkgs);
    fgfs_get_fuel1_imp(\$t1gi);
    fgfs_get_fuel1_us(\$t1gus);
    fgfs_get_fuel2_imp(\$t2gi);
    fgfs_get_fuel2_us(\$t2gus);
    fgfs_get_fuel_lbs(\$tlbs);
    fgfs_get_fuel_kgs(\$tkgs);
    my $rc = get_curr_consumables();
    ${$rc}{'time'} = time();
    ${$rc}{'tank1-imp'} = $t1gi;
    ${$rc}{'tank1-us'} = $t1gus;
    ${$rc}{'tank2-imp'} = $t2gi;
    ${$rc}{'tank2-us'} = $t2gus;
    ${$rc}{'total-imp'} = ($t1gi + $t2gi);
    ${$rc}{'total-us'} = ($t1gus + $t2gus);
    ${$rc}{'total-lbs'} = $tlbs;
    ${$rc}{'total-kgs'} = $tkgs;
    return $rc;
}

sub show_consumables($) {
    my $rc = shift;
    if (defined ${$rc}{'time'}) {
        my ($t1gi,$t1gus,$t2gi,$t2gus,$totgi,$totgus);
        my ($tlbs,$tkgs);
        my $ctm = lu_get_hhmmss_UTC(${$rc}{'time'});
        $t1gi = ${$rc}{'tank1-imp'};
        $t1gus = ${$rc}{'tank1-us'};
        $t2gi = ${$rc}{'tank2-imp'};
        $t2gus = ${$rc}{'tank2-us'};
        $totgi = ${$rc}{'total-imp'};
        $totgus = ${$rc}{'total-us'};
        $tlbs = ${$rc}{'total-lbs'};
        $tkgs = ${$rc}{'total-kgs'};

        # display fixes
        set_decimal1_stg(\$t1gi);
        set_decimal1_stg(\$t1gus);
        set_decimal1_stg(\$t2gi);
        set_decimal1_stg(\$t2gus);
        set_decimal1_stg(\$totgi);
        set_decimal1_stg(\$totgus);
        set_decimal1_stg(\$tlbs);
        set_decimal1_stg(\$tkgs);
        prt("$ctm: Total $totgi gal.imp ($totgus us), $tlbs lbs ($tkgs kgs). T1 $t1gi($t1gus), T2 $t2gi($t2gus)\n");
    }
}

sub show_consumables_change() {
    my $ret = 0;
    my $rc = get_curr_consumables();
    if (defined ${$rc}{'time'}) {
        my %curr = %{$rc};              # copy
        $rc = fgfs_get_consumables();   # get update
        if (significant_diff($curr{'total-lbs'},${$rc}{'total-lbs'},1)) {
            show_consumables($rc);
            $ret = 1;
        } else {
            %{$rc} = %curr;
        }
    } else {
        show_consumables(fgfs_get_consumables());
        $ret = 1;
    }
    return $ret;
}

# ================================================
sub fgfs_get_wind_speed($) {
    my ($ref) = @_;
    #fgfs_get("/environment/wind-speed-kt", $ref) or get_exit(-2); # double
    fgfs_get("/environment/metar/base-wind-speed-kt", $ref) or get_exit(-2); # double
    return 1;
}
sub fgfs_get_wind_heading($) {
    my ($ref) = @_;
    #fgfs_get("/environment/wind-from-heading-deg", $ref) or get_exit(-2); # double
    #fgfs_get("/environment/metar/base-wind-range-from", $ref) or get_exit(-2); # double
    fgfs_get("/environment/metar/base-wind-dir-deg", $ref) or get_exit(-2); # double
    return 1;
}
sub fgfs_get_wind_east($) {
    my ($ref) = @_;
    #fgfs_get("/environment/wind-from-east-fps", $ref) or get_exit(-2); # double
    fgfs_get("/environment/metar/base-wind-from-east-fps", $ref) or get_exit(-2); # double
    return 1;
}
sub fgfs_get_wind_north($) {
    my ($ref) = @_;
    #fgfs_get("/environment/wind-from-north-fps", $ref) or get_exit(-2); # double
    fgfs_get("/environment/metar/base-wind-from-north-fps", $ref) or get_exit(-2); # double
    return 1;
}
sub fgfs_get_metar($) {
    my ($ref) = @_;
    fgfs_get("/environment/metar/data", $ref) or get_exit(-2); # double
    return 1;
}

sub fgfs_get_metar_with_delay($$) {
    my ($ref,$ms) = @_;
    fgfs_get_with_delay("/environment/metar/data", $ref, $ms) or get_exit(-2); # double
    return 1;
}

sub fgfs_get_metar_repeat($) {
    my ($ref) = @_;
    fgfs_get_repeat("/environment/metar/data", $ref) or get_exit(-2); # double
    return 1;
}

sub fgfs_get_mag_var($) {
    my $ref = shift;
    fgfs_get("/environment/magnetic-variation-deg", $ref) or get_exit(-2); # double
    return 1;
}

sub fgfs_get_metar_second($) {
    my ($ref) = @_;
    prt("Trying fgfs_get_metar_second...\n");
    fgfs_get_metar($ref) or get_exit(-2); # double
    prt("Got value ".${$ref}." - doing second, 3rd 4th...\n");
    my ($aero,$fdm,$root);
    fgfs_get_aero(\$aero);
    fgfs_get_fdm(\$fdm);
    fgfs_get_root(\$root);
    prt("Got 2nd values [$aero] [$fdm] [$root]\n");
    #${$ref} .= " $val";
    return 1;
}

sub fgfs_get_environ() {
    my ($wspd,$whdg,$weast,$wnor,$metar,$mv);
    fgfs_get_mag_var(\$mv);
    fgfs_get_wind_speed(\$wspd);
    fgfs_get_wind_heading(\$whdg);
    fgfs_get_wind_east(\$weast);
    fgfs_get_wind_north(\$wnor);
    my $renv = get_curr_env();
    ${$renv}{'time'} = time();
    ${$renv}{'speed-kt'} = $wspd;
    ${$renv}{'heading-deg'} = $whdg;
    ${$renv}{'east-fps'} = $weast;
    ${$renv}{'north-fps'} = $wnor;
    ${$renv}{'mag-variation'} = $mv;
    $mag_variation = $mv;

    # getting this string seems to cause problems
    if ($add_metar) {
        if ($try_metar_delay) {
            fgfs_get_metar_with_delay(\$metar,200);    # string
        } elsif ($try_metar_repeat) {
            fgfs_get_metar_repeat(\$metar);    # string
        } elsif ($try_metar_second) {
            fgfs_get_metar_second(\$metar);    # string
        } else {
            fgfs_get_metar(\$metar);    # string
        }
        ${$renv}{'metar'} = $metar;
    }

    return $renv;
}

sub show_environ($) {
    my ($renv) = @_;
    my ($tm,$wspd,$whdg,$weast,$wnor);
    my ($tmp1,$tmp2);
    if (defined ${$renv}{'time'}) {
        $tm = ${$renv}{'time'};
        $wspd = ${$renv}{'speed-kt'};
        $whdg = ${$renv}{'heading-deg'};
        $weast = ${$renv}{'east-fps'};
        $wnor = ${$renv}{'north-fps'};
        my $ctm = lu_get_hhmmss_UTC($tm);
        my $chdg = sprintf("%03d", int($whdg + 0.5));
        my $cspd = sprintf("%02d", int($wspd + 0.5));
        $tmp1 = $mag_deviation;
        $tmp2 = $mag_variation;
        set_decimal1_stg(\$tmp1);
        set_decimal1_stg(\$tmp2);
        set_decimal1_stg(\$weast);
        set_decimal1_stg(\$wnor);
        $last_wind_info = "$chdg${cspd}KT";
        prt("$ctm: WIND: $last_wind_info - fps E=$weast, N=$wnor - MagVar=$tmp1($tmp2)\n");
        my $metar = ${$renv}{'metar'};
        if ((defined $metar) && length($metar)) {
            prt("$ctm: METAR: [$metar]\n");
        } else {
            ### prt("$ctm: METAR: not available\n");
        }
    }
}

sub show_environ_change() {
    my $ret = 1;
    my $renv = get_curr_env();
    if (defined ${$renv}{'time'}) {
        my %curr = %{$renv};
        $renv = fgfs_get_environ();
        if ((significant_diff(${$renv}{'speed-kt'},$curr{'speed-kt'},0.5))||
            (significant_diff(${$renv}{'heading-deg'},$curr{'heading-deg'},2))||
            (significant_diff(${$renv}{'east-fps'},$curr{'east-fps'},0.5))||
            (significant_diff(${$renv}{'north-fps'},$curr{'north-fps'},0.5))) {
            show_environ($renv);
        } else {
            %{$renv} = %curr;
            $ret = 0;
        }
    } else {
        show_environ(fgfs_get_environ());
    }
    return $ret;
}


# ===============================================
sub fgfs_get_comm1_active($) {
    my ($ref) = @_;
    fgfs_get("/instrumentation/comm/frequencies/selected-mhz", $ref) or get_exit(-2); # double
    return 1;
}
sub fgfs_get_comm1_stdby($) {
    my ($ref) = @_;
    fgfs_get("/instrumentation/comm/frequencies/standby-mhz", $ref) or get_exit(-2); # double
    return 1;
}
sub fgfs_get_comm1($$) {
    my ($rca,$rcs) = @_;
    fgfs_get_comm1_active($rca);
    fgfs_get_comm1_stdby($rcs);
}

sub fgfs_get_comm2_active($) {
    my ($ref) = @_;
    fgfs_get("/instrumentation/comm[1]/frequencies/selected-mhz", $ref) or get_exit(-2); # double
    return 1;
}
sub fgfs_get_comm2_stdby($) {
    my ($ref) = @_;
    fgfs_get("/instrumentation/comm[1]/frequencies/standby-mhz", $ref) or get_exit(-2); # double
    return 1;
}
sub fgfs_get_comm2($$) {
    my ($rca,$rcs) = @_;
    fgfs_get_comm2_active($rca);
    fgfs_get_comm2_stdby($rcs);
}

# NAV1 Display
sub fgfs_get_nav1_radial($) {
    my ($ref) = @_;
    fgfs_get("/instrumentation/nav/radials/selected-deg", $ref) or get_exit(-2); # double
    return 1;
}

sub fgfs_get_nav1_active($) {
    my ($ref) = @_;
    fgfs_get("/instrumentation/nav/frequencies/selected-mhz", $ref) or get_exit(-2); # double
    return 1;
}
sub fgfs_get_nav1_stdby($) {
    my ($ref) = @_;
    fgfs_get("/instrumentation/nav/frequencies/standby-mhz", $ref) or get_exit(-2); # double
    return 1;
}

sub fgfs_get_nav1($$) {
    my ($rna,$rns) = @_;
    fgfs_get_nav1_active($rna);
    fgfs_get_nav1_stdby($rns);
}

sub fgfs_get_nav2_active($) {
    my ($ref) = @_;
    fgfs_get("/instrumentation/nav[1]/frequencies/selected-mhz", $ref) or get_exit(-2); # double
    return 1;
}
sub fgfs_get_nav2_stdby($) {
    my ($ref) = @_;
    fgfs_get("/instrumentation/nav[1]/frequencies/standby-mhz", $ref) or get_exit(-2); # double
    return 1;
}

sub fgfs_get_nav2($$) {
    my ($rna,$rns) = @_;
    fgfs_get_nav2_active($rna);
    fgfs_get_nav2_stdby($rns);
}

sub fgfs_get_adf_active($) {
    my ($ref) = @_;
    fgfs_get("/instrumentation/adf/frequencies/selected-khz", $ref) or get_exit(-2); # double
    return 1;
}
sub fgfs_get_adf_stdby($) {
    my ($ref) = @_;
    fgfs_get("/instrumentation/adf/frequencies/standby-khz", $ref) or get_exit(-2); # double
    return 1;
}
sub fgfs_get_adf($$) {
    my ($radf,$radfs) = @_;
    fgfs_get_adf_active($radf);
    fgfs_get_adf_stdby($radfs);
}

sub fgfs_get_comms() {
    my ($c1a,$c1s);
    my ($n1a,$n1s);
    my ($c2a,$c2s);
    my ($n2a,$n2s);
    my ($adf,$adfs);
    fgfs_get_adf(\$adf,\$adfs);
    fgfs_get_comm1(\$c1a,\$c1s);
    fgfs_get_nav1(\$n1a,\$n1s);
    fgfs_get_comm2(\$c2a,\$c2s);
    fgfs_get_nav2(\$n2a,\$n2s);
    my $rc = get_curr_comms();
    ${$rc}{'time'} = time();
    ${$rc}{'adf-act'} = $adf;
    ${$rc}{'adf-sby'} = $adfs;
    ${$rc}{'comm1-act'} = $c1a;
    ${$rc}{'comm1-sby'} = $c1s;
    ${$rc}{'nav1-act'}  = $n1a;
    ${$rc}{'nav1-sby'}  = $n1s;
    ${$rc}{'comm2-act'} = $c2a;
    ${$rc}{'comm2-sby'} = $c2s;
    ${$rc}{'nav2-act'}  = $n2a;
    ${$rc}{'nav2-sby'}  = $n2s;
    return $rc;
}

sub show_comms($) {
    my ($rc) = @_;
    my ($c1a,$c1s);
    my ($n1a,$n1s);
    my ($adf,$adfs);
    my ($c2a,$c2s);
    my ($n2a,$n2s);
    if (defined ${$rc}{'time'}) {
        my $ctm = lu_get_hhmmss_UTC(${$rc}{'time'});
        $adf = ${$rc}{'adf-act'};
        $adfs = ${$rc}{'adf-sby'};
        $c1a = ${$rc}{'comm1-act'};
        $c1s = ${$rc}{'comm1-sby'};
        $n1a = ${$rc}{'nav1-act'};
        $n1s = ${$rc}{'nav1-sby'};
        $c2a = ${$rc}{'comm2-act'};
        $c2s = ${$rc}{'comm2-sby'};
        $n2a = ${$rc}{'nav2-act'};
        $n2s = ${$rc}{'nav2-sby'};
        if (is_numeric($adf) && is_numeric($adfs)) {
            prt("$ctm: ".sprintf("ADF   %03d (%03d)",$adf,$adfs)."\n");
        } else {
            prt("$ctm: ADF   $adf ($adfs) (NN)\n");
        }
        prt("$ctm: ".sprintf("COMM1 %03.3f (%03.3f) NAV1 %03.3f (%03.3f)",$c1a,$c1s,$n1a,$n1s)."\n");
        prt("$ctm: ".sprintf("COMM2 %03.3f (%03.3f) NAV2 %03.3f (%03.3f)",$c2a,$c2s,$n2a,$n2s)."\n");
    }
}

sub show_comms_change() {
    my $ret = 0;
    my $rc = get_curr_comms();
    if (defined ${$rc}{'time'}) {
        my %curr = %{$rc};
        $rc = fgfs_get_comms();
        if ((${$rc}{'adf-act'} != $curr{'adf-act'})||
            (${$rc}{'comm1-act'} != $curr{'comm1-act'})||
            (${$rc}{'nav1-act'} != $curr{'nav1-act'})||
            (${$rc}{'comm2-act'} != $curr{'comm2-act'})||
            (${$rc}{'nav2-act'} != $curr{'nav2-act'})) {
            show_comms($rc);
            $ret = 1;
        } else {
            %{$rc} = %curr;
        }
    } else {
        show_comms(fgfs_get_comms());
        $ret = 1;
    }
    return $ret;
}


# ===============================================
# Auto-pilot locks
sub fgfs_get_K_ah($) {
    my $ref = shift;
    fgfs_get("/autopilot/KAP140/locks/alt-hold", $ref) or get_exit(-2); # = true/false
    return 1;
}
sub fgfs_get_K_pa($) {
    my $ref = shift;
    fgfs_get("/autopilot/KAP140/locks/pitch-axis", $ref) or get_exit(-2); # = true/false
    return 1;
}
sub fgfs_get_K_pm($) {
    my $ref = shift;
    fgfs_get("/autopilot/KAP140/locks/pitch-mode", $ref) or get_exit(-2); # = 1/0
    return 1;
}
sub fgfs_get_K_ra($) {
    my $ref = shift;
    fgfs_get("/autopilot/KAP140/locks/roll-axis", $ref) or get_exit(-2); # = true/false
    return 1;
}
sub fgfs_get_K_rm($) {
    my $ref = shift;
    fgfs_get("/autopilot/KAP140/locks/roll-mode", $ref) or get_exit(-2); # = 1/0
    return 1;
}

sub fgfs_get_K_locks() {
    my ($ah,$pa,$pm,$ra,$rm);
    fgfs_get_K_ah(\$ah);
    fgfs_get_K_pa(\$pa);
    fgfs_get_K_pm(\$pm);
    fgfs_get_K_ra(\$ra);
    fgfs_get_K_rm(\$rm);
    my $rk = get_curr_Klocks();
    ${$rk}{'time'} = time();
    ${$rk}{'ah'} = $ah;
    ${$rk}{'pa'} = $pa;
    ${$rk}{'pm'} = $pm;
    ${$rk}{'ra'} = $ra;
    ${$rk}{'rm'} = $rm;
    return $rk;
}

sub show_K_locks($) {
    my $rk = shift;
    my ($tm,$ah,$pa,$pm,$ra,$rm);
    $tm = lu_get_hhmmss_UTC(${$rk}{'time'});
    $ah = ${$rk}{'ah'};
    $pa = ${$rk}{'pa'};
    $pm = ${$rk}{'pm'};
    $ra = ${$rk}{'ra'};
    $rm = ${$rk}{'rm'};
    prt("$tm: KLOCKS: ah=$ah, pa=$pa, pm=$pm, ra=$ra, rm=$rm\n");
}

sub show_K_locks_change() {
    my $ret = 0;
    my $rk = get_curr_Klocks();
    if (defined ${$rk}{'time'}) {
        my %curr = %{$rk};
        $rk = fgfs_get_K_locks();
        # KLOCKS: ah=false, pa=false, pm=0, ra=false, rm=0
        if ((${$rk}{'ah'} ne $curr{'ah'})||
            (${$rk}{'pa'} ne $curr{'pa'})||
            (${$rk}{'pm'} != $curr{'pm'})||
            (${$rk}{'ra'} ne $curr{'ra'})||
            (${$rk}{'rm'} != $curr{'rm'})) {
            show_K_locks($rk);
            $ret = 1;
        }
    } else {
        show_K_locks(fgfs_get_K_locks());
        $ret = 1;
    }
    return $ret;
}

# ===============================================
sub is_show_time() {
    my $ct = time();
    if ($last_show_time == 0) {
        $begin_show_time = $ct;
        $last_show_time = $ct;
        return 1;
    } elsif ($ct >= ($last_show_time + $show_interval)) {
        $last_show_time = $ct;
        return 1;
    }
    return 0;
}

sub check_keyboard() {
    my ($char,$val,$pmsg);
    if (got_keyboard(\$char)) {
        $val = ord($char);
        $pmsg = sprintf( "%02X", $val );
        if ($val == 27) {
            prtt("ESC key... Exiting...\n");
            return 1;
        } elsif ($char eq 't') {
            if ($targ_ygil) {
                $targ_ygil = 0;
                prtt("Setting YDSU as target...\n");
            } else {
                $targ_ygil = 1;
                prtt("Setting YGIL as target...\n");
            }
        } else {
            prtt("Got keyboard input hex[$pmsg]...\n");
        }
    }
    return 0;
}

sub show_items() {
    while (1) {
        return if (check_keyboard());
        if (is_show_time()) {
            my $chg = 0;
            #prtt("Get 'sim' information...\n");
            # show_sim_info(fgfs_get_sim_info());
            $chg += show_sim_info_change();
            # show engine
            #show_engine(fgfs_get_engine());
            $chg += show_engine_change();
            #prtt("Get Fuel - comsumables...\n");
            #show_consumables(fgfs_get_consumables());
            $chg += show_consumables_change();
            #prtt("Getting current COMMS...\n");
            #show_comms(fgfs_get_comms());
            $chg += show_comms_change();
            # show brakes
            #show_brakes(fgfs_get_brakes());
            $chg += show_brakes_change();
            # position
            show_position(fgfs_get_position());
            # AUTOPILOT
            #show_K_locks(fgfs_get_K_locks());
            $chg += show_K_locks_change();
            #prtt("Getting current environment...\n");
            #show_environ(fgfs_get_environ());
            $chg += show_environ_change();
            my $elap = secs_HHMMSS($last_show_time - $begin_show_time);
            prtt("No significant change in other items. Elap $elap\n") if ($chg == 0);
        }
    }
}

sub show_intervals()
{
    if (@intervals) {
        my ($ok,$btm,$ntm,$dtm);
        $ok = scalar @intervals;
        $btm = 0;
        foreach $ntm (@intervals) {
            $btm += $ntm;
        }
        $dtm = $btm / $ok;
        prtt("$ok accesses took $btm secs, av $dtm per access...\n");
        # 39 accesses took 15.568619 secs, avarage 0.399195358974359 per access...
    } else {
        prtt("What no intervals...\n");
    }
}

### MAIN ### ================================================
#############################################################
if (scalar @ARGV < 2) {
    give_help();
}

$HOST = $ARGV[0];
$PORT = $ARGV[1];
prtt("Set host $HOST, port $PORT - get connection\n");

$FGFS_IO = fgfs_connect($HOST, $PORT, $TIMEOUT) ||
    (prtt("ERROR: can't open socket! Is FG running, with TELNET enabled?\n") and exit(1));

if ($set_bin_mode) {
    binmode $FGFS_IO;   # try binmode on connection
}

fgfs_send("data");  # switch exchange to data mode

ReadMode('cbreak'); # sets mode 4 for keyboard - could maybe use 5 = raw???
show_items();   # forever loop, until ESC key
ReadMode(0);    # restore normal mode

show_intervals();

close_fgfs_io();

ReadMode(0);    # restore normal mode

exit(0);

### END ### =================================================
#############################################################

# eof
