I have prepared a patch that: - Introduces a FGTileMgr::scenery_available method which asks the tilemanager if scenery for a given range around a lat/lon pair is already loaded and make use of that method at some -9999 meter checks. - Introduces a FGScenery::get_elevation_m method which queries the altitude at a given position. In constrast to the groundcache functions this is the best choice if you ask for one *single* altitude value. Make use of that thing in AI/ATC classes and for the current views ground level. At the current views part the groundcache is reused if possible. - The computation of the 'current groundlevel' is no longer done on the tilemanagers update since the required functions are now better seperated. Alltogether it eliminates somehow redundant terrain level computations which are now superseeded by that more finegrained functions and the existence of the groundcache. Additionally it introduces an api to commonly required functions which was very complex to do prevously.
539 lines
18 KiB
C++
539 lines
18 KiB
C++
// native_fdm.cxx -- FGFS "Native" flight dynamics protocal class
|
|
//
|
|
// Written by Curtis Olson, started September 2001.
|
|
//
|
|
// Copyright (C) 2001 Curtis L. Olson - http://www.flightgear.org/~curt
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License as
|
|
// published by the Free Software Foundation; either version 2 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful, but
|
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
//
|
|
// $Id$
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <simgear/debug/logstream.hxx>
|
|
#include <simgear/io/lowlevel.hxx> // endian tests
|
|
#include <simgear/io/iochannel.hxx>
|
|
#include <simgear/timing/sg_time.hxx>
|
|
|
|
#include <FDM/flight.hxx>
|
|
#include <Time/tmp.hxx>
|
|
#include <Main/fg_props.hxx>
|
|
#include <Main/globals.hxx>
|
|
#include <Scenery/scenery.hxx>
|
|
|
|
#include "native_fdm.hxx"
|
|
|
|
// FreeBSD works better with this included last ... (?)
|
|
#if defined(WIN32) && !defined(__CYGWIN__)
|
|
# include <windows.h>
|
|
#else
|
|
# include <netinet/in.h> // htonl() ntohl()
|
|
#endif
|
|
|
|
|
|
// The function htond is defined this way due to the way some
|
|
// processors and OSes treat floating point values. Some will raise
|
|
// an exception whenever a "bad" floating point value is loaded into a
|
|
// floating point register. Solaris is notorious for this, but then
|
|
// so is LynxOS on the PowerPC. By translating the data in place,
|
|
// there is no need to load a FP register with the "corruped" floating
|
|
// point value. By doing the BIG_ENDIAN test, I can optimize the
|
|
// routine for big-endian processors so it can be as efficient as
|
|
// possible
|
|
static void htond (double &x)
|
|
{
|
|
if ( sgIsLittleEndian() ) {
|
|
int *Double_Overlay;
|
|
int Holding_Buffer;
|
|
|
|
Double_Overlay = (int *) &x;
|
|
Holding_Buffer = Double_Overlay [0];
|
|
|
|
Double_Overlay [0] = htonl (Double_Overlay [1]);
|
|
Double_Overlay [1] = htonl (Holding_Buffer);
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Float version
|
|
static void htonf (float &x)
|
|
{
|
|
if ( sgIsLittleEndian() ) {
|
|
int *Float_Overlay;
|
|
int Holding_Buffer;
|
|
|
|
Float_Overlay = (int *) &x;
|
|
Holding_Buffer = Float_Overlay [0];
|
|
|
|
Float_Overlay [0] = htonl (Holding_Buffer);
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
FGNativeFDM::FGNativeFDM() {
|
|
}
|
|
|
|
FGNativeFDM::~FGNativeFDM() {
|
|
}
|
|
|
|
|
|
// open hailing frequencies
|
|
bool FGNativeFDM::open() {
|
|
if ( is_enabled() ) {
|
|
SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
|
|
<< "is already in use, ignoring" );
|
|
return false;
|
|
}
|
|
|
|
SGIOChannel *io = get_io_channel();
|
|
|
|
if ( ! io->open( get_direction() ) ) {
|
|
SG_LOG( SG_IO, SG_ALERT, "Error opening channel communication layer." );
|
|
return false;
|
|
}
|
|
|
|
set_enabled( true );
|
|
|
|
// Is this really needed here ????
|
|
cur_fdm_state->_set_Sea_level_radius( SG_EQUATORIAL_RADIUS_FT );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void FGProps2NetFDM( FGNetFDM *net, bool net_byte_order ) {
|
|
unsigned int i;
|
|
|
|
// Version sanity checking
|
|
net->version = FG_NET_FDM_VERSION;
|
|
|
|
// Aero parameters
|
|
net->longitude = cur_fdm_state->get_Longitude();
|
|
net->latitude = cur_fdm_state->get_Latitude();
|
|
net->altitude = cur_fdm_state->get_Altitude() * SG_FEET_TO_METER;
|
|
net->agl = cur_fdm_state->get_Altitude_AGL() * SG_FEET_TO_METER;
|
|
net->phi = cur_fdm_state->get_Phi();
|
|
net->theta = cur_fdm_state->get_Theta();
|
|
net->psi = cur_fdm_state->get_Psi();
|
|
net->alpha = cur_fdm_state->get_Alpha();
|
|
net->beta = cur_fdm_state->get_Beta();
|
|
net->phidot = cur_fdm_state->get_Phi_dot_degps() * SG_DEGREES_TO_RADIANS;
|
|
net->thetadot = cur_fdm_state->get_Theta_dot_degps()
|
|
* SG_DEGREES_TO_RADIANS;
|
|
net->psidot = cur_fdm_state->get_Psi_dot_degps() * SG_DEGREES_TO_RADIANS;
|
|
|
|
net->vcas = cur_fdm_state->get_V_calibrated_kts();
|
|
net->climb_rate = cur_fdm_state->get_Climb_Rate();
|
|
|
|
net->v_north = cur_fdm_state->get_V_north();
|
|
net->v_east = cur_fdm_state->get_V_east();
|
|
net->v_down = cur_fdm_state->get_V_down();
|
|
net->v_wind_body_north = cur_fdm_state->get_uBody();
|
|
net->v_wind_body_east = cur_fdm_state->get_vBody();
|
|
net->v_wind_body_down = cur_fdm_state->get_wBody();
|
|
|
|
net->A_X_pilot = cur_fdm_state->get_A_X_pilot();
|
|
net->A_Y_pilot = cur_fdm_state->get_A_Y_pilot();
|
|
net->A_Z_pilot = cur_fdm_state->get_A_Z_pilot();
|
|
|
|
net->stall_warning = fgGetDouble("/sim/alarms/stall-warning", 0.0);
|
|
net->slip_deg
|
|
= fgGetDouble("/instrumentation/slip-skid-ball/indicated-slip-skid");
|
|
|
|
// Engine parameters
|
|
net->num_engines = FGNetFDM::FG_MAX_ENGINES;
|
|
for ( i = 0; i < net->num_engines; ++i ) {
|
|
SGPropertyNode *node = fgGetNode("engines/engine", i, true);
|
|
if ( node->getBoolValue( "running" ) ) {
|
|
net->eng_state[i] = 2;
|
|
} else if ( node->getBoolValue( "cranking" ) ) {
|
|
net->eng_state[i] = 1;
|
|
} else {
|
|
net->eng_state[i] = 0;
|
|
}
|
|
net->rpm[i] = node->getDoubleValue( "rpm" );
|
|
net->fuel_flow[i] = node->getDoubleValue( "fuel-flow-gph" );
|
|
net->egt[i] = node->getDoubleValue( "egt-degf" );
|
|
// cout << "egt = " << aero->EGT << endl;
|
|
net->cht[i] = node->getDoubleValue( "cht-degf" );
|
|
net->mp_osi[i] = node->getDoubleValue( "mp-osi" );
|
|
net->tit[i] = node->getDoubleValue( "tit" );
|
|
net->oil_temp[i] = node->getDoubleValue( "oil-temperature-degf" );
|
|
net->oil_px[i] = node->getDoubleValue( "oil-pressure-psi" );
|
|
}
|
|
|
|
// Consumables
|
|
net->num_tanks = FGNetFDM::FG_MAX_TANKS;
|
|
for ( i = 0; i < net->num_tanks; ++i ) {
|
|
SGPropertyNode *node = fgGetNode("/consumables/fuel/tank", i, true);
|
|
net->fuel_quantity[i] = node->getDoubleValue("level-gal_us");
|
|
}
|
|
|
|
// Gear and flaps
|
|
net->num_wheels = FGNetFDM::FG_MAX_WHEELS;
|
|
for (i = 0; i < net->num_wheels; ++i ) {
|
|
SGPropertyNode *node = fgGetNode("/gear/gear", i, true);
|
|
net->wow[i] = node->getIntValue("wow");
|
|
net->gear_pos[i] = node->getDoubleValue("position-norm");
|
|
net->gear_steer[i] = node->getDoubleValue("steering-norm");
|
|
net->gear_compression[i] = node->getDoubleValue("compression-norm");
|
|
}
|
|
|
|
// the following really aren't used in this context
|
|
net->cur_time = globals->get_time_params()->get_cur_time();
|
|
net->warp = globals->get_warp();
|
|
net->visibility = fgGetDouble("/environment/visibility-m");
|
|
|
|
// Control surface positions
|
|
SGPropertyNode *node = fgGetNode("/surface-positions", true);
|
|
net->elevator = node->getDoubleValue( "elevator-pos-norm" );
|
|
net->elevator_trim_tab
|
|
= node->getDoubleValue( "elevator-trim-tab-pos-norm" );
|
|
// FIXME: CLO 10/28/04 - This really should be separated out into 2 values
|
|
net->left_flap = node->getDoubleValue( "flap-pos-norm" );
|
|
net->right_flap = node->getDoubleValue( "flap-pos-norm" );
|
|
net->left_aileron = node->getDoubleValue( "left-aileron-pos-norm" );
|
|
net->right_aileron = node->getDoubleValue( "right-aileron-pos-norm" );
|
|
net->rudder = node->getDoubleValue( "rudder-pos-norm" );
|
|
net->rudder = node->getDoubleValue( "nose-wheel-pos-norm" );
|
|
net->speedbrake = node->getDoubleValue( "speedbrake-pos-norm" );
|
|
net->spoilers = node->getDoubleValue( "spoilers-pos-norm" );
|
|
|
|
if ( net_byte_order ) {
|
|
// Convert the net buffer to network format
|
|
net->version = htonl(net->version);
|
|
|
|
htond(net->longitude);
|
|
htond(net->latitude);
|
|
htond(net->altitude);
|
|
htonf(net->agl);
|
|
htonf(net->phi);
|
|
htonf(net->theta);
|
|
htonf(net->psi);
|
|
htonf(net->alpha);
|
|
htonf(net->beta);
|
|
|
|
htonf(net->phidot);
|
|
htonf(net->thetadot);
|
|
htonf(net->psidot);
|
|
htonf(net->vcas);
|
|
htonf(net->climb_rate);
|
|
htonf(net->v_north);
|
|
htonf(net->v_east);
|
|
htonf(net->v_down);
|
|
htonf(net->v_wind_body_north);
|
|
htonf(net->v_wind_body_east);
|
|
htonf(net->v_wind_body_down);
|
|
|
|
htonf(net->A_X_pilot);
|
|
htonf(net->A_Y_pilot);
|
|
htonf(net->A_Z_pilot);
|
|
|
|
htonf(net->stall_warning);
|
|
htonf(net->slip_deg);
|
|
|
|
for ( i = 0; i < net->num_engines; ++i ) {
|
|
net->eng_state[i] = htonl(net->eng_state[i]);
|
|
htonf(net->rpm[i]);
|
|
htonf(net->fuel_flow[i]);
|
|
htonf(net->egt[i]);
|
|
htonf(net->cht[i]);
|
|
htonf(net->mp_osi[i]);
|
|
htonf(net->tit[i]);
|
|
htonf(net->oil_temp[i]);
|
|
htonf(net->oil_px[i]);
|
|
}
|
|
net->num_engines = htonl(net->num_engines);
|
|
|
|
for ( i = 0; i < net->num_tanks; ++i ) {
|
|
htonf(net->fuel_quantity[i]);
|
|
}
|
|
net->num_tanks = htonl(net->num_tanks);
|
|
|
|
for ( i = 0; i < net->num_wheels; ++i ) {
|
|
net->wow[i] = htonl(net->wow[i]);
|
|
htonf(net->gear_pos[i]);
|
|
htonf(net->gear_steer[i]);
|
|
htonf(net->gear_compression[i]);
|
|
}
|
|
net->num_wheels = htonl(net->num_wheels);
|
|
|
|
net->cur_time = htonl( net->cur_time );
|
|
net->warp = htonl( net->warp );
|
|
htonf(net->visibility);
|
|
|
|
htonf(net->elevator);
|
|
htonf(net->elevator_trim_tab);
|
|
htonf(net->left_flap);
|
|
htonf(net->right_flap);
|
|
htonf(net->left_aileron);
|
|
htonf(net->right_aileron);
|
|
htonf(net->rudder);
|
|
htonf(net->nose_wheel);
|
|
htonf(net->speedbrake);
|
|
htonf(net->spoilers);
|
|
}
|
|
}
|
|
|
|
|
|
void FGNetFDM2Props( FGNetFDM *net, bool net_byte_order ) {
|
|
unsigned int i;
|
|
|
|
if ( net_byte_order ) {
|
|
// Convert to the net buffer from network format
|
|
net->version = ntohl(net->version);
|
|
|
|
htond(net->longitude);
|
|
htond(net->latitude);
|
|
htond(net->altitude);
|
|
htonf(net->agl);
|
|
htonf(net->phi);
|
|
htonf(net->theta);
|
|
htonf(net->psi);
|
|
htonf(net->alpha);
|
|
htonf(net->beta);
|
|
|
|
htonf(net->phidot);
|
|
htonf(net->thetadot);
|
|
htonf(net->psidot);
|
|
htonf(net->vcas);
|
|
htonf(net->climb_rate);
|
|
htonf(net->v_north);
|
|
htonf(net->v_east);
|
|
htonf(net->v_down);
|
|
htonf(net->v_wind_body_north);
|
|
htonf(net->v_wind_body_east);
|
|
htonf(net->v_wind_body_down);
|
|
|
|
htonf(net->A_X_pilot);
|
|
htonf(net->A_Y_pilot);
|
|
htonf(net->A_Z_pilot);
|
|
|
|
htonf(net->stall_warning);
|
|
htonf(net->slip_deg);
|
|
|
|
net->num_engines = htonl(net->num_engines);
|
|
for ( i = 0; i < net->num_engines; ++i ) {
|
|
net->eng_state[i] = htonl(net->eng_state[i]);
|
|
htonf(net->rpm[i]);
|
|
htonf(net->fuel_flow[i]);
|
|
htonf(net->egt[i]);
|
|
htonf(net->cht[i]);
|
|
htonf(net->mp_osi[i]);
|
|
htonf(net->tit[i]);
|
|
htonf(net->oil_temp[i]);
|
|
htonf(net->oil_px[i]);
|
|
}
|
|
|
|
net->num_tanks = htonl(net->num_tanks);
|
|
for ( i = 0; i < net->num_tanks; ++i ) {
|
|
htonf(net->fuel_quantity[i]);
|
|
}
|
|
|
|
net->num_wheels = htonl(net->num_wheels);
|
|
for ( i = 0; i < net->num_wheels; ++i ) {
|
|
net->wow[i] = htonl(net->wow[i]);
|
|
htonf(net->gear_pos[i]);
|
|
htonf(net->gear_steer[i]);
|
|
htonf(net->gear_compression[i]);
|
|
}
|
|
|
|
net->cur_time = htonl(net->cur_time);
|
|
net->warp = ntohl(net->warp);
|
|
htonf(net->visibility);
|
|
|
|
htonf(net->elevator);
|
|
htonf(net->elevator_trim_tab);
|
|
htonf(net->left_flap);
|
|
htonf(net->right_flap);
|
|
htonf(net->left_aileron);
|
|
htonf(net->right_aileron);
|
|
htonf(net->rudder);
|
|
htonf(net->nose_wheel);
|
|
htonf(net->speedbrake);
|
|
htonf(net->spoilers);
|
|
}
|
|
|
|
if ( net->version == FG_NET_FDM_VERSION ) {
|
|
// cout << "pos = " << net->longitude << " " << net->latitude << endl;
|
|
// cout << "sea level rad = " << cur_fdm_state->get_Sea_level_radius()
|
|
// << endl;
|
|
cur_fdm_state->_updateGeodeticPosition( net->latitude,
|
|
net->longitude,
|
|
net->altitude
|
|
* SG_METER_TO_FEET );
|
|
if ( net->agl > -9000 ) {
|
|
cur_fdm_state->_set_Altitude_AGL( net->agl * SG_METER_TO_FEET );
|
|
} else {
|
|
double agl_m = net->altitude
|
|
- cur_fdm_state->get_Runway_altitude_m();
|
|
cur_fdm_state->_set_Altitude_AGL( agl_m * SG_METER_TO_FEET );
|
|
}
|
|
cur_fdm_state->_set_Euler_Angles( net->phi,
|
|
net->theta,
|
|
net->psi );
|
|
cur_fdm_state->_set_Alpha( net->alpha );
|
|
cur_fdm_state->_set_Beta( net->beta );
|
|
cur_fdm_state->_set_Euler_Rates( net->phidot,
|
|
net->thetadot,
|
|
net->psidot );
|
|
cur_fdm_state->_set_V_calibrated_kts( net->vcas );
|
|
cur_fdm_state->_set_Climb_Rate( net->climb_rate );
|
|
cur_fdm_state->_set_Velocities_Local( net->v_north,
|
|
net->v_east,
|
|
net->v_down );
|
|
cur_fdm_state->_set_Velocities_Wind_Body( net->v_wind_body_north,
|
|
net->v_wind_body_east,
|
|
net->v_wind_body_down );
|
|
|
|
cur_fdm_state->_set_Accels_Pilot_Body( net->A_X_pilot,
|
|
net->A_Y_pilot,
|
|
net->A_Z_pilot );
|
|
|
|
fgSetDouble( "/sim/alarms/stall-warning", net->stall_warning );
|
|
fgSetDouble( "/instrumentation/slip-skid-ball/indicated-slip-skid",
|
|
net->slip_deg );
|
|
fgSetBool( "/instrumentation/slip-skid-ball/override", true );
|
|
|
|
for ( i = 0; i < net->num_engines; ++i ) {
|
|
SGPropertyNode *node = fgGetNode( "engines/engine", i, true );
|
|
|
|
// node->setBoolValue("running", t->isRunning());
|
|
// node->setBoolValue("cranking", t->isCranking());
|
|
|
|
// cout << net->eng_state[i] << endl;
|
|
if ( net->eng_state[i] == 0 ) {
|
|
node->setBoolValue( "cranking", false );
|
|
node->setBoolValue( "running", false );
|
|
} else if ( net->eng_state[i] == 1 ) {
|
|
node->setBoolValue( "cranking", true );
|
|
node->setBoolValue( "running", false );
|
|
} else if ( net->eng_state[i] == 2 ) {
|
|
node->setBoolValue( "cranking", false );
|
|
node->setBoolValue( "running", true );
|
|
}
|
|
|
|
node->setDoubleValue( "rpm", net->rpm[i] );
|
|
node->setDoubleValue( "fuel-flow-gph", net->fuel_flow[i] );
|
|
node->setDoubleValue( "egt-degf", net->egt[i] );
|
|
node->setDoubleValue( "cht-degf", net->cht[i] );
|
|
node->setDoubleValue( "mp-osi", net->mp_osi[i] );
|
|
node->setDoubleValue( "tit", net->tit[i] );
|
|
node->setDoubleValue( "oil-temperature-degf", net->oil_temp[i] );
|
|
node->setDoubleValue( "oil-pressure-psi", net->oil_px[i] );
|
|
}
|
|
|
|
for (i = 0; i < net->num_tanks; ++i ) {
|
|
SGPropertyNode * node
|
|
= fgGetNode("/consumables/fuel/tank", i, true);
|
|
node->setDoubleValue("level-gal_us", net->fuel_quantity[i] );
|
|
}
|
|
|
|
for (i = 0; i < net->num_wheels; ++i ) {
|
|
SGPropertyNode * node = fgGetNode("/gear/gear", i, true);
|
|
node->setDoubleValue("wow", net->wow[i] );
|
|
node->setDoubleValue("position-norm", net->gear_pos[i] );
|
|
node->setDoubleValue("steering-norm", net->gear_steer[i] );
|
|
node->setDoubleValue("compression-norm", net->gear_compression[i] );
|
|
}
|
|
|
|
/* these are ignored for now ... */
|
|
/*
|
|
if ( net->cur_time ) {
|
|
fgSetLong("/sim/time/cur-time-override", net->cur_time);
|
|
}
|
|
|
|
globals->set_warp( net->warp );
|
|
last_warp = net->warp;
|
|
*/
|
|
|
|
SGPropertyNode *node = fgGetNode("/surface-positions", true);
|
|
node->setDoubleValue("elevator-pos-norm", net->elevator);
|
|
node->setDoubleValue("elevator-trim-tab-pos-norm",
|
|
net->elevator_trim_tab);
|
|
// FIXME: CLO 10/28/04 - This really should be separated out
|
|
// into 2 values
|
|
node->setDoubleValue("flap-pos-norm", net->left_flap);
|
|
node->setDoubleValue("flap-pos-norm", net->right_flap);
|
|
node->setDoubleValue("left-aileron-pos-norm", net->left_aileron);
|
|
node->setDoubleValue("right-aileron-pos-norm", net->right_aileron);
|
|
node->setDoubleValue("rudder-pos-norm", net->rudder);
|
|
node->setDoubleValue("nose-wheel-pos-norm", net->nose_wheel);
|
|
node->setDoubleValue("speedbrake-pos-norm", net->speedbrake);
|
|
node->setDoubleValue("spoilers-pos-norm", net->spoilers);
|
|
} else {
|
|
SG_LOG( SG_IO, SG_ALERT,
|
|
"Error: version mismatch in FGNetFDM2Props()" );
|
|
SG_LOG( SG_IO, SG_ALERT,
|
|
"\tread " << net->version << " need " << FG_NET_FDM_VERSION );
|
|
SG_LOG( SG_IO, SG_ALERT,
|
|
"\tNeeds to upgrade net_fdm.hxx and recompile." );
|
|
}
|
|
}
|
|
|
|
|
|
// process work for this port
|
|
bool FGNativeFDM::process() {
|
|
SGIOChannel *io = get_io_channel();
|
|
int length = sizeof(buf);
|
|
|
|
if ( get_direction() == SG_IO_OUT ) {
|
|
// cout << "size of cur_fdm_state = " << length << endl;
|
|
FGProps2NetFDM( &buf );
|
|
if ( ! io->write( (char *)(& buf), length ) ) {
|
|
SG_LOG( SG_IO, SG_ALERT, "Error writing data." );
|
|
return false;
|
|
}
|
|
} else if ( get_direction() == SG_IO_IN ) {
|
|
if ( io->get_type() == sgFileType ) {
|
|
if ( io->read( (char *)(& buf), length ) == length ) {
|
|
SG_LOG( SG_IO, SG_DEBUG, "Success reading data." );
|
|
FGNetFDM2Props( &buf );
|
|
}
|
|
} else {
|
|
int result;
|
|
result = io->read( (char *)(& buf), length );
|
|
while ( result == length ) {
|
|
if ( result == length ) {
|
|
SG_LOG( SG_IO, SG_DEBUG, " Success reading data." );
|
|
FGNetFDM2Props( &buf );
|
|
}
|
|
result = io->read( (char *)(& buf), length );
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// close the channel
|
|
bool FGNativeFDM::close() {
|
|
SGIOChannel *io = get_io_channel();
|
|
|
|
set_enabled( false );
|
|
|
|
if ( ! io->close() ) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|