[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