#include "gtl_scr_engine.h" #include #include "core/gtl_logger.h" #include "core/gtl_device.h" #include "core/gtl_analog_data.h" #include "math/gtl_math_sum.h" #include "math/gtl_math_rms.h" #include "math/gtl_math_var.h" #include "math/gtl_math_freq.h" #include "math/gtl_math_ampl.h" #include "math/gtl_math_kurt.h" #include "math/gtl_math_delta_phase.h" #include "math/gtl_math_delta_phase_spec.h" #include "math/gtl_math_filter_iir.h" #include "math/gtl_math_intg.h" #include "math/gtl_math_diff.h" #include "math/gtl_math.h" #include "hw/gtl_hw_player_file_gtr.h" #include "hw/gtl_hw_player_file_wav.h" namespace gtl { namespace scr { QStringList engine::import_paths; engine::engine(QObject* parent) : QObject(parent) , _engine(nullptr) , _player(nullptr) , _file(nullptr) { _log = new log(this); connect(_log, &log::new_message, this, &engine::log_message); } engine::~engine() { set_file(); } void gtl::scr::engine::clear() { clear_options(); clear_objects(); remove_all_ad(); } void engine::get_state(QJsonObject &root) const { QJsonArray base_indices; for(auto obj: _objects) { if(is_gtl_math_analog_value(obj)) base_indices.append(QJsonObject{{static_cast(obj)->name(), static_cast(obj)->value()}}); } root.insert("base_indices", base_indices); QJsonArray specs; for(auto obj: _objects) { if(is_gtl_scr_spec(obj)) { QJsonObject spec_object; static_cast(obj)->get_state(spec_object); specs.append(spec_object); } } root.insert("specs", specs); } bool engine::set_file(QString path, std::vector channels) { // qDebug() << path; if(_file) { delete _player; _player = nullptr; delete _file; _file = nullptr; for(auto it : _analog_inputs) delete it; _analog_inputs.clear(); } clear(); if(path.isEmpty()) return true; QFileInfo info(path); if(info.suffix().toLower() == "gtr") _file = new gtl::hw::player_file_gtr(this, path); else if(info.suffix().toLower() == "wav") _file = new gtl::hw::player_file_wav(this, path); if(_file == nullptr) { gtl::logger::error("script engine", "error opening file: " + path); return false; } if(!_file->is_ok()) { gtl::logger::error("script engine", "error reading file: " + path); delete _file; _file = nullptr; return false; } if(channels.empty()) { for(int i = 0; i < _file->channels(); i++) { gtl::analog_data* ad = new analog_data(_file->rate(), _file->reference(i)); ad->set_name(_file->channel(i)); _analog_inputs.push_back(ad); } } else { for(auto index: channels) { gtl::analog_data* ad = new analog_data(_file->rate(), _file->reference(index)); ad->set_name(_file->channel(index)); _analog_inputs.push_back(ad); } } emit analog_inputs_changed(); _player = new player(_file, channels, this); connect(_player, &player::data_read, this, &engine::set_data_from_file/*, Qt::DirectConnection*/); return true; } QQmlEngine *engine::qml_engine() const { return _engine; } void engine::set_ad(std::vector::iterator begin, std::vector::iterator end) { remove_all_ad(); for(auto it = begin; it != end; it++) add_ad(*it); } void engine::add_local_import_path(QString path) { _local_import_paths.push_back(path); } void engine::clear_local_import_paths() { _local_import_paths.clear(); } analog_data *engine::analog_input(int idx) { return qobject_cast(_analog_inputs[idx]); } bool engine::evaluate(QString program) { clear_objects(); if(_engine) delete _engine; _engine = new QQmlEngine(this); init(); QJSValue result = _engine->evaluate(program); if(result.isError()) gtl::logger::error("script", result.toString() + " " + tr("at line") + ": " + result.property("lineNumber").toString() + " " + tr("in file") + ": " + result.property("fileName").toString()); bool res = !result.isError(); if(res && _player) { _player->stop(); _player->start(); } return res; } void engine::clear_options() { _options = QJsonObject(); } void engine::set_options(const QJsonObject &root) { _options = root; emit options_changed(); } void engine::get_results(QJsonObject &root) { root = _results; } QVariant engine::get_analog_inputs() const { return QVariant::fromValue(_analog_inputs); } bool engine::is_type(QObject *obj, QString type) const { if(obj == NULL) return false; const QMetaObject* meta_object = obj->metaObject(); while(meta_object) { if(QString(meta_object->className()) == type) return true; meta_object = meta_object->superClass(); } return false; } bool engine::is_gtl_analog_data(QObject *obj) { return is_type(obj, "gtl::analog_data"); } bool engine::is_gtl_math_analog_value(QObject *obj) const { return is_type(obj, "gtl::math::analog_value"); } bool engine::is_gtl_scr_spec(QObject *obj) const { return is_type(obj, "gtl::scr::spec"); } log* engine::get_log() const { return _log; } QJsonObject engine::get_options() const { return _options; } QJsonObject engine::get_results() const { //return QVariant::fromValue(_results); return _results; } void engine::clear_objects() { while(!_objects.empty()) { QObject *obj = _objects.first(); _objects.removeFirst(); delete obj; } } void engine::init() { _engine->globalObject().setProperty("gtl", _engine->newQObject(this)); _engine->globalObject().property("gtl").setProperty("filter_iir", _engine->newQMetaObject(>l::math::filter_iir::staticMetaObject)); _engine->globalObject().property("gtl").setProperty("spec", _engine->newQMetaObject(>l::scr::spec::staticMetaObject)); } void engine::add_ad(gtl::analog_data *ad) { gtl::data_model_node *device = ad->root(); auto it = std::find_if(_analog_inputs.begin(), _analog_inputs.end(), [=](QObject* ai){return static_cast(ai)->root() == device;}); if(it == _analog_inputs.end()) connect(static_cast(device), >l::device::recieved_data, this, &engine::device_recieved_data); _analog_inputs.push_back(ad); connect(ad, >l::data_model_node::deleting, this, &engine::deleting_ad); emit analog_inputs_changed(); } void engine::remove_ad(gtl::analog_data *ad) { _analog_inputs.removeOne(ad); gtl::data_model_node *device = ad->root(); auto it = std::find_if(_analog_inputs.begin(), _analog_inputs.end(), [=](QObject* ai){return static_cast(ai)->root() == device;}); if(it == _analog_inputs.end()) disconnect(static_cast(device), >l::device::recieved_data, this, &engine::device_recieved_data); emit analog_inputs_changed(); } void engine::remove_all_ad() { for(auto it : _analog_inputs) disconnect(static_cast(static_cast(it)->root()), >l::device::recieved_data, this, &engine::device_recieved_data); _analog_inputs.clear(); emit analog_inputs_changed(); } QJSValue engine::add_value_sum(QJSValue parent) { QObject* obj = parent.toQObject(); gtl::math::sum* sum_value = NULL; if(is_gtl_analog_data(obj)) { sum_value = new gtl::math::sum(static_cast(obj)); _objects.push_back(sum_value); } else gtl::logger::error("script", "error creating sum_value object: parent is not gtl::analog_data object"); emit base_index_created(sum_value); return _engine->newQObject(sum_value); } QJSValue engine::add_value_rms(QJSValue parent) { QObject* obj = parent.toQObject(); gtl::math::rms* rms_value = NULL; if(is_gtl_analog_data(obj)) { rms_value = new gtl::math::rms(static_cast(obj)); _objects.push_back(rms_value); } else gtl::logger::error("script", "error creating rms_value object: parent is not gtl::analog_data object"); emit base_index_created(rms_value); return _engine->newQObject(rms_value); } QJSValue engine::add_value_var(QJSValue parent) { QObject* obj = parent.toQObject(); gtl::math::var* var_value = NULL; if(is_gtl_analog_data(obj)) { var_value = new gtl::math::var(static_cast(obj)); _objects.push_back(var_value); } else gtl::logger::error("script", "error creating rms_value object: parent is not gtl::analog_data object"); emit base_index_created(var_value); return _engine->newQObject(var_value); } QJSValue engine::add_value_freq(QJSValue parent) { QObject* obj = parent.toQObject(); gtl::math::freq* freq_value = NULL; if(is_gtl_analog_data(obj)) { freq_value = new gtl::math::freq(static_cast(obj)); _objects.push_back(freq_value); } else gtl::logger::error("script", "error creating freq_value object: parent is not gtl::analog_data object"); emit base_index_created(freq_value); return _engine->newQObject(freq_value); } QJSValue engine::add_value_ampl(QJSValue parent) { QObject* obj = parent.toQObject(); gtl::math::ampl* ampl_value = NULL; if(is_gtl_analog_data(obj)) { ampl_value = new gtl::math::ampl(static_cast(obj)); _objects.push_back(ampl_value); } else gtl::logger::error("script", "error creating freq_value object: parent is not gtl::analog_data object"); emit base_index_created(ampl_value); return _engine->newQObject(ampl_value); } QJSValue engine::add_value_kurt(QJSValue parent) { QObject* obj = parent.toQObject(); gtl::math::kurt* kurt_value = NULL; if(is_gtl_analog_data(obj)) { kurt_value = new gtl::math::kurt(static_cast(obj)); _objects.push_back(kurt_value); } else gtl::logger::error("script", "error creating freq_value object: parent is not gtl::analog_data object"); emit base_index_created(kurt_value); return _engine->newQObject(kurt_value); } QJSValue engine::add_delta_phase(QJSValue parent, QJSValue parent1) { QObject* obj = parent.toQObject(); QObject* obj1 = parent1.toQObject(); gtl::math::delta_phase* delta_phase = NULL; if(is_gtl_analog_data(obj) && is_gtl_analog_data(obj1)) { delta_phase = new gtl::math::delta_phase(static_cast(obj), static_cast(obj1)); _objects.push_back(delta_phase); } else gtl::logger::error("script", "error creating delta_phase object: parent is not gtl::analog_data object"); return _engine->newQObject(delta_phase); } QJSValue engine::add_delta_phase_spec(QJSValue parent, QJSValue parent1) { QObject* obj = parent.toQObject(); QObject* obj1 = parent1.toQObject(); gtl::math::delta_phase_spec* delta_phase = NULL; if(is_gtl_analog_data(obj) && is_gtl_analog_data(obj1)) { delta_phase = new gtl::math::delta_phase_spec(static_cast(obj), static_cast(obj1)); _objects.push_back(delta_phase); } else gtl::logger::error("script", "error creating delta_phase object: parent is not gtl::analog_data object"); return _engine->newQObject(delta_phase); } QJSValue engine::add_filter_iir(QJSValue parent) { QObject* obj = parent.toQObject(); gtl::math::filter_iir* filter_iir = NULL; if(is_gtl_analog_data(obj)) { filter_iir = new gtl::math::filter_iir(static_cast(obj), true); _objects.push_back(static_cast(filter_iir)); connect(filter_iir, >l::data_model_node::deleting, this, &engine::deleting_object); } else gtl::logger::error("script", "error creating filter object: parent is not gtl::analog_data object"); return _engine->newQObject(filter_iir); } QJSValue engine::add_intg(QJSValue parent) { QObject* obj = parent.toQObject(); gtl::math::intg *intg = NULL; if(is_gtl_analog_data(obj)) { intg = new gtl::math::intg(static_cast(obj), true); _objects.push_back(static_cast(intg)); connect(intg, >l::data_model_node::deleting, this, &engine::deleting_object); } else gtl::logger::error("script", "error creating filter object: parent is not gtl::analog_data object"); return _engine->newQObject(intg); } QJSValue engine::add_diff(QJSValue parent) { QObject* obj = parent.toQObject(); gtl::math::diff *diff = NULL; if(is_gtl_analog_data(obj)) { diff = new gtl::math::diff(static_cast(obj), true); _objects.push_back(static_cast(diff)); connect(diff, >l::data_model_node::deleting, this, &engine::deleting_object); } else gtl::logger::error("script", "error creating filter object: parent is not gtl::analog_data object"); return _engine->newQObject(diff); } QJSValue engine::add_ausp(QJSValue parent) { QObject* obj = parent.toQObject(); gtl::scr::spec* ausp = NULL; if(is_gtl_analog_data(obj)) { ausp = new gtl::scr::spec(gtl::math::spec::ausp, static_cast(obj)); _objects.push_back(static_cast(ausp)); connect(ausp, >l::math::spec::deleting, this, &engine::deleting_object); connect(ausp, >l::scr::spec::create_engine_object, this, &engine::create_jsobject); emit spec_created(ausp); } else gtl::logger::error("script", "error creating ausp object: parent is not gtl::analog_data object"); return _engine->newQObject(ausp); } QJSValue engine::add_spen(QJSValue parent) { QObject* obj = parent.toQObject(); gtl::scr::spec* spen = NULL; if(is_gtl_analog_data(obj)) { spen = new gtl::scr::spec(gtl::math::spec::spen, static_cast(obj)); _objects.push_back(static_cast(spen)); connect(spen, >l::math::spec::deleting, this, &engine::deleting_object); connect(spen, >l::scr::spec::create_engine_object, this, &engine::create_jsobject); emit spec_created(spen); } else gtl::logger::error("script", "error creating spen object: parent is not gtl::analog_data object"); return _engine->newQObject(spen); } QJSValue engine::get_analog_input(QString name) { QObject *input = NULL; auto it = std::find_if(_analog_inputs.begin(), _analog_inputs.end(), [=](QObject *obj){return static_cast(obj)->name() == name;}); if(it != _analog_inputs.end()) input = *it; return _engine->newQObject(input); } QJSValue engine::import(QString file_name) { if(QFileInfo::exists(file_name)) return _engine->importModule(file_name); for(auto it : _local_import_paths) { QFileInfo file(it + "/" + file_name); if(file.exists()) { return _engine->importModule(file.absoluteFilePath()); } } for(auto it : import_paths) { QFileInfo file(it + "/" + file_name); if(file.exists()) { return _engine->importModule(file.absoluteFilePath()); } } gtl::logger::error("script", tr("can't find module file: ") + file_name); return QJSValue(); } qreal engine::get_kurt_value(const QJsonArray &data_array) const { std::vector array; for(auto it = data_array.cbegin(); it != data_array.cend(); it++) array.push_back(it->toDouble()); return mathFunctions::kurt(array.begin(), array.end()); } qreal engine::get_var_value(const QJsonArray &data_array) const { std::vector array; for(auto it = data_array.cbegin(); it != data_array.cend(); it++) array.push_back(it->toDouble()); return mathFunctions::var(array.begin(), array.end()); } void engine::device_recieved_data() { } void engine::deleting_ad() { remove_ad(static_cast(sender())); } void engine::deleting_object() { _objects.removeOne(sender()); } void engine::create_jsobject(QObject *obj, QJSValue &js_object) { js_object = _engine->newQObject(obj); } void engine::set_results(const QJsonObject &value) { _results = value; emit results_changed(); } void engine::set_data_from_file() { for(int i = 0; i < _analog_inputs.size(); i++) static_cast(_analog_inputs[i])->set_data(_player->data(), _player->samples(), _player->channels().empty() ? i : _player->channels().at(i), /*_analog_inputs.size()*/_file->channels()); device_recieved_data(); _player->confirm(); } log::log(QObject *parent) : QObject(parent) { } void log::info(QString tag, QString message) { gtl::logger::info(tag, message); emit new_message(tag, message); } void log::warning(QString tag, QString message) { gtl::logger::warning(tag, message); emit new_message(tag, message); } void log::error(QString tag, QString message) { gtl::logger::error(tag, message); emit new_message(tag, message); } void log::debug(QString tag, QString message) { gtl::logger::debug(tag, message); emit new_message(tag, message); } player::player(hw::player_file *file, const std::vector &channels, QObject *parent) : QThread(parent) , _file(file) , _samples(qRound(0.1*file->rate())) , _buffer(_samples * file->channels()) , _channels(channels) { } player::~player() { stop(); } void player::start() { _is_playing = true; QThread::start(); } void player::stop() { if(!isRunning()) return; _status = true; _is_playing = false; wait(); } qreal player::rate() const { return _file->rate(); } void player::confirm() { _status = true; } int player::samples() const { return _samples; } qreal *player::data() const { return (qreal*)&_buffer[0]; } const std::vector &player::channels() const { return _channels; } void player::run() { setPriority(QThread::LowPriority); int idx = 0; while(_is_playing) { _file->get_data(&_buffer[0], idx, _samples, true); if(_samples) { _status = false; emit data_read(); // qDebug() << "playing..."; } while((!_status) && _is_playing) msleep(1); } } } }