Skip to content

Commit

Permalink
pico: oscquery generation
Browse files Browse the repository at this point in the history
  • Loading branch information
jcelerier committed Dec 2, 2024
1 parent 3cb9148 commit 808c663
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 59 deletions.
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,16 @@ add_library(score_addon_pico
Pico/SourcePrinter.cpp
Pico/LibossiaGenerator.hpp
Pico/LibossiaGenerator.cpp
Pico/OSCQueryGenerator.hpp
Pico/OSCQueryGenerator.cpp
Pico/Pruner.hpp
Pico/Pruner.cpp

score_addon_pico.hpp
score_addon_pico.cpp
)

target_compile_definitions(score_addon_pico PRIVATE "PICO_SOURCE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\"")
target_link_libraries(score_addon_pico PRIVATE score_plugin_protocols)

avnd_score_plugin_init(BASE_TARGET score_addon_pico)
Expand Down
2 changes: 2 additions & 0 deletions Pico/DeviceIO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ QString DeviceIO::printInitialization() const

return str;
}
// TODO MCPWM
// TODO Servo
case PWM:
{
QString str;
Expand Down
178 changes: 122 additions & 56 deletions Pico/ExportDialog.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
#include "ExportDialog.hpp"

#include <Device/Protocol/DeviceInterface.hpp>

#include <Explorer/DocumentPlugin/DeviceDocumentPlugin.hpp>

#include <Scenario/Document/BaseScenario/BaseScenario.hpp>
#include <Scenario/Document/ScenarioDocument/ScenarioDocumentModel.hpp>

#include <ossia/network/base/device.hpp>
#include <ossia/network/base/node.hpp>

#include <QApplication>
#include <QComboBox>
#include <QDialog>
Expand All @@ -14,9 +19,11 @@
#include <QFormLayout>
#include <QLineEdit>
#include <QPlainTextEdit>
#include <QProcess>
#include <QPushButton>

#include <Pico/ApplicationPlugin.hpp>
#include <Pico/OSCQueryGenerator.hpp>
#include <Pico/Pruner.hpp>
#include <Pico/SourcePrinter.hpp>

Expand All @@ -25,28 +32,46 @@
#include <Protocols/SimpleIO/SimpleIODevice.hpp>
namespace Pico
{
static void copy_folder_recursively(const QString& src, const QString& dst)
{
QDirIterator it{
src, QDir::Filter::NoDotAndDotDot | QDir::Filter::Files | QDir::Filter::Dirs,
QDirIterator::Subdirectories};
QDir dir{src};
const int absSourcePathLength = dir.absoluteFilePath(src).length();

while(it.hasNext())
{
const auto fi = it.nextFileInfo();
const QString abs_path = dst + fi.absoluteFilePath().mid(absSourcePathLength);

if(fi.isDir())
QDir{}.mkpath(abs_path);
else if(fi.isFile() && !QFileInfo::exists(abs_path))
QFile::copy(fi.absoluteFilePath(), abs_path);
}
}

ExportDialog::ExportDialog(AppPlug& plug, QWidget* parent)
: QDialog{parent}
, plug{plug}
{
auto lay = new QFormLayout{this};
/// Export mode
m_mode = new QComboBox{this};
m_mode->addItems({tr("Remote-control"), tr("Full score")});
lay->addRow(tr("Mode"), m_mode);

m_template = new QLineEdit{this};
m_template->setText(
"/home/jcelerier/projets/oss/puara-module-template/ossia-device-test");
m_template->setText(PICO_SOURCE_DIR "/templates/totem-tinypico-firmware");
lay->addRow(tr("Path to template"), m_template);

m_destination = new QLineEdit{this};
m_destination->setText("/tmp/test");
lay->addRow(tr("Destination"), m_destination);

m_textArea = new QPlainTextEdit{this};
m_textArea->setTextInteractionFlags(Qt::TextInteractionFlag::TextBrowserInteraction);
// QTBUG-131762
// m_textArea->setTextInteractionFlags(Qt::TextInteractionFlag::TextBrowserInteraction);
lay->addWidget(m_textArea);

m_buttons = new QDialogButtonBox{this};
Expand All @@ -60,14 +85,6 @@ ExportDialog::ExportDialog(AppPlug& plug, QWidget* parent)
connect(build_btn, &QPushButton::clicked, this, &ExportDialog::on_build);

lay->addWidget(m_buttons);

// Template
// e.g. a puara module template ?
// or a wasm one ?
// or a desktop one

// Target ?
// note : do not overwrite copied files
}

bool ExportDialog::copy_template_folder()
Expand All @@ -83,68 +100,76 @@ bool ExportDialog::copy_template_folder()
return false;
}

QDir src_dir{src};
src_dir.setFilter(QDir::Filter::Files | QDir::Filter::NoDotAndDotDot);

QDirIterator d{src_dir, QDirIterator::IteratorFlag::Subdirectories};

while(d.hasNext())
{
auto src_file = d.next();
auto src_relpath = src_dir.relativeFilePath(src_file);

QString dst_file = dst + QDir::separator() + src_relpath;
if(!QFileInfo::exists(dst_file))
{
QFile::copy(src_file, dst_file);
}
}
copy_folder_recursively(src, dst);
return true;
}

void ExportDialog::export_device(const score::DocumentContext& ctx)
bool ExportDialog::export_device(const score::DocumentContext& ctx)
{
// For template ossia-device
auto& devices = ctx.plugin<Explorer::DeviceDocumentPlugin>();
for(auto dev : devices.list().devices())
{
if(auto obj = qobject_cast<Protocols::SimpleIODevice*>(dev))
{
Protocols::SimpleIOCodeWriter_ESP32 wr{*obj};
std::string ret;
ret += R"_(
// Write the device OSC UDP I/O
{
Protocols::SimpleIOCodeWriter_ESP32 wr{*obj};
std::string ret;
ret += R"_(
#include "ossia_embedded_api.hpp"
#include "constants.hpp"
#include "utility.hpp"
#include <soc/adc_channel.h>
)_";
ret += wr.init();
ret += wr.readOSC();
ret += wr.readPins();
ret += wr.writeOSC();
ret += wr.writePins();

QFile dst{
m_destination->text() + QDir::separator() + "ossia.device.generated.cpp"};
dst.open(QIODevice::WriteOnly);
dst.write(ret.data(), ret.size());
dst.flush();

break;
ret += wr.init();
ret += wr.readOSC();
ret += wr.readPins();
ret += wr.writeOSC();
ret += wr.writePins();

{
QFile dst{
m_destination->text() + QDir::separator() + "ossia-device.generated.cpp"};
dst.open(QIODevice::WriteOnly);
dst.write(ret.data(), ret.size());
dst.flush();
}
}

// Write the device OSCQuery API
{
auto root_model_node = obj->getNode(State::Address{});
root_model_node.set(obj->settings());
auto ret = Pico::oscquery_generate_namespace(root_model_node);
{
QFile dst{
m_destination->text() + QDir::separator()
+ "ossia-oscquery.generated.hpp"};
dst.open(QIODevice::WriteOnly);
dst.write(ret.toUtf8());
dst.flush();
}
}

return true;
}
}
return false;
}

void ExportDialog::export_scenario(const score::DocumentContext& ctx)
bool ExportDialog::export_scenario(const score::DocumentContext& ctx)
{
// For template ossia-full-*
Scenario::ScenarioDocumentModel& base
= score::IDocument::get<Scenario::ScenarioDocumentModel>(ctx.document);

const auto& baseInterval = base.baseScenario().interval();
if(baseInterval.processes.size() == 0)
return;
return false;

auto root = m_destination->text() + QDir::separator();
QString root = m_destination->text() + QDir::separator();

ProcessScenario ps{ctx};
{
Expand Down Expand Up @@ -173,35 +198,76 @@ void ExportDialog::export_scenario(const score::DocumentContext& ctx)
f.write(content.toUtf8());
}
}
return true;
}

void ExportDialog::on_export()
bool ExportDialog::on_export()
{
auto doc = plug.currentDocument();
if(!doc)
return;
return false;

if(!copy_template_folder())
{
qDebug() << "Failed to copy template folder from " << m_template->text() << " to "
<< m_destination->text();
return;
return false;
}

switch(static_cast<ExportMode>(m_mode->currentIndex()))
{
case RemoteControl:
export_device(doc->context());
break;
return export_device(doc->context());
case FullScore:
export_scenario(doc->context());
break;
return export_scenario(doc->context());
}
return false;
}

void ExportDialog::append_stdout(QString s)
{
this->m_textArea->appendPlainText(s);
}

void ExportDialog::append_stderr(QString s)
{
this->m_textArea->appendPlainText(s);
}

void ExportDialog::on_build()
{
on_export();
if(!on_export())
return;

QString root = m_destination->text() + QDir::separator();
QProcess* p = new QProcess;
p->setWorkingDirectory(root);
connect(p, &QProcess::readyReadStandardOutput, this, [this, p]() {
this->append_stdout(p->readAllStandardOutput());
});
connect(p, &QProcess::readyReadStandardError, this, [this, p]() {
this->append_stderr(p->readAllStandardError());
});
connect(p, &QProcess::finished, p, &QProcess::deleteLater);

if(QFile::exists(root + "platformio.ini"))
{
p->start(
qEnvironmentVariable("HOME") + "/.platformio/penv/bin/pio",
{"run", "-t", "upload"});
}
else if(QFile::exists(root + "build.sh"))
{
p->start(root + "build.sh");
}
else if(QFile::exists(root + "CMakeLists.txt"))
{
p->start("cninja");
}
else
{
delete p;
}
}

}
8 changes: 5 additions & 3 deletions Pico/ExportDialog.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@ class ExportDialog : public QDialog
AppPlug& plug;
explicit ExportDialog(AppPlug& plug, QWidget* parent);

void on_export();
bool on_export();

void on_build();

private:
bool copy_template_folder();
void export_device(const score::DocumentContext& ctx);
void export_scenario(const score::DocumentContext& ctx);
bool export_device(const score::DocumentContext& ctx);
bool export_scenario(const score::DocumentContext& ctx);
void append_stdout(QString);
void append_stderr(QString);

QComboBox* m_mode{};
QLineEdit* m_template{};
Expand Down
59 changes: 59 additions & 0 deletions Pico/OSCQueryGenerator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#include "OSCQueryGenerator.hpp"

#include <Device/Protocol/DeviceInterface.hpp>

#include <ossia/network/generic/generic_device.hpp>
#include <ossia/network/oscquery/detail/json_writer.hpp>

namespace Pico
{
static void
oscquery_generate_namespace_rec(ossia::net::generic_device& odev, const Device::Node& n)
{
auto settings
= Device::FullAddressSettings::make<Device::FullAddressSettings::as_parent>(
n.get<Device::AddressSettings>(), Device::address(*n.parent()));

ossia::net::node_base* node = Device::createNodeFromPath(settings.address.path, odev);
if(node && settings.value.v)
{
if(auto p = node->create_parameter(settings.value.get_type()))
{
auto& addr = *p;
SCORE_ASSERT(settings.ioType);
addr.set_access(*settings.ioType);
addr.set_bounding(settings.clipMode);
addr.set_repetition_filter(ossia::repetition_filter(settings.repetitionFilter));
addr.set_value_type(settings.value.get_type());
addr.set_value(settings.value);
addr.set_domain(settings.domain);
addr.set_unit(settings.unit);
addr.get_node().set_extended_attributes(settings.extendedAttributes);
}
}

for(const auto& child : n)
oscquery_generate_namespace_rec(odev, child);
}

QString oscquery_generate_namespace(const Device::Node& root)
{
// 1. Load the data model into an ossia device
ossia::net::generic_device odev;
for(const auto& child : root)
oscquery_generate_namespace_rec(odev, child);

// 2. Use libossia's oscquery generation
auto str = ossia::oscquery::json_writer::query_namespace(odev.get_root_node());

QString res = QString(R"!(#pragma once
static constexpr std::string_view oscquery_namespace()
{
using namespace std::literals;
return R"_(%1)_"sv;
}
)!")
.arg(QString::fromUtf8(str.GetString(), str.GetLength()));
return res;
}
}
Loading

0 comments on commit 808c663

Please sign in to comment.