Доброго времени суток Всем!
Работаю над проектом, представляющим собой некую систему, собирающую данные с разных устройств. Данные предствлены в виде каналов. Устройства постоянно опрашиваются, данные в каналах обновляются. По происшествию каких-либо событий надо посылать команды на устройства.
Есть неоходимость ввести сценарии работы, определяющие эти события (events) и соответствующие обработчики (handlers). В качестве языка сценариев хочу использовать python.
Небольшое описание:
Сценарий будет представлять из себя python код, содержащийся, например в std::string
строке. У него будет функия pyInit()
, выполняемая при загрузке сценария. В pyInit()
будем создавать события и биндовать на них обработчики. Далее в рабочем цикле C++ сервер будет запускать события, которые могут вызывать обработчики (если событие произошло).
Событие с точки зрения C++ - это объект, содержащий указатель на python функцию, определяющую это событие и указатель на python функцию определяющую обработчик.
На самом деле в C++ будет две основных категории событий cppEvents и pythonEvents. pythonEvents - это как раз те события, которые будут определяться в скрипте. cppEvents - это определенные в C++ события, такие как например eventValueChanged. Т.е. определяем eventBase от которого наследуем eventPython, eventChanged.
Так же в python нужно экспортировать некоторые функции, такие как
auto getChannelValue(std::string chName)
bool sendCmd(std::sting cmd)
и т. д...
В качестве альтернативы python-у можно рассмотреть какой-нибудь другой язык сценариев, либо систему-плагинов, написанных на C++ и динамически подгружаемых. В случае плагинов получим быстродействие, в случае скриптов - простоту использования.
Вопросы:
- Как правильно передать в python скрипт? Те в каком виде у меня должен быть сценарий (например в виде std::string или файла)?
- Как вызвать функцию python из C++ кода, не завершая далее python окружения? Что бы в дальнейшем в этом же окружении вызвать другую.
- На сколько долгими будут вызовы python функций из C++ кода?
- Достаточно ли будет
boost::python
для реализации этого? Или придется рабоать с Python C-API? Хотелось бы по максммум использовать boost::python, по минимум Python C-API.
Везде описывают как создать C++ модуль для python. Мне же нужно из C++ кода вызывать python функйии, которые будут работать с моими C++ объектами.
Хотелось бы, что бы сценарий выглядел как то так: http://pastebin.com/2ZsiPeFT
В C++ все описано примерно так: http://pastebin.com/HHqbRRVx
Заранее благодарен за ответы!
ps: Если кому удобнее, то приведу код и тут.
# Пример сценария (как хотелось бы)
from mymodule import *
from subprocess import Popen, PIPE
# Вспомогательная функция. Запускает комманду в ОС.
def pExec(cmd):
try:
p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, shell=False)
return p
except:
print "Execute error"
return NULL
# Глобальная переменная. На самом деле нужна static переменная в функии pyEventOne().
# Она будет использоваться для хранения предидущего значения между вызовами pyEventOne()
pVal = getChannelValue("ChName")
# Наше событие.
# Возвращяет true, если оно произошло.
# И если true, то при вызове eventBase->update() будет запущен handler - функция python
# Для простоты описано событие аналогичное eventChanged
def pyEventOne():
newVal = getChannelValue("ChName")
if (newVal == pVal):
return False
else:
pVal = newVal
return True
# Обработчик события.
# Вызывает экспотрированные из C++ методы, и может делать что-то еще.
def handlerOne():
ev1.LockHanler()
cppSendCmd("cmd...")
ev2.UnlockHandler()
# еще один обработчик
def handlerTwo():
cppSendCmd("cmd...")
pExec("some system command")
# Функуия, вызываемая при инициализации сценария.
# Создает события, биндует обработчики.
def pyInit():
# Создает событие (конструирует объект eventChanged)
# и привязывает его к каналу данных с именем "SomeChannelName".
# Возвращает объект ev1
ev1 = CreateChEvent("SomeChannelName")
# Конструирует объект eventPython
ev2 = CreatePyEvent(pyEventOne)
# Передаем в наши объекты указатели на функции обработчики
ev1.SetHandler(handlerOne)
ev2.SetHandler(handlerTwo)
# Пока отключаем обработку события ev2,
# что бы на него не реагировать раньше времени
ev2.LockHandler()
//// С++ код (как то так реализованы event-ы):
class eventBase {
public:
using eventHandler_t = std::function<void()>; /* для примера указал такой тип */
eventBase();
virtual ~eventBase();
void setHandler(eventHandler_t h);
void lockHandler();
void unlockHandler();
bool handlerLocked() const;
bool update() { /* do something */ return this->run(); };
int id() const;
protected:
void runHandler(); /* call python function in python space */
private:
eventHandler_t handler; /* pointer to python function */
bool handlerLock = false;
virtual bool run() = 0; /* event function */
int _id = 0;
};
class eventChanged : public eventBase {
public:
eventChanged(qc::Channel* ch);
virtual ~eventChanged();
private:
qc::Channel* _channel;
virtual bool run() override; /* true if value of channel has changed */
};
class eventPython : public eventBase {
public:
using eventPythonFunc_t = std::function<bool()>; /* тоже для примера */
eventPython();
eventPython(eventPythonFunc_t);
virtual ~eventPython();
void setFunc(eventPythonFunc_t f) { evFunc = f; }
private:
eventPythonFunc_t evFunc;
virtual bool run() override; /* run python event function, return true if event ocurred */
};
/* events collection. */
class eventsFld {
public:
friend eventBase;
static void update() { for (auto ev : events) ev->update(); }
static auto size() { return events.size(); }
private:
static std::list<eventBase*> events;
static int lastid;
static int add(eventBase*); /* use in eventBase::eventBase() */
static bool remove(eventBase*); /* use in eventBase::~eventBase() */
};
class scenario {
public:
scenario(const std::string& );
void init() {
try {
/* run pyInit(): */
/* make new events and bind handlers */
} catch (...) {
}
}
private:
std::string pyScript;
};
/* далее создаем сценарий */
/* нинциализируем его */
/* а в рабочем цикле вызываем eventsFld::update() - обновляем все события */