* Added guard lock class - impacts upon
    - xmlrpc class implementation
    - wefax implementation
  * This ensures that a mutex is always unlocked when
    leaving a function or block
pull/2/head
David Freese 2011-11-13 09:49:02 -06:00
rodzic c2ba559f1c
commit 50fa414165
7 zmienionych plików z 668 dodań i 90 usunięć

Wyświetl plik

@ -124,4 +124,28 @@ bool thread_in_list(int id, const int* list);
#include "fl_lock.h"
/// This ensures that a mutex is always unlocked when leaving a function or block.
class guard_lock
{
public:
guard_lock(pthread_mutex_t* m);
~guard_lock(void);
private:
pthread_mutex_t* mutex;
};
/// This wraps together a mutex and a condition variable which are used
/// together very often for queues etc...
class syncobj
{
pthread_mutex_t m_mutex ;
pthread_cond_t m_cond ;
public:
syncobj();
~syncobj();
pthread_mutex_t * mtxp(void) { return & m_mutex; }
void signal();
bool wait( double seconds );
};
#endif // !THREADS_H_

Wyświetl plik

@ -35,6 +35,8 @@ public:
static void setpicture_link(wefax *me);
static void save_image(const std::string & fil_name, const std::string & extra_comments);
static void power( double start, double phase, double image, double black, double stop );
static void send_image( const std::string & fil_name );
static void restore_max_lines(void);
};
#endif // _WEFAX_PIC_H

Wyświetl plik

@ -28,6 +28,11 @@ class wefax : public modem {
/// For updating the logbook when loading/saving an image file.
cQsoRec m_qso_rec ;
/// Non-copiable object.
wefax ();
wefax ( const wefax & );
wefax & operator=( const wefax & );
public:
wefax (trx_mode md);
virtual ~wefax ();
@ -103,6 +108,27 @@ public:
{
m_adif_log = the_flag ;
}
/// Helper string indicating the internal state of the wefax engine.
std::string state_string(void) const;
/// Maximum wait time when getting information about received and sent files.
static const int max_delay = 3600 * 24 * 365 ;
/// Called by the engine when a file is received.
void put_received_file(const std::string & filnam);
/// Used by XML-RPC to get the list of received files.
std::string get_received_file(double max_seconds=max_delay);
/// Called by XML-RPC to send a file which resides on the machine where fldigi runs.
std::string send_file( const std::string & filnam, double max_seconds=max_delay);
/// Called before sending a file. Transmitting is an exclusive process.
bool transmit_lock_acquire( const std::string & filnam, double max_seconds=max_delay);
/// Called after sending a file so another sending can take place.
void transmit_lock_release( const std::string & err_msg );
};
#endif

Wyświetl plik

@ -21,7 +21,9 @@
// ----------------------------------------------------------------------------
#include <config.h>
#include <stdexcept>
#include <string.h>
#include <errno.h>
#include "threads.h"
THREAD_ID_TYPE thread_id_;
@ -99,3 +101,50 @@ void linux_log_tid(void)
LOG_DEBUG(PACKAGE_TARNAME " thread %" PRIdPTR " is LWP %ld", GET_THREAD_ID(), syscall(SYS_gettid));
}
#endif
// Synchronization objects.
guard_lock::guard_lock(pthread_mutex_t* m) : mutex(m)
{
pthread_mutex_lock(mutex);
}
guard_lock::~guard_lock(void)
{
pthread_mutex_unlock(mutex);
}
syncobj::syncobj()
{
pthread_mutex_init( & m_mutex, NULL );
pthread_cond_init( & m_cond, NULL );
}
syncobj::~syncobj()
{
pthread_mutex_destroy( & m_mutex );
pthread_cond_destroy( & m_cond );
}
void syncobj::signal()
{
int rc = pthread_cond_signal( &m_cond );
if( rc )
{
throw std::runtime_error(strerror(rc));
}
}
bool syncobj::wait( double seconds )
{
int rc = pthread_cond_timedwait_rel( &m_cond, &m_mutex, seconds );
switch( rc )
{
case 0 : return true ;
default : throw std::runtime_error(strerror(rc));
case ETIMEDOUT: return false ;
}
}

Wyświetl plik

@ -57,6 +57,8 @@
#include "waterfall.h"
#include "macros.h"
#include "qrunner.h"
#include "wefax.h"
#include "wefax-pic.h"
#if USE_HAMLIB
#include "hamlib.h"
@ -189,16 +191,8 @@ ostream& XML_RPC_Server::list_methods(ostream& out)
// =============================================================================
// Methods that change the server state must call XMLRPC_LOCK
class xmlrpc_lock
{
public:
xmlrpc_lock(pthread_mutex_t* m) : mutex(m) { pthread_mutex_lock(mutex); }
~xmlrpc_lock(void) { pthread_mutex_unlock(mutex); }
private:
pthread_mutex_t* mutex;
};
#define XMLRPC_LOCK SET_THREAD_ID(XMLRPC_TID); xmlrpc_lock autolock_(server_mutex)
// guard_lock (include/threads.h) ensures that mutex are always unlocked.
#define XMLRPC_LOCK SET_THREAD_ID(XMLRPC_TID); guard_lock autolock_(server_mutex)
// =============================================================================
@ -2433,6 +2427,206 @@ public:
// =============================================================================
// Returns the current wefax modem pointer.
static wefax * get_wefax(void)
{
if( ( active_modem->get_mode() >= MODE_WEFAX_FIRST )
&& ( active_modem->get_mode() <= MODE_WEFAX_LAST ) )
{
wefax * ptr = dynamic_cast<wefax *>( active_modem );
if( ptr == NULL ) throw runtime_error("Inconsistent wefax object");
return ptr ;
}
throw runtime_error("Not in wefax mode");
}
struct Wefax_state_string : public xmlrpc_c::method
{
Wefax_state_string() {
_signature = "s:n";
_help = "Returns Wefax engine state (tx and rx) for information."; }
void execute(const xmlrpc_c::paramList& params, xmlrpc_c::value* retval)
try
{
*retval = xmlrpc_c::value_string( get_wefax()->state_string() );
}
catch( const exception & e )
{
*retval = xmlrpc_c::value_string( e.what());
}
};
struct Wefax_skip_apt : public xmlrpc_c::method
{
Wefax_skip_apt() {
_signature = "s:n";
_help = "Skip APT during Wefax reception"; }
void execute(const xmlrpc_c::paramList& params, xmlrpc_c::value* retval)
try
{
get_wefax()->skip_apt();
*retval = xmlrpc_c::value_string( "" );
}
catch( const exception & e )
{
*retval = xmlrpc_c::value_string( e.what() );
}
};
/// TODO: Refresh the screen with the new value.
struct Wefax_skip_phasing : public xmlrpc_c::method
{
Wefax_skip_phasing() {
_signature = "s:n";
_help = "Skip phasing during Wefax reception"; }
void execute(const xmlrpc_c::paramList& params, xmlrpc_c::value* retval)
try
{
get_wefax()->skip_phasing(true);
*retval = xmlrpc_c::value_string( "" );
}
catch( const exception & e )
{
*retval = xmlrpc_c::value_string( e.what() );
}
};
// TODO: The image should be reloaded just like cancelling from the GUI.
struct Wefax_set_tx_abort_flag : public xmlrpc_c::method
{
Wefax_set_tx_abort_flag() {
_signature = "s:n";
_help = "Cancels Wefax image transmission"; }
void execute(const xmlrpc_c::paramList& params, xmlrpc_c::value* retval)
try
{
get_wefax()->set_tx_abort_flag();
*retval = xmlrpc_c::value_string( "" );
}
catch( const exception & e )
{
*retval = xmlrpc_c::value_string( e.what() );
}
};
struct Wefax_end_reception : public xmlrpc_c::method
{
Wefax_end_reception() {
_signature = "s:n";
_help = "End Wefax image reception"; }
void execute(const xmlrpc_c::paramList& params, xmlrpc_c::value* retval)
try
{
get_wefax()->end_reception();
*retval = xmlrpc_c::value_string( "" );
}
catch( const exception & e )
{
*retval = xmlrpc_c::value_string( e.what() );
}
};
struct Wefax_start_manual_reception : public xmlrpc_c::method
{
Wefax_start_manual_reception() {
_signature = "s:n";
_help = "Starts fax image reception in manual mode"; }
void execute(const xmlrpc_c::paramList& params, xmlrpc_c::value* retval)
try
{
get_wefax()->set_rx_manual_mode(true);
get_wefax()->skip_apt();
get_wefax()->skip_phasing(true);
*retval = xmlrpc_c::value_string( "" );
}
catch( const exception & e )
{
*retval = xmlrpc_c::value_string( e.what() );
}
};
struct Wefax_set_adif_log : public xmlrpc_c::method
{
Wefax_set_adif_log() {
_signature = "s:b";
_help = "Set/reset logging to received/transmit images to ADIF log file"; }
void execute(const xmlrpc_c::paramList& params, xmlrpc_c::value* retval)
try
{
get_wefax()->set_adif_log( params.getBoolean(0));
*retval = xmlrpc_c::value_string( "" );
}
catch( const exception & e )
{
*retval = xmlrpc_c::value_string( e.what() );
}
};
struct Wefax_set_max_lines : public xmlrpc_c::method
{
Wefax_set_max_lines() {
_signature = "s:i";
_help = "Set maximum lines for fax image reception"; }
void execute(const xmlrpc_c::paramList& params, xmlrpc_c::value* retval)
try
{
get_wefax()->set_max_lines( params.getInt(0));
/// This updates the GUI.
REQ( wefax_pic::restore_max_lines );
*retval = xmlrpc_c::value_string( "" );
}
catch( const exception & e )
{
*retval = xmlrpc_c::value_string( e.what() );
}
};
struct Wefax_get_received_file : public xmlrpc_c::method
{
Wefax_get_received_file() {
_signature = "s:i";
_help = "Waits for next received fax file, returns its name with a delay. Empty string if timeout."; }
void execute(const xmlrpc_c::paramList& params, xmlrpc_c::value* retval)
try
{
std::string filename = get_wefax()->get_received_file( params.getInt(0));
*retval = xmlrpc_c::value_string( filename );
}
catch( const exception & e )
{
*retval = xmlrpc_c::value_string( e.what() );
}
};
struct Wefax_send_file : public xmlrpc_c::method
{
Wefax_send_file() {
_signature = "s:si";
_help = "Send file. returns an empty string if OK otherwise an error message."; }
void execute(const xmlrpc_c::paramList& params, xmlrpc_c::value* retval)
try
{
std::string status = get_wefax()->send_file( params.getString(0), params.getInt(1) );
*retval = xmlrpc_c::value_string( status );
}
catch( const exception & e )
{
*retval = xmlrpc_c::value_string( e.what() );
}
};
// =============================================================================
// End XML-RPC interface
// method list: ELEM_(class_name, "method_name")
@ -2586,7 +2780,18 @@ public:
ELEM_(Spot_get_auto, "spot.get_auto") \
ELEM_(Spot_set_auto, "spot.set_auto") \
ELEM_(Spot_toggle_auto, "spot.toggle_auto") \
ELEM_(Spot_pskrep_get_count, "spot.pskrep.get_count")
ELEM_(Spot_pskrep_get_count, "spot.pskrep.get_count") \
\
ELEM_(Wefax_state_string, "wefax.state_string") \
ELEM_(Wefax_skip_apt, "wefax.skip_apt") \
ELEM_(Wefax_skip_phasing, "wefax.skip_phasing") \
ELEM_(Wefax_set_tx_abort_flag, "wefax.set_tx_abort_flag") \
ELEM_(Wefax_end_reception, "wefax.end_reception") \
ELEM_(Wefax_start_manual_reception, "wefax.start_manual_reception") \
ELEM_(Wefax_set_adif_log, "wefax.set_adif_log") \
ELEM_(Wefax_set_max_lines, "wefax.set_max_lines") \
ELEM_(Wefax_get_received_file, "wefax.get_received_file") \
ELEM_(Wefax_send_file, "wefax.send_file") \
struct rm_pred

Wyświetl plik

@ -91,14 +91,20 @@ static Fl_Choice *wefax_choice_tx_lpm = (Fl_Choice *)0;
static Fl_Round_Button *wefax_round_tx_adif_log = (Fl_Round_Button *)0;
static Fl_Button *wefax_btn_tx_send_color = (Fl_Button *)0;
static Fl_Button *wefax_btn_tx_send_grey = (Fl_Button *)0;
static Fl_Output *wefax_out_tx_row_num = (Fl_Output *)0;
static Fl_Button *wefax_btn_tx_send_abort = (Fl_Button *)0;
static Fl_Button *wefax_btn_tx_load = (Fl_Button *)0;
static Fl_Button *wefax_btn_tx_clear = (Fl_Button *)0;
static Fl_Button *wefax_btn_tx_close = (Fl_Button *)0;
static Fl_Shared_Image *wefax_shared_tx_img = (Fl_Shared_Image *)0;
static unsigned char *wefax_xmtimg = (unsigned char *)0;
static unsigned char *wefax_xmtpicbuff = (unsigned char *)0;
/// This indicates whether an image to send is loaded in the GUI.
/// It allows to acquire twice when re-loading an image without sending.
static bool wefax_image_loaded_in_gui = false ;
/// Used for shifting the received image left and right.
static volatile int center_val_prev = 0 ;
@ -144,6 +150,43 @@ static const int mini_max_fax_lines = 1000 ;
/// However, when the printing resumes, the position is not altered.
static volatile bool reception_paused = false ;
/// Sets the label of the received or sent image.
static void set_win_label( Fl_Double_Window * wefax_pic, const std::string & lab)
{
char* label = strdup(lab.c_str());
wefax_pic->copy_label(label);
free(label);
wefax_pic->redraw();
}
/// Called when clearing the image to send.
static void clear_image(void)
{
if (wefax_xmtimg)
{
delete [] wefax_xmtimg;
wefax_xmtimg = NULL ;
}
if (wefax_shared_tx_img) {
wefax_shared_tx_img->release();
wefax_shared_tx_img = 0;
}
set_win_label(wefax_pic_tx_win,"");
}
/// Clears the loaded image. It allows XML-RPC clients to send an image.
static void wefax_cb_pic_tx_clear( Fl_Widget *, void *)
{
FL_LOCK_D();
wefax_image_loaded_in_gui = false ;
clear_image();
wefax_pic_tx_picture->clear();
wefax_pic::restart_tx_viewer();
/// Now the lock can be acquired by XML-RPC.
wefax_serviceme->transmit_lock_release( "Cleared" );
FL_UNLOCK_D();
}
static void wefax_cb_pic_tx_close( Fl_Widget *, void *)
{
FL_LOCK_D();
@ -386,8 +429,7 @@ void wefax_pic::update_rx_pic_bw(unsigned char data, int pix_pos )
/// Prints the row number sometimes only, to save CPU.
if( ( pix_pos % 1000 ) == 0 ) {
char row_num_buffer[20];
snprintf( row_num_buffer, sizeof(row_num_buffer),
"%d", row_number );
snprintf( row_num_buffer, sizeof(row_num_buffer), "%d", row_number );
wefax_out_rx_row_num->value( row_num_buffer );
}
@ -439,6 +481,15 @@ static void wefax_cb_pic_rx_resume( Fl_Widget *, void *)
wefax_serviceme->update_rx_label();
}
static void LocalSleep( int seconds )
{
#ifdef __MINGW32__
MilliSleep(seconds);
#else
usleep(100000*seconds);
#endif
}
/// Displays the latest image file saved.
static void add_to_files_list( const std::string & the_fil_nam )
{
@ -459,22 +510,21 @@ static void add_to_files_list( const std::string & the_fil_nam )
/// This window is hidden/shown to signal that a file was added.
static const int nb_blink = 5 ;
for( int ix_blink = 0 ; ix_blink < nb_blink ; ++ix_blink ) {
#ifdef __MINGW32__
wefax_browse_rx_events->hide();
wefax_browse_rx_events->redraw();
MilliSleep(1);
LocalSleep(1);
wefax_browse_rx_events->show();
wefax_browse_rx_events->redraw();
MilliSleep(1);
#else
wefax_browse_rx_events->hide();
wefax_browse_rx_events->redraw();
usleep(100000);
wefax_browse_rx_events->show();
wefax_browse_rx_events->redraw();
usleep(100000);
#endif
LocalSleep(1);
}
/// If there is not directory specification, adds the default dir.
if( the_fil_nam.empty() )
LOG_WARN("Empty file name");
else if( the_fil_nam[0] != '/' )
wefax_serviceme->put_received_file( progdefaults.wefax_save_dir + '/' + the_fil_nam );
else
wefax_serviceme->put_received_file( the_fil_nam );
};
static void wefax_cb_pic_rx_abort( Fl_Widget *, void *)
@ -628,7 +678,7 @@ static void wefax_cb_rx_set_filter( Fl_Widget *, void * )
wefax_serviceme->set_rx_filter(ix_filter);
}
static void restore_max_lines(void)
void wefax_pic::restore_max_lines(void)
{
char buf_max_lines[20];
snprintf( buf_max_lines, sizeof(buf_max_lines), "%d", wefax_serviceme->get_max_lines() );
@ -643,15 +693,15 @@ static void wefax_cb_pic_max_lines( Fl_Widget *, void * )
/// The value given by FLTK should be an integer, but better to be sure.
if( 1 != sscanf( ptr_val_gui, "%d", &max_val_gui ) ) {
LOG_ERROR( "Cannot parse: %s", ptr_val_gui ) ;
restore_max_lines();
LOG_ERROR( _("Cannot parse: %s"), ptr_val_gui ) ;
wefax_pic::restore_max_lines();
return ;
}
/// Faxes should not be too small otherwise we will spend all our time
/// saving automatic image files.
if( max_val_gui < mini_max_fax_lines ) {
restore_max_lines();
wefax_pic::restore_max_lines();
return ;
}
@ -952,7 +1002,7 @@ void wefax_pic::create_rx_viewer(void)
/// This buffer will never change so it does not matter if it is static.
static char tooltip_max_lines[256];
snprintf(tooltip_max_lines, sizeof(tooltip_max_lines),
"Maximum number of lines per image. Must be bigger than %d",
"Maximum number of lines per image. Minimum value is %d",
mini_max_fax_lines );
wefax_int_rx_max->tooltip(tooltip_max_lines);
@ -1068,11 +1118,7 @@ void wefax_pic::set_rx_label(const std::string & win_label)
}
/// This copy seems strange, but otherwise the label is not updated.
char* label = strdup(tmp_label.c_str());
wefax_pic_rx_win->copy_label(label);
free(label);
wefax_pic_rx_win->redraw_label();
set_win_label(wefax_pic_rx_win, tmp_label);
FL_UNLOCK_D();
}
@ -1090,33 +1136,31 @@ void wefax_pic::save_image(const std::string & fil_name, const std::string & ext
wefax_serviceme->qso_rec_save();
}
static void wefax_load_image(const char *fil_name)
/// Protected by an exclusive mutex.
static std::string wefax_load_image_after_acquire(const char * fil_name)
{
if (wefax_serviceme != active_modem) return;
if (wefax_serviceme != active_modem) return "Not in WEFAX mode";
wefax_serviceme->qso_rec_init();
qso_notes( "TX:", fil_name );
if (wefax_shared_tx_img) {
wefax_shared_tx_img->release();
wefax_shared_tx_img = 0;
}
clear_image();
wefax_shared_tx_img = Fl_Shared_Image::get(fil_name);
if (!wefax_shared_tx_img) {
LOG_ERROR("Could not Fl_Shared_Image::get %s", fil_name);
return;
std::string err_msg("Cannot call Fl_Shared_Image::get on file:" + std::string(fil_name) );
LOG_ERROR("%s",err_msg.c_str());
return err_msg;
}
if (wefax_shared_tx_img->count() > 1) { // we only handle rgb images
LOG_ERROR("Handle only RGB images: %s", fil_name );
wefax_shared_tx_img->release();
wefax_shared_tx_img = 0;
return;
std::string err_msg("Handle only RGB images: " + std::string(fil_name) );
LOG_ERROR("%s",err_msg.c_str());
clear_image();
return err_msg;
}
unsigned char * img_data = (unsigned char *)wefax_shared_tx_img->data()[0];
int img_wid = wefax_shared_tx_img->w();
int img_hei = wefax_shared_tx_img->h();
int img_depth = wefax_shared_tx_img->d();
if (wefax_xmtimg) delete [] wefax_xmtimg;
wefax_xmtimg = new unsigned char [img_wid * img_hei * bytes_per_pix];
if (img_depth == bytes_per_pix)
memcpy(wefax_xmtimg, img_data, img_wid*img_hei*bytes_per_pix);
@ -1135,22 +1179,23 @@ static void wefax_load_image(const char *fil_name)
wefax_xmtimg[j] = wefax_xmtimg[j+1] = wefax_xmtimg[j+2] = img_data[i];
}
} else {
LOG_ERROR( "Inconsistent img_depth=%d\n", img_depth );
exit(EXIT_FAILURE);
std::stringstream err_strm ;
err_strm << "Inconsistent img_depth=" << img_depth << " for " << fil_name ;
std::string err_msg = err_strm.str();
LOG_ERROR("%s",err_msg.c_str());
return err_msg ;
};
wefax_pic::tx_viewer_resize(img_wid, img_hei);
char* label = strdup(fil_name);
wefax_pic_tx_win->copy_label(basename(label));
free(label);
set_win_label(wefax_pic_tx_win, fil_name);
wefax_pic_tx_box->label(0);
// load the picture widget with the rgb image
FL_LOCK_D();
wefax_pic_tx_picture->show();
wefax_pic_tx_picture->clear();
wefax_pic_tx_win->redraw();
wefax_pic_tx_picture->video(wefax_xmtimg, img_wid * img_hei * bytes_per_pix);
int tim_color = wefax_serviceme->tx_time( img_wid * img_hei * bytes_per_pix );
@ -1167,7 +1212,22 @@ static void wefax_load_image(const char *fil_name)
wefax_btn_tx_send_grey->tooltip(wefax_txgry_tooltip);
wefax_btn_tx_send_grey->activate();
wefax_btn_tx_clear->activate();
FL_UNLOCK_D();
return std::string();
}
static void wefax_load_image(const char * fil_name)
{
if (wefax_serviceme != active_modem) return;
if( false == wefax_image_loaded_in_gui )
{
/// So we do not re-acquire the exclusive access to wefax transmission.
wefax_serviceme->transmit_lock_acquire(fil_name);
wefax_image_loaded_in_gui = true ;
}
wefax_load_image_after_acquire(fil_name);
}
void wefax_pic::set_tx_pic(unsigned char data, int col, int row, int tx_img_col, bool is_color )
@ -1190,6 +1250,15 @@ void wefax_pic::set_tx_pic(unsigned char data, int col, int row, int tx_img_col,
wefax_pic_tx_picture->pixel( data, tripleOffset + 1 );
wefax_pic_tx_picture->pixel( data, tripleOffset + 2 );
}
static int previous_row = -1 ;
if( row != previous_row )
{
previous_row = row ;
char row_num_buffer[20];
snprintf( row_num_buffer, sizeof(row_num_buffer), "%d", row );
wefax_out_tx_row_num->value( row_num_buffer );
}
}
static void wefax_cb_pic_tx_load(Fl_Widget *, void *)
@ -1218,13 +1287,18 @@ static void wefax_pic_tx_send_common(
FL_LOCK_D();
wefax_choice_tx_lpm->hide();
wefax_round_tx_adif_log->hide();
wefax_btn_tx_send_color->hide();
wefax_btn_tx_send_grey->hide();
wefax_btn_tx_load->hide();
wefax_btn_tx_clear->hide();
wefax_btn_tx_close->hide();
wefax_out_tx_row_num->show();
wefax_btn_tx_send_abort->show();
wefax_pic_tx_picture->clear();
wefax_out_tx_row_num->value( "Init" );
wefax_serviceme->set_tx_parameters(
get_choice_lpm_value( wefax_choice_tx_lpm ),
wefax_xmtpicbuff,
@ -1306,12 +1380,27 @@ static void wefax_cb_pic_tx_adif_log( Fl_Widget *, void *)
void wefax_pic::restart_tx_viewer(void)
{
wefax_out_tx_row_num->hide();
wefax_btn_tx_send_abort->hide();
wefax_choice_tx_lpm->show();
wefax_btn_tx_send_color->show();
wefax_btn_tx_send_grey->show();
wefax_round_tx_adif_log->show();
wefax_btn_tx_load->show();
wefax_btn_tx_close->show();
if( wefax_image_loaded_in_gui )
{
// If the image was loaded from the GUI.
wefax_choice_tx_lpm->show();
wefax_btn_tx_send_color->show();
wefax_btn_tx_send_grey->show();
wefax_btn_tx_clear->show();
}
else
{
/// If the image was loaded and sent from XML-RPC, or no image present.
wefax_choice_tx_lpm->deactivate();
wefax_btn_tx_send_color->deactivate();
wefax_btn_tx_send_grey->deactivate();
wefax_btn_tx_clear->deactivate();
}
}
void wefax_pic::create_tx_viewer(void)
@ -1351,7 +1440,7 @@ void wefax_pic::create_tx_viewer(void)
static const int hei_tx_btn = 24 ;
int width_btn = 0;
int width_offset = 35;
int width_offset = 30;
width_btn = 70 ;
wefax_choice_tx_lpm = make_lpm_choice( width_offset, y_btn, width_btn, hei_tx_btn );
@ -1366,33 +1455,46 @@ void wefax_pic::create_tx_viewer(void)
wefax_round_tx_adif_log->align(FL_ALIGN_LEFT);
width_offset += width_btn ;
width_btn = 60 ;
wefax_btn_tx_send_color = new Fl_Button(width_offset, y_btn, width_btn, hei_tx_btn, "Xmt Color");
width_btn = 50 ;
wefax_btn_tx_send_color = new Fl_Button(width_offset, y_btn, width_btn, hei_tx_btn, "Tx Color");
wefax_btn_tx_send_color->callback(wefax_cb_pic_tx_send_color, 0);
width_offset += width_btn ;
width_btn = 60 ;
wefax_btn_tx_send_grey = new Fl_Button(width_offset, y_btn, width_btn, hei_tx_btn, "Xmt Grey");
width_btn = 50 ;
wefax_btn_tx_send_grey = new Fl_Button(width_offset, y_btn, width_btn, hei_tx_btn, "Tx B/W");
wefax_btn_tx_send_grey->callback( wefax_cb_pic_tx_send_grey, 0);
width_offset += width_btn ;
width_btn = 40 ;
width_btn = 35 ;
wefax_btn_tx_load = new Fl_Button(width_offset, y_btn, width_btn, hei_tx_btn, _("Load"));
wefax_btn_tx_load->callback(wefax_cb_pic_tx_load, 0);
wefax_btn_tx_load->tooltip("Load image to send");
width_offset += width_btn ;
width_btn = 40 ;
width_btn = 35 ;
wefax_btn_tx_clear = new Fl_Button(width_offset, y_btn, width_btn, hei_tx_btn, _("Clear"));
wefax_btn_tx_clear->callback(wefax_cb_pic_tx_clear, 0);
width_offset += width_btn ;
width_btn = 35 ;
wefax_btn_tx_close = new Fl_Button(width_offset, y_btn, width_btn, hei_tx_btn, _("Close"));
wefax_btn_tx_close->callback(wefax_cb_pic_tx_close, 0);
wefax_btn_tx_send_abort = new Fl_Button(84, y_btn, 122, hei_tx_btn, "Abort Xmt");
static const char * title_row_num = "" ;
width_offset += fl_width( title_row_num );
wefax_out_tx_row_num = new Fl_Output(20, y_btn, 100, hei_tx_btn, _(title_row_num));
wefax_out_tx_row_num->align(FL_ALIGN_LEFT);
wefax_out_tx_row_num->tooltip("Fax line number being sent.");
wefax_btn_tx_send_abort = new Fl_Button(180, y_btn, 122, hei_tx_btn, "Abort Xmt");
wefax_btn_tx_send_abort->callback(wefax_cb_pic_tx_send_abort, 0);
wefax_btn_tx_send_abort->tooltip("Abort transmission");
wefax_out_tx_row_num->hide();
wefax_btn_tx_send_abort->hide();
wefax_btn_tx_send_color->deactivate();
wefax_btn_tx_send_grey->deactivate();
wefax_btn_tx_clear->deactivate();
wefax_pic_tx_win->end();
FL_UNLOCK_D();
@ -1511,4 +1613,21 @@ void wefax_pic::cb_mnu_pic_viewer(Fl_Menu_ *, void *) {
}
}
/// Called from XML-RPC thread.
void wefax_pic::send_image( const std::string & fil_nam )
{
LOG_INFO("%s", fil_nam.c_str() );
/// Here, transmit_lock_acquire is called by the XML-RPC client.
std::string err_msg = wefax_load_image_after_acquire( fil_nam.c_str() );
if( ! err_msg.empty() )
{
if (wefax_serviceme == active_modem)
{
/// Allows another XML-RPC client or the GUI to send an image.
wefax_serviceme->transmit_lock_release( err_msg );
}
return ;
}
wefax_cb_pic_tx_send_grey( NULL, NULL );
}

Wyświetl plik

@ -33,6 +33,8 @@
#include <valarray>
#include <cmath>
#include <cstddef>
#include <queue>
#include <map>
#include <libgen.h>
#include "debug.h"
@ -277,7 +279,7 @@ static const char * state_to_str(fax_state a_state)
case TXIMAGE : return "Sending image" ;
case IDLE : return "Idle" ;
}
return "UNKOWN" ;
return "UNKNOWN" ;
};
/// TODO: This should be hidden to this class.
@ -351,12 +353,6 @@ class fax_implementation {
double m_i_fir_old;
double m_q_fir_old;
/// Returns a string telling the RX state. Mostly for debugging.
const char * state_rx_str(void) const
{
return state_to_str(m_rx_state);
};
void decode(const int* buf, int nb_samples);
/// Used for transmission.
@ -378,6 +374,7 @@ class fax_implementation {
m_img_sample = 0;
m_pix_samples_nb = 0;
}
public:
fax_implementation(int fax_mode, wefax * ptr_wefax );
void init_rx(int the_smpl_rate);
@ -395,7 +392,7 @@ public:
int img_w,
int img_h,
int xmt_bytes );
void trx_do_next(void);
bool trx_do_next(void);
void tx_apt_stop(void);
double carrier(void) const
@ -446,6 +443,21 @@ public:
/// This generates a filename based on the frequency, current time, internal state etc...
std::string generate_filename( const char *extra_msg ) const ;
std::string state_rx_str(void) const
{
return state_to_str(m_rx_state);
}
/// Returns a string telling the state. Informational purpose.
std::string state_string(void) const
{
std::stringstream result ;
result << "tx:" << state_to_str(m_tx_state)
<< " "
<< "rx:" << state_to_str(m_rx_state);
return result.str();
};
private:
/// Centered around the frequency.
double power_usb_noise(void) const
@ -685,9 +697,107 @@ private:
void decode_apt(int x, const fax_signal & the_signal );
void decode_phasing(int x, const fax_signal & the_signal );
bool decode_image(int x);
private:
/// Each received file has its name pushed in this queue.
syncobj m_sync_rx ;
std::queue< std::string > m_received_files ;
public:
/// Called by the engine each time a file is saved.
void put_received_file( const std::string &filnam )
{
guard_lock g( m_sync_rx.mtxp() );
LOG_INFO("%s", filnam.c_str() );
m_received_files.push( filnam );
m_sync_rx.signal();
}
/// Returns a received file name, by chronological order.
std::string get_received_file( double max_seconds )
{
guard_lock g( m_sync_rx.mtxp() );
LOG_INFO("delay=%f", max_seconds );
if( m_received_files.empty() )
{
if( ! m_sync_rx.wait(max_seconds) ) return std::string();
}
std::string filnam = m_received_files.front();
m_received_files.pop();
return filnam ;
}
private:
/// No unicity if the same filename is sent by several clients at the same time.
/// This does not really matter because the error codes should be the same.
syncobj m_sync_tx_fil ;
std::string m_tx_fil ; // Filename being transmitted.
syncobj m_sync_tx_msg ;
typedef std::map< std::string, std::string > sent_files_type ;
sent_files_type m_sent_files ;
public:
/// If the delay is exceeded, returns with an error message.
std::string send_file( const std::string & filnam, double max_seconds )
{
LOG_INFO("%s", filnam.c_str() );
bool is_acquired = transmit_lock_acquire(filnam, max_seconds);
if( ! is_acquired ) return "Timeout sending " + filnam ;
REQ( wefax_pic::send_image, filnam );
guard_lock g( m_sync_tx_fil.mtxp() );
sent_files_type::iterator itFi ;
while(true)
{
itFi = m_sent_files.find( filnam );
if( itFi != m_sent_files.end() )
{
break ;
}
if( ! m_sync_tx_msg.wait( max_seconds ) )
{
return "Timeout";
}
}
std::string err_msg = itFi->second ;
m_sent_files.erase( itFi );
return err_msg ;
}
/// Called when loading a file from the GUI, or indirectly from XML-RPC.
bool transmit_lock_acquire(const std::string & filnam, double delay = wefax::max_delay )
{
LOG_INFO("%s", filnam.c_str() );
guard_lock g( m_sync_tx_fil.mtxp() );
if ( ! m_tx_fil.empty() )
{
if( ! m_sync_tx_fil.wait( delay ) ) return false ;
}
m_tx_fil = filnam ;
return true ;
}
/// Allows to send another file. Called by XML-RPC and the GUI.
void transmit_lock_release( const std::string & err_msg )
{
guard_lock g( m_sync_tx_msg.mtxp() );
LOG_INFO("%s %s", m_tx_fil.c_str(), err_msg.c_str() );
if( m_tx_fil.empty() )
{
LOG_WARN("%s File name should not be empty", err_msg.c_str() );
}
else
{
m_sent_files[ m_tx_fil ] = err_msg ;
m_tx_fil.clear();
}
m_sync_tx_msg.signal();
}
}; // class fax_implementation
/// Narrow, middle etc... input filters. Constructed at program startup.
/// Narrow, middle etc... input filters. Constructed at program startup. Readonly.
fir_filter_pair_set fax_implementation::m_rx_filters ;
fax_implementation::fax_implementation( int fax_mode, wefax * ptr_wefax )
@ -704,6 +814,8 @@ fax_implementation::fax_implementation( int fax_mode, wefax * ptr_wefax )
m_start_duration = 5 ;
m_stop_duration = 5 ;
m_manual_mode = false ;
m_rx_state = IDLE ;
m_tx_state = IDLE ;
int index_of_correlation ;
/// http://en.wikipedia.org/wiki/Radiofax
@ -1124,7 +1236,7 @@ void fax_implementation::skip_apt_rx(void)
{
wefax_pic::skip_rx_apt();
if(m_rx_state!=RXAPTSTART) {
LOG_ERROR("Should be in APT state. State=%s. Manual=%d", state_rx_str(), m_manual_mode );
LOG_ERROR("Should be in APT state. State=%s. Manual=%d", state_rx_str().c_str(), m_manual_mode );
}
m_lpm_img=m_lpm_sum_rx=0;
m_rx_state=RXPHASING;
@ -1143,7 +1255,7 @@ void fax_implementation::skip_phasing_to_image(bool auto_center)
REQ( wefax_pic::skip_rx_phasing, auto_center );
if(m_rx_state!=RXPHASING) {
LOG_ERROR("Should be in phasing state. State=%s", state_rx_str() );
LOG_ERROR("Should be in phasing state. State=%s", state_rx_str().c_str() );
}
m_rx_state=RXIMAGE;
@ -1167,7 +1279,7 @@ void fax_implementation::skip_phasing_to_image(bool auto_center)
void fax_implementation::skip_phasing_rx(bool auto_center)
{
if(m_rx_state!=RXPHASING) {
LOG_ERROR("Should be in phasing state. State=%s", state_rx_str() );
LOG_ERROR("Should be in phasing state. State=%s", state_rx_str().c_str() );
}
skip_phasing_to_image(auto_center);
@ -1307,10 +1419,9 @@ void fax_implementation::modulate(const double* buffer, int number)
m_ptr_wefax->ModulateXmtr( stack_xmt_buf, number );
}
/// Returns the number of pixels written from the image.
void fax_implementation::trx_do_next(void)
/// Returns true if succesful
bool fax_implementation::trx_do_next(void)
{
LOG_DEBUG("m_xmt_bytes=%d m_lpm_img=%f", m_xmt_bytes, m_lpm_img );
/// The number of samples sent for one line. The LPM is given by the GUI.
@ -1321,8 +1432,8 @@ void fax_implementation::trx_do_next(void)
double buf[block_len];
bool end_of_loop = false ;
bool tx_completed = true ;
int curr_sample_idx = 0 , nb_samples_to_send = 0 ;
const char * curr_status_msg = "APT start" ;
for(int num_bytes_to_write=0; ; ++num_bytes_to_write ) {
bool disp_msg = ( ( num_bytes_to_write % block_len ) == 0 ) && ( num_bytes_to_write > 0 );
@ -1330,9 +1441,11 @@ void fax_implementation::trx_do_next(void)
if( disp_msg ) {
modulate( buf, num_bytes_to_write);
const char * curr_status_msg = state_to_str( m_tx_state );
/// TODO: Should be multiplied by 3 when sending in BW ?
if( m_ptr_wefax->is_tx_finished(curr_sample_idx, nb_samples_to_send, curr_status_msg ) ) {
end_of_loop = true ;
tx_completed = false;
continue ;
};
num_bytes_to_write = 0 ;
@ -1347,7 +1460,6 @@ void fax_implementation::trx_do_next(void)
curr_sample_idx++;
} else {
m_tx_state=TXPHASING;
curr_status_msg = "Phasing" ;
curr_sample_idx=0;
}
}
@ -1361,7 +1473,6 @@ void fax_implementation::trx_do_next(void)
curr_sample_idx++;
} else {
m_tx_state=ENDPHASING;
curr_status_msg = "End phasing" ;
curr_sample_idx=0;
}
}
@ -1372,7 +1483,6 @@ void fax_implementation::trx_do_next(void)
curr_sample_idx++;
} else {
m_tx_state=TXIMAGE;
curr_status_msg = "Sending image" ;
curr_sample_idx=0;
}
}
@ -1447,7 +1557,6 @@ void fax_implementation::trx_do_next(void)
}
} else {
m_tx_state=TXAPTSTOP;
curr_status_msg = "APT stop" ;
curr_sample_idx=0;
}
}
@ -1458,12 +1567,12 @@ void fax_implementation::trx_do_next(void)
curr_sample_idx++;
} else {
m_tx_state=IDLE;
curr_status_msg = "Finished" ;
end_of_loop = true ;
continue ;
}
}
} // loop
return tx_completed ;
}
void fax_implementation::tx_params_set(
@ -1724,14 +1833,20 @@ void wefax::set_tx_parameters(
/// Callback continuously called by fldigi modem class.
int wefax::tx_process()
{
m_impl->trx_do_next();
bool tx_was_completed = m_impl->trx_do_next();
std::string status ;
if( false == tx_was_completed ) {
status = "Transmission cancelled";
LOG_INFO("Sending cancelled" );
m_qso_rec.putField(NOTES, status.c_str() );
}
qso_rec_save();
REQ_FLUSH(GET_THREAD_ID());
FL_LOCK_E();
wefax_pic::restart_tx_viewer();
transmit_lock_release(status);
m_abortxmt = false;
FL_UNLOCK_E();
m_impl->tx_apt_stop();
@ -1779,10 +1894,10 @@ void wefax::set_lpm( int the_lpm )
return m_impl->lpm_set( the_lpm );
}
/// Transmission time in seconds.
/// Transmission time in seconds. Factor 3 if b/w image.
int wefax::tx_time( int nb_bytes ) const
{
return (double)nb_bytes / modem::samplerate ;
return (double)nb_bytes / ( modem::samplerate * 3.0 );
}
/// This prints a message about the progress of image sending,
@ -1825,6 +1940,7 @@ std::string wefax::suggested_filename(void) const
return m_impl->generate_filename( "gui" );
};
/// This creates a QSO record to be written to an adif file.
void wefax::qso_rec_init(void)
{
if( m_adif_log == false ) {
@ -1884,6 +2000,7 @@ void wefax::qso_rec_init(void)
// m_qso_rec.putField(TX_PWR, inpTX_pwr_log->value());
}
/// Called once a QSO rec has been filled with information. Saved to adif file.
void wefax::qso_rec_save(void)
{
if( m_adif_log == false ) {
@ -1914,3 +2031,39 @@ void wefax::set_freq(double)
modem::set_freq(m_impl->carrier());
}
// String telling the tx and rx internal state.
std::string wefax::state_string(void) const
{
return m_impl->state_string();
}
/// Called when a file is saved, so XML-RPC calls can get the filename.
void wefax::put_received_file( const std::string &filnam )
{
m_impl->put_received_file( filnam );
}
/// Returns a received file name, by chronological order.
std::string wefax::get_received_file( double max_seconds )
{
return m_impl->get_received_file( max_seconds );
}
/// This is not thread-safe, at all.
std::string wefax::send_file( const std::string & filnam, double max_seconds )
{
return m_impl->send_file( filnam, max_seconds );
}
/// Transmitting files is done in exclusive mode.
bool wefax::transmit_lock_acquire(const std::string & filnam, double max_seconds )
{
return m_impl->transmit_lock_acquire( filnam, max_seconds );
}
/// Called after a file is sent.
void wefax::transmit_lock_release( const std::string & err_msg )
{
m_impl->transmit_lock_release( err_msg );
}