Вызов nanosleep () из сценария Lua приостановил поток QT GUI - PullRequest
7 голосов
/ 19 октября 2011

Я разрабатываю тестовый инструмент для генерации сигнала из параллельного порта ПК.Этот инструмент предназначен для генерации любого шаблона сигнала с точностью синхронизации мс, поэтому я использую скрипт Lua для определения шаблона сигнала, графический интерфейс запускает новый QThread для запуска сценария, когда пользователь нажимает кнопку [Пуск].

Следующие три функции для Lua реализованы как глобальные функции C ++:

  • pwrite: запись данных в параллельный порт.
  • msleep: ожидание определенных мс (реализовано с использованием nanosleep ())
  • print: перезаписать функцию печати Lua по умолчанию, она добавит сообщение в один виджет QTextEdit.

при вызове pwrite записанные данные сохраняются в глобальной переменной, затем отображается графический интерфейсобновляется с интервалом 20 мс для обновления данных параллельного порта в графическом интерфейсе.(это обновление с интервалом в 20 мсек не является хорошим дизайном, но я не понял, как использовать сигнал для обновления графического интерфейса при изменении данных).

Инструмент в настоящее время в основном функционален.Выходной сигнал не имеет проблем, но обновление данных параллельного порта имеет некоторые проблемы:

Когда Lua вызывает msleep, поток GUI останавливается, данные параллельного порта обновляются только после завершения msleep.

Итакмои вопросы:

  1. Как реализовать метод сна, чтобы он не мешал обновлению потока GUI?

  2. Как реализоватьpwrite, чтобы графический интерфейс мог получать сигнал для обновления данных параллельного порта при изменении записанных данных?

Запрограммировать графический интерфейс, как показано ниже: Program GUI

Соответствующий код:

    /* common.cpp file */

int L_MSleep(lua_State* l)
{
    int milisec=0;
    struct timespec req={0, 0};
    time_t sec;

    milisec=luaL_optint(l,1,0); // obtain parameter

    if (milisec==0)
       return 0;

    sec=(int)(milisec/1000);

    milisec=milisec-(sec*1000);
    req.tv_sec=sec;
    req.tv_nsec=milisec*1000000L;

    while(nanosleep(&req,&req)==-1)
         continue;

    return 1;
}


/* LuaRunner.cpp file */
LuaRunner::LuaRunner(QObject *parent) :
    QThread(parent)
{
    runlua = false;
}

void LuaRunner::run()
{
    QString err = "";

    runlua = true;
    LUA_RunScript(this->ff, err);
    runlua = false;

    if(err != "")
    {
        emit errorMessage(err);
    }
}

int LuaRunner::LUA_RunScript(QString ff, QString &err)
{
    L = lua_open();
    luaL_openlibs(L);

    if (luaL_loadfile(L, ff.toAscii()) || lua_pcall(L, 0, 0, 0))
    {
        err = QString(lua_tostring(L, -1));
        return -1;
    }

    lua_register(L, "ssleep", L_SSleep);
    lua_register(L, "msleep", L_MSleep);
    lua_register(L, "pwrite", L_PortWrite);
    lua_register(L, "print", L_Log);

    lua_getglobal(L, "dotest");
    if (!lua_isfunction(L, -1))
    {
        err = QString("Test function(dotest) should be a function");
        return -1;
    }

    if(lua_pcall(L, 0, 0, 0))
    {
        err = QString(lua_tostring(L, -1));
        return -1;
    }

    lua_close(L);

    return 0;
}

Ответы [ 2 ]

4 голосов
/ 29 мая 2012

Вы правильно запускаете скрипт Lua в специальной ветке. Это правильный способ сделать это - почти. Вы перезапускаете поток каждый раз, когда хотите запустить скрипт. Это неверно. Вы также получаете доступ к данным в потоке GUI из потока LUA без какой-либо синхронизации. Это не хорошо. Qt предоставляет отличный механизм для этого в виде соединений в очереди между сигналами и слотами. Когда вызов сигнального слота проходит границы потоков, параметры заключаются в QEvent и асинхронно доставляются к цели QObject. В каждом потоке доставка событий сериализуется, поэтому вам не нужно беспокоиться о повреждении данных и т. Д.

Вот как это должно быть сделано:

// LUAObject.h
#include <QObject>

class LUAObject : public QObject
{
   Q_OBJECT
public:
   LUAObject(QObject * parent = 0);
public slots:
   void setScript(const QString &);
   void runScript();
   void stop();

signals:
   void hasError(const QString &);
   void finished();
   void hasParallelData(int);
   void hasMessage(const QString &);

private:
   QString script;
   bool stop;
}

// LUAObject.cpp

// whatever Lua includes you need etc

LUAObject::LUAObject(QObject* p) : QObject(p)
{}

void LUAObject::stop() { stopped = true; }    

void LUAObject::setScript(const QString & scr)
{ script = scr; }

int L_PWrite(lua_State* l)
{
   int data = luaL_optint(l, 1, -1);
   if (data != -1) {
      // access the parallel port HERE, NOT in the GUI thread!
      emit hasParallelData(luaL_optint(l, 1, 0));
   }
   return 0;
}

// returns a bool - true means we are stopped and should exit
int L_MSleep(lua_State* l)
{
   int ms = luaL_optint(l, 1, -1);
   if (ms == -1) return 0;
   QApplication::processEvents(QEventLoop::WaitForMoreEvents, ms);
   lua_pushBoolean(l, stopped); // event processing would run the stop() slot call
   return 1;
}

int L_SSleep(lua_State* l)
{
   int secs = luaL_optint(l, 1, -1);
   if (secs == -1) return 0;
   QApplication::processEvents(QEventLoop::WaitForMoreEvents, secs*1000);
   lua_pushBoolean(l, stopped); // event processing would run the stop() slot call
   return 1;
}

int L_Log(lua_State* l)
{
   const char * msg = luaL_optstring(l, 1, 0);
   if (!msg) return 0;
   emit hasMessage(msg);
   return 0;
}

class Lua // RAII
{
public:
   explicit Lua(lua_state * l) : L(l) {}
   ~Lua() { lua_close(L); }
   operator lua_state*() const { return L; }
private:
   lua_state * L;
   Q_DISABLE_COPY(LUA)
};

LUAObject::runScript()
{
   stopped = false;
   Lua L(lua_open());
   luaL_openlibs(L);

   if (luaL_loadbuffer(L, script.toAscii().constData(), script.length(), "script") || lua_pcall(L, 0, 0, 0))
   {
       emit hasError(lua_tostring(L, -1));
       return;
   }

   lua_register(L, "ssleep", L_SSleep);
   lua_register(L, "msleep", L_MSleep);
   lua_register(L, "pwrite", L_PWrite);
   lua_register(L, "print", L_Log);

   lua_getglobal(L, "dotest");
   if (!lua_isfunction(L, -1))
   {
      emit hasError("Test function(dotest) should be a function");
      return;
   }

   if(lua_pcall(L, 0, 0, 0))
   {
      emit hasError(lua_tostring(L, -1));
      return;
   }

   emit finished();
}

// main.cpp

#include <QApplication>
#include <QMetaMethod>
#include "LUAObject.h"

...

int main(int argc, char** argv)
{
   QApplication(argc, argv);

   MainWindow window;

   ...
   QThread thread;
   LUAObject lua;
   thread.start(QThread::TimeCriticalPriority);
   lua.moveToThread(&thread);

   ...

   // NOTE: you can ONLY connect to LUAObject slots, you CANNOT call them
   // directly since it runs in a separate thread!
   connect(&window, SIGNAL(startClicked()), &lua, SLOT(runScript());
   connect(&lua, SIGNAL(hasError(QString)), &window, SLOT(luaError(QString)));

   ...
   window.show();
   int rc = qApp->exec();
   QMetaObject::invokeMethod(&lua, SLOT(stop())); // cross-thread slot invocation
   thread.exit();
   thread.wait();
   return rc;
}

Я оставляю реализацию интерфейса на ваше воображение. Обратите внимание, что это непроверенный код. Это может взорвать ваш компьютер, насколько я знаю.

0 голосов
/ 21 октября 2011

Возможно, вам следует использовать QT msleep , поскольку он предоставляется для QThread

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...