[med-svn] [aghermann] 60/60: lua-enabled rk1968 part 1/2
andrei zavada
hmmr-guest at alioth.debian.org
Mon Nov 4 23:50:02 UTC 2013
This is an automated email from the git hooks/post-receive script.
hmmr-guest pushed a commit to branch WIP
in repository aghermann.
commit f8d71732a9f86c1d10b21a8968b5d205a5c0b121
Author: Andrei Zavada <hmmr at ra>
Date: Tue Nov 5 01:49:25 2013 +0200
lua-enabled rk1968 part 1/2
---
debian/control | 10 +-
upstream/data/mw.glade | 6 +
upstream/data/sf-rk1968.glade | 5 +-
upstream/src/aghermann/rk1968/rk1968.cc | 309 +++++++++++++++++++++-----
upstream/src/aghermann/rk1968/rk1968.hh | 56 +++--
upstream/src/aghermann/ui/sf/channel.cc | 2 +-
upstream/src/aghermann/ui/sf/d/rk1968.cc | 6 +
upstream/src/aghermann/ui/sf/d/rk1968_cb.cc | 41 +++-
upstream/src/libmetrics/page-metrics-base.hh | 2 +
9 files changed, 357 insertions(+), 80 deletions(-)
diff --git a/debian/control b/debian/control
index 3ea3af1..bea6c56 100644
--- a/debian/control
+++ b/debian/control
@@ -2,8 +2,15 @@ Source: aghermann
Section: science
Priority: optional
Maintainer: Andrei Zavada <johnhommer at gmail.com>
-Build-Depends: debhelper (>= 9), dh-autoreconf, hardening-wrapper, hardening-includes, autoconf-archive, libgomp1, libconfig++-dev, pkg-config, libgsl0-dev, libfftw3-dev, libsamplerate0-dev (>= 0.1.7), libgtk-3-dev, libitpp-dev, libunique-3.0-dev, libxml2-utils, libvte-2.90-dev
+Build-Depends:
+ debhelper (>= 9), dh-autoreconf, hardening-wrapper, hardening-includes, autoconf-archive, pkg-config,
+ libgomp1, libconfig++-dev, libgsl0-dev, libfftw3-dev, libsamplerate0-dev (>= 0.1.7),
+ libgtk-3-dev, libitpp-dev, libunique-3.0-dev, libvte-2.90-dev,
+ liblua-dev (>= 5.2.0)
+ libxml2-utils
+
Standards-Version: 3.9.4
+
Homepage: http://johnhommer.com/academic/code/aghermann
Vcs-Git: git://git.debian.org/git/debian-med/aghermann.git
Vcs-Browser: http://anonscm.debian.org/gitweb/?p=debian-med/aghermann.git
@@ -18,4 +25,3 @@ Description: Sleep-research experiment manager
drawing on screen or to file; conventional PSD and EEG Micrcontinuity
profiles; Independent Component Analysis; artifact detection; and
Process S simulation following Achermann et al, 1993.
-
diff --git a/upstream/data/mw.glade b/upstream/data/mw.glade
index c2fab7a..74a4f21 100644
--- a/upstream/data/mw.glade
+++ b/upstream/data/mw.glade
@@ -7482,6 +7482,12 @@ dragging individual signals with <i>Alt</i>.</small></property
<child>
<placeholder/>
</child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
</object>
</child>
<child>
diff --git a/upstream/data/sf-rk1968.glade b/upstream/data/sf-rk1968.glade
index 6b8588c..3d6c0b6 100644
--- a/upstream/data/sf-rk1968.glade
+++ b/upstream/data/sf-rk1968.glade
@@ -206,6 +206,9 @@
<property name="xalign">0</property>
<property name="label" translatable="yes">Script:</property>
</object>
+ <packing>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
</child>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow1">
@@ -228,7 +231,7 @@
</child>
</object>
<packing>
- <property name="expand">False</property>
+ <property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
diff --git a/upstream/src/aghermann/rk1968/rk1968.cc b/upstream/src/aghermann/rk1968/rk1968.cc
index f572e8b..b075d41 100644
--- a/upstream/src/aghermann/rk1968/rk1968.cc
+++ b/upstream/src/aghermann/rk1968/rk1968.cc
@@ -18,6 +18,7 @@
extern "C" {
#include <lua.h>
+#include <lualib.h>
#include <lauxlib.h>
}
@@ -36,29 +37,50 @@ using namespace agh::rk1968;
using agh::global::lua_state;
+
+CScoreAssistant::
+CScoreAssistant (const string& name_,
+ TExpDirLevel level_, CExpDesign& ED_, const SExpDirLevelId& level_id_)
+ : CStorablePPack (common_subdir, name_, level_, ED_, level_id_),
+ status (0)
+{
+ if ( load() )
+ APPLOG_WARN ("no script loaded from \"%s\"", path().c_str());
+}
+
+CScoreAssistant::
+~CScoreAssistant ()
+{
+ save();
+}
+
+
+
int
CScoreAssistant::
load()
{
+ script_contents.clear();
+
string full_path = path();
- ostringstream acc;
ifstream oleg (full_path);
- while ( oleg.good() )
- acc << oleg;
- script_contents = acc.str();
+ char b[888];
+ while ( oleg.good() ) {
+ auto n = oleg.readsome( b, 888);
+ if ( n == 0 )
+ break;
+ b[n] = 0;
+ script_contents += b;
+ }
- // there is luaL_loadfile, but we would like to keep the contents, too
- int ret1 = luaL_loadbuffer(
- lua_state,
- script_contents.c_str(),
- script_contents.size(),
- full_path.c_str());
- return ret1;
+ return script_contents.empty()
+ ? (status |= TFlags::enofile, -1)
+ : (status &= ~TFlags::enofile, 0);
}
int
CScoreAssistant::
-save()
+save() const
{
ofstream oleg (path());
oleg << script_contents;
@@ -66,63 +88,242 @@ save()
return 0;
}
+
int
CScoreAssistant::
-score( agh::SEpisode& E, size_t* n_pages_scored_p)
+compile()
{
- // make various signal metrics available for lua
- // and execute a script
-
- size_t n_pages_scored = 0;
-
- sigfile::CHypnogram& F1 = E.sources.front();
-
- // 1. prepare input vectors
- forward_list<valarray<TFloat>>
- courses_delta,
- courses_theta;
- {
- forward_list<agh::CRecording*> HH;
- for ( auto &R : E.recordings )
- if ( R.second.psd_profile.have_data() )
- HH.push_front( &R.second);
- if ( HH.empty() )
- return -1;
-
- for ( auto &H : HH ) {
- courses_delta.emplace_front(
- H->psd_profile.course(
- agh::CExpDesign::freq_bands[metrics::TBand::delta][0],
- agh::CExpDesign::freq_bands[metrics::TBand::delta][1]));
- courses_theta.emplace_front(
- H->psd_profile.course(
- agh::CExpDesign::freq_bands[metrics::TBand::theta][0],
- agh::CExpDesign::freq_bands[metrics::TBand::theta][1]));
- }
+ if ( script_contents.empty() ) {
+ APPLOG_WARN ("no script to compile");
+ status |= TFlags::ecompile;
+ return -1;
}
- // 2.
- for ( size_t p = 0; p < F1.n_pages(); ++p ) {
- // luaL_put...
- lua_pcall(
- agh::global::lua_state,
- 2, // nargsin
- 1, // nargsout
- 0);
- int score; // = luaL_get...
- F1[p].mark( (sigfile::SPage::TScore) score);
+ lua_settop( lua_state, 0);
+ int ret1 = luaL_loadbuffer(
+ lua_state,
+ script_contents.c_str(),
+ script_contents.size(),
+ path().c_str());
+ if ( ret1 ) {
+ const char* errmsg = lua_tostring( lua_state, -1);
+ APPLOG_WARN ("compilation failed: %s (%d)", errmsg, ret1);
+ status |= TFlags::ecompile;
+ } else
+ status &= ~TFlags::ecompile;
+
+ return ret1;
+}
+
+
+extern "C" {
+static int host_get_data( lua_State*);
+static int host_mark_page( lua_State*);
+}
+
+
+
+CScoreAssistant::TScoreErrors
+CScoreAssistant::
+score( agh::SEpisode& E, int* n_pages_scored_p)
+{
+ sepisodep = &E;
+
+ lua_settop( lua_state, 0);
+ if ( compile() )
+ return TScoreErrors::no_script;
+
+ lua_pushlightuserdata( lua_state, this);
+ lua_pushcfunction( lua_state, host_get_data);
+ lua_pushcfunction( lua_state, host_mark_page);
+
+ int call_result = lua_pcall(
+ lua_state,
+ 3, // nargsin
+ 1, // nargsout, expecting the number of pages scored (positive), or some error code (negative)
+ 0);
+ if ( call_result ) {
+ APPLOG_WARN ("script call failed (%d)", call_result);
+ return TScoreErrors::lua_call_error;
}
+ int ret2 = lua_tointeger( lua_state, -1);
+ //lua_pop( lua_state, 1); // zapping stack anyway
+
if ( n_pages_scored_p )
- *n_pages_scored_p = n_pages_scored;
+ *n_pages_scored_p = ret2;
+
+ return TScoreErrors::ok;
+}
+
- return 0;
+enum TOpcode {
+ op_noop = 0, // 0 is sort of invalid in lua
+
+ // 1. Informational.
+ // The script, it is assumed, will have some storage to
+ // save channel setup data between calls.
+ op_get_channel_list,
+ op_get_channel_list_of_type,
+ op_get_channel_data,
+
+ // 2. Metrics describing a page.
+ // This is supposed to convey, to the script, what a
+ // properly RK1968-trained technician "sees".
+ op_get_psd = 100,
+ op_get_mc,
+ op_get_swu,
+};
+
+
+
+extern "C" {
+
+static int
+host_get_data( lua_State *L)
+{
+ int nargsin = lua_gettop(L) - 2; // the first two being, a CScoreAssistant* and opcode
+
+ auto this_p = (CScoreAssistant*)lua_touserdata( L, 1);
+ if ( !this_p ) {
+ lua_pushfstring( L, "Target CScoreAssistant object is NULL");
+ lua_error(L);
+ }
+ auto& E = *this_p->sepisodep;
+
+ int opcode = lua_tointeger( L, 2);
+ auto make_arity_mismatch_return = [&L, &nargsin, &opcode] ( int correct_nargsin)
+ {
+ lua_pushboolean( L, false);
+ lua_pushfstring( L, "Bad arity for opcode %d (need %d, got %d)", opcode, correct_nargsin, nargsin);
+ lua_error(L);
+ };
+ auto make_error_return = [&L] ( const char* fmt, ...) __attribute__ ((format (printf, 2, 3)))
+ {
+ lua_pushboolean( L, false);
+ va_list ap;
+ va_start (ap, fmt);
+ lua_pushvfstring( L, fmt, ap);
+ va_end (ap);
+ lua_error(L);
+ };
+#define NEED_ARITY(A) \
+ if ( nargsin != A ) { make_arity_mismatch_return(A); return 2; }
+
+ switch ( opcode ) {
+ case op_noop:
+ return 0;
+
+ case op_get_channel_list: {
+ NEED_ARITY(0);
+
+ list<const char*> A;
+ for ( auto& H : E.recordings )
+ A.push_back( H.first.c_str());
+ lua_pushinteger( L, E.recordings.size());
+ lua_pushstring( L, agh::str::join( A, ";").c_str());
+ return 2;
+ }
+
+ case op_get_channel_list_of_type: {
+ NEED_ARITY(1);
+
+ const char* type = lua_tostring( L, 3);
+
+ list<agh::CRecording*> A;
+ for ( auto& H : E.recordings )
+ if ( 0 == strcmp( H.first.type_s(), type) )
+ A.push_back( &H.second);
+ lua_pushinteger( L, E.recordings.size());
+ lua_pushstring( L, agh::str::join( A, ";").c_str());
+ return 2;
+ }
+
+ case op_get_channel_data: {
+ NEED_ARITY(1);
+
+ const char* channel = lua_tostring( L, 3);
+ auto Hi = E.recordings.find( sigfile::SChannel (channel));
+ if ( Hi == E.recordings.end() ) {
+ make_error_return( "No such channel (%s)", channel);
+ } else {
+ auto& R = Hi->second;
+ lua_pushinteger( L, R.F().samplerate(R.h()));
+ return 1;
+ }
+ }
+
+ case op_get_psd: {
+ NEED_ARITY(4);
+
+ const char* channel = lua_tostring( L, 3);
+ auto Hi = E.recordings.find( sigfile::SChannel (channel));
+ if ( Hi == E.recordings.end() ) {
+ make_error_return( "No such channel (%s)", channel);
+ } else {
+ auto& R = Hi->second;
+ int page = lua_tointeger( L, 4);
+ if ( page < 0 || (size_t)page >= R.total_pages() ) {
+ make_error_return( "Page %d out of range (%zu is last)", page, R.total_pages());
+ }
+ double fa = lua_tonumber( L, 5);
+ double fz = lua_tonumber( L, 6);
+ if ( fa >= fz ) {
+ make_error_return( "Invalid frequency range for PSD");
+ }
+
+ lua_pushnumber( L, R.psd_profile.course( fa, fz)[page]);
+ return 1;
+ }
+ }
+
+ case op_get_mc:
+
+ case op_get_swu:
+
+ default:
+ make_error_return( "Invalid host opcode %d", opcode);
+ }
+#undef NEED_ARITY
+
+ return 0; // unreachable because lua_error has a longjmp
}
+static int
+host_mark_page( lua_State *L)
+{
+ //int nargsin = lua_gettop(L);
+ auto this_p = (CScoreAssistant*)lua_touserdata( L, 1);
+ if ( !this_p ) {
+ lua_pushfstring( L, "Target CScoreAssistant object is NULL");
+ lua_error(L);
+ }
+
+ int page = lua_tonumber( L, 2);
+ int scorecode = lua_tonumber( L, 3);
+
+ auto& E = *this_p->sepisodep;
+ using namespace sigfile;
+ CHypnogram& F1 = E.sources.front();
+ if ( page < 0 || (size_t)page >= F1.n_pages() ) {
+ lua_pushfstring( L, "Invalid page number (%d)", page);
+ lua_error(L);
+ }
+ if ( SPage::score_code((SPage::TScore)scorecode) == '?' ) {
+ lua_pushfstring( L, "Invalid score code (%d)", scorecode);
+ lua_error(L);
+ }
+
+ F1[page].mark( (SPage::TScore)scorecode);
+
+ return 0;
+}
+
+} // extern "C"
// Local Variables:
// Mode: c++
diff --git a/upstream/src/aghermann/rk1968/rk1968.hh b/upstream/src/aghermann/rk1968/rk1968.hh
index b54469a..6582786 100644
--- a/upstream/src/aghermann/rk1968/rk1968.hh
+++ b/upstream/src/aghermann/rk1968/rk1968.hh
@@ -15,6 +15,10 @@
#include <float.h>
#include <string>
+extern "C" {
+#include <lua.h>
+}
+
#include "aghermann/expdesign/dirlevel.hh"
#include "libsigproc/sigproc.hh"
@@ -30,23 +34,18 @@ class CScoreAssistant
static constexpr const char* common_subdir = ".rk1968/";
CScoreAssistant (const string& name_,
- TExpDirLevel level_, CExpDesign& ED_, const SExpDirLevelId& level_id_)
- : CStorablePPack (common_subdir, name_, level_, ED_, level_id_)
- {
- load();
- }
+ TExpDirLevel, CExpDesign&, const SExpDirLevelId&);
explicit CScoreAssistant (const CScoreAssistant& rv)
: CStorablePPack (common_subdir, rv.name + " (dup)", TExpDirLevel::transient, rv.ED, rv.level_id),
+ status (0),
script_contents (rv.script_contents)
{}
explicit CScoreAssistant (CExpDesign& ED_, const SExpDirLevelId& level_id_)
- : CStorablePPack (common_subdir, "(unnamed)", TExpDirLevel::transient, ED_, level_id_)
+ : CStorablePPack (common_subdir, "(unnamed)", TExpDirLevel::transient, ED_, level_id_),
+ status (0)
{}
- ~CScoreAssistant ()
- {
- save();
- }
+ ~CScoreAssistant ();
CScoreAssistant&
operator=( CScoreAssistant&& rv)
@@ -67,18 +66,49 @@ class CScoreAssistant
return script_contents == rv.script_contents;
}
+ enum TFlags { enofile = 1 << 0, ecompile = 1 << 1, };
+ int status;
+
// this one is not libconfig-based, so override these
int load();
- int save();
+ int save() const;
string script_contents;
-
- int score( agh::SEpisode&, size_t* pages_scored_successfuly);
+ int compile();
+
+ enum class TScoreErrors {
+ ok,
+ no_script,
+ no_eeg_channels,
+ lua_call_error
+ };
+ static const char*
+ score_error_s( TScoreErrors x)
+ {
+ switch ( x ) {
+ case TScoreErrors::ok: return "OK";
+ case TScoreErrors::no_script: return "No script compiled";
+ case TScoreErrors::no_eeg_channels: return "No EEG channels";
+ case TScoreErrors::lua_call_error: return "Lua script call failed";
+ default: return "(unknown error code)";
+ }
+ }
+ TScoreErrors
+ score( agh::SEpisode&, int* pages_scored_p);
+
+ // private:
+ // friend extern "C" int agh::rk1968::host_get_data( lua_State*);
+ // friend extern "C" int agh::rk1968::host_mark_page( lua_State*);
+ agh::SEpisode*
+ sepisodep;
+
};
+
+
template <typename T>
pair<T, size_t>
emg_steady_tone( const sigproc::SSignalRef<T>& V, size_t steady_secs, double max_dev_factor)
diff --git a/upstream/src/aghermann/ui/sf/channel.cc b/upstream/src/aghermann/ui/sf/channel.cc
index 241d251..6f3be71 100644
--- a/upstream/src/aghermann/ui/sf/channel.cc
+++ b/upstream/src/aghermann/ui/sf/channel.cc
@@ -159,7 +159,7 @@ SChannel (agh::CRecording& r,
(size_t)roundf((raw_profile[i] - hist_range_min) / hist_binsize),
(size_t)0, (size_t)hist_bins-1) ];
- static const size_t steady_secs = 10;
+ static const size_t steady_secs = 30;
static const double max_dev_factor = 1.1;
size_t steady_tone_at;
tie (emg_steady_tone, steady_tone_at) =
diff --git a/upstream/src/aghermann/ui/sf/d/rk1968.cc b/upstream/src/aghermann/ui/sf/d/rk1968.cc
index 7f5ef41..ef8a6df 100644
--- a/upstream/src/aghermann/ui/sf/d/rk1968.cc
+++ b/upstream/src/aghermann/ui/sf/d/rk1968.cc
@@ -68,6 +68,12 @@ SRK1968Dialog (SScoringFacility& p_)
gtk_combo_box_set_model_properly(
eSFRKProfileList, mSFRKProfiles);
+ {
+ auto font_desc = pango_font_description_from_string( "Mono 10");
+ gtk_widget_override_font( (GtkWidget*)eSFRKScript, font_desc);
+ pango_font_description_free( font_desc);
+ }
+
G_CONNECT_1 (wSFRK, show);
G_CONNECT_1 (wSFRK, hide);
diff --git a/upstream/src/aghermann/ui/sf/d/rk1968_cb.cc b/upstream/src/aghermann/ui/sf/d/rk1968_cb.cc
index eaf534a..2ea819f 100644
--- a/upstream/src/aghermann/ui/sf/d/rk1968_cb.cc
+++ b/upstream/src/aghermann/ui/sf/d/rk1968_cb.cc
@@ -10,6 +10,7 @@
*/
#include "rk1968.hh"
+#include "aghermann/globals.hh"
using namespace std;
using namespace agh::ui;
@@ -27,14 +28,16 @@ wSFRK_show_cb(
// 1. profiles
RK.load_profiles();
+ APPLOG_INFO ("loaded %zu scripts", RK.profiles.size());
if ( RK.profiles.empty() )
RK.profiles.emplace_back(
*SF._p.ED,
agh::SExpDirLevelId {SF._p.ED->group_of(SF.csubject()), SF.csubject().id, SF.session()});
- RK.current_profile = RK.profiles.begin();
RK.populate_combo();
+ if ( gtk_combo_box_get_active( RK.eSFRKProfileList) == -1 )
+ gtk_combo_box_set_active( RK.eSFRKProfileList, 0);
RK.set_profile_manage_buttons_visibility();
// 2. service backup
@@ -81,6 +84,15 @@ eSFRK_any_profile_value_changed_cb(
const gpointer userdata)
{
auto& RK = *(SScoringFacility::SRK1968Dialog*)userdata;
+
+ RK.W_V.down();
+ if ( RK.Pp2.compile() ) {
+ gtk_widget_set_sensitive( (GtkWidget*)RK.bSFRKPreview, FALSE);
+ // do some beeping perhaps?
+ } else {
+ gtk_widget_set_sensitive( (GtkWidget*)RK.bSFRKPreview, TRUE);
+ }
+
RK.eX_any_profile_value_changed_cb();
}
@@ -99,19 +111,30 @@ bSFRKPreview_toggled_cb(
SBusyBlock bb (RK.wSFRK);
RK.W_V.down();
- size_t pages_scored_successfuly;
- if ( 0 == RK.Pp2.score( SF.sepisode(), &pages_scored_successfuly) )
+ int pages_scored_successfuly;
+ auto res = RK.Pp2.score( SF.sepisode(), &pages_scored_successfuly);
+ if ( res == agh::rk1968::CScoreAssistant::TScoreErrors::ok ) {
+ SF.get_hypnogram();
+ gtk_widget_set_sensitive(
+ (GtkWidget*)RK.bSFRKApply,
+ TRUE);
gtk_label_set_markup(
RK.lSFRKBottomInfo,
snprintf_buf(
- "<small>Scored %zu page%s</small>", pages_scored_successfuly, (pages_scored_successfuly == 1) ? "" : "s"));
- else
+ "<small>Scored %d page%s</small>",
+ pages_scored_successfuly, (pages_scored_successfuly == 1) ? "" : "s"));
+ } else {
+ pop_ok_message(
+ (GtkWindow*)RK.wSFRK,
+ "Scoring script problem",
+ "%s", agh::rk1968::CScoreAssistant::score_error_s(res));
gtk_label_set_markup(
RK.lSFRKBottomInfo, "Hm. Something is wrong.");
-
- SF.get_hypnogram();
-
- gtk_widget_set_sensitive( (GtkWidget*)RK.bSFRKApply, TRUE);
+ RK.suppress_preview_handler = true;
+ gtk_toggle_button_set_active( button, FALSE);
+ RK.suppress_preview_handler = false;
+ return;
+ }
} else {
SF.sepisode().sources.front().pages() = RK.backup;
diff --git a/upstream/src/libmetrics/page-metrics-base.hh b/upstream/src/libmetrics/page-metrics-base.hh
index c7eb984..42ddb88 100644
--- a/upstream/src/libmetrics/page-metrics-base.hh
+++ b/upstream/src/libmetrics/page-metrics-base.hh
@@ -36,6 +36,8 @@ __attribute__ ((pure))
name( TType t)
{
switch ( t ) {
+ case TType::raw:
+ return "raw";
case TType::psd:
return "PSD";
case TType::mc:
--
Alioth's /git/debian-med/git-commit-notice on /srv/git.debian.org/git/debian-med/aghermann.git
More information about the debian-med-commit
mailing list