diff --git a/cmake/ScoreAvndHelper.cmake b/cmake/ScoreAvndHelper.cmake index 5c419e364a..abc773eddd 100644 --- a/cmake/ScoreAvndHelper.cmake +++ b/cmake/ScoreAvndHelper.cmake @@ -76,7 +76,7 @@ function(avnd_score_plugin_finalize) endfunction() function(avnd_score_plugin_add) - cmake_parse_arguments(AVND "" "TARGET;MAIN_CLASS;NAMESPACE;BASE_TARGET" "SOURCES" ${ARGN}) + cmake_parse_arguments(AVND "DEVICE" "TARGET;MAIN_CLASS;NAMESPACE;BASE_TARGET" "SOURCES" ${ARGN}) if(AVND_NAMESPACE) set(AVND_QUALIFIED "${AVND_NAMESPACE}::${AVND_MAIN_CLASS}") @@ -96,8 +96,13 @@ function(avnd_score_plugin_add) # ) # endif() + if(AVND_DEVICE) + set(source_proto "${SCORE_AVND_SOURCE_DIR}/device-prototype.cpp.in") + else() + set(source_proto "${SCORE_AVND_SOURCE_DIR}/prototype.cpp.in") + endif() configure_file( - "${SCORE_AVND_SOURCE_DIR}/prototype.cpp.in" + "${source_proto}" "${CMAKE_BINARY_DIR}/${AVND_TARGET}_avnd.cpp" @ONLY NEWLINE_STYLE LF diff --git a/src/plugins/score-plugin-avnd/AvndDevices/AvndDevice.hpp b/src/plugins/score-plugin-avnd/AvndDevices/AvndDevice.hpp new file mode 100644 index 0000000000..04ecddcedd --- /dev/null +++ b/src/plugins/score-plugin-avnd/AvndDevices/AvndDevice.hpp @@ -0,0 +1,262 @@ +#pragma once +#include + +#include + +#include + +#include +#include + +#include + +namespace oscr +{ +template +class Protocol final : public ossia::net::protocol_base +{ +public: + explicit Protocol(const ossia::net::network_context_ptr& ctx) + : ossia::net::protocol_base{flags{}} + { + } + + template + ossia::net::parameter_base* create_node(ossia::net::node_base& root, Field& f) + { + ossia::net::node_base* node{}; + if constexpr(avnd::has_path) + { + static constexpr auto name = avnd::get_path(); + node = &ossia::net::create_node(root, name); + } + else + { + static constexpr auto name = avnd::get_name(); + if constexpr(Input) + { + node = &ossia::net::create_node(root, fmt::format("/in/{}", name)); + } + else + { + node = &ossia::net::create_node(root, fmt::format("/out/{}", name)); + } + } + + using val_t = std::decay_t; + return node->create_parameter(oscr::type_for_arg()); + } + + void set_field_value(auto& f, const ossia::value& v) + { + oscr::from_ossia_value(f, v, f.value); + if_possible(f.update(impl)); + process_on_input(); + } + + void set_device(ossia::net::device_base& dev) override + { + // Create the parameters on the device + auto& root = dev.get_root_node(); + inputs.resize(avnd::input_introspection::size); + avnd::input_introspection::for_all_n( + avnd::get_inputs(impl), [&](auto& f, avnd::field_index) { + inputs[I] = create_node(root, f); + inputs[I]->add_callback( + [&f, this](const ossia::value& v) { set_field_value(f, v); }); + }); + + outputs.resize(avnd::output_introspection::size); + avnd::output_introspection::for_all_n( + avnd::get_outputs(impl), [&](auto& f, avnd::field_index) { + outputs[I] = create_node(root, f); + }); + } + + bool push(const ossia::net::parameter_base& param, const ossia::value& v) override + { + // Push on input : process + for(int i = 0; i < inputs.size(); i++) + { + auto* p = inputs[i]; + if(p == ¶m) + { + avnd::input_introspection::for_nth( + avnd::get_inputs(impl), i, + [this, &v](auto& f) { this->set_field_value(f, v); }); + break; + } + } + return false; + } + + void process_on_input() + { + impl(); + + avnd::output_introspection::for_all_n( + avnd::get_outputs(impl), [&](auto& f, avnd::field_index) { + SCORE_ASSERT(outputs[I]); + outputs[I]->set_value(oscr::to_ossia_value(f, f.value)); + }); + } + + bool pull(ossia::net::parameter_base&) override { return false; } + bool push_raw(const ossia::net::full_parameter_data&) override { return false; } + bool observe(ossia::net::parameter_base&, bool) override { return false; } + bool update(ossia::net::node_base& node_base) override { return false; } + + Node impl; + std::vector inputs; + std::vector outputs; +}; + +template +class DeviceImplementation final : public Device::OwningDeviceInterface +{ +public: + DeviceImplementation( + const Device::DeviceSettings& settings, + const Explorer::DeviceDocumentPlugin& plugin, const score::DocumentContext& ctx) + : OwningDeviceInterface{settings} + , m_ctx{plugin} + { + m_capas.canRefreshTree = true; + m_capas.canAddNode = false; + m_capas.canRemoveNode = false; + m_capas.canRenameNode = false; + m_capas.canSetProperties = false; + m_capas.canSerialize = false; + } + + ~DeviceImplementation() { } + + bool reconnect() override + { + disconnect(); + + auto protocol = std::make_unique>(this->m_ctx.networkContext()); + auto dev = std::make_unique( + std::move(protocol), settings().name.toStdString()); + + m_dev = std::move(dev); + deviceChanged(nullptr, m_dev.get()); + + return connected(); + } + void disconnect() override { OwningDeviceInterface::disconnect(); } + +private: + const Explorer::DeviceDocumentPlugin& m_ctx; +}; + +template +class ProtocolSettingsWidget final : public Device::ProtocolSettingsWidget +{ +public: + explicit ProtocolSettingsWidget(QWidget* parent = nullptr) + : Device::ProtocolSettingsWidget(parent) + { + m_deviceNameEdit = new State::AddressFragmentLineEdit{this}; + m_deviceNameEdit->setText("SpatGRIS"); + auto layout = new QFormLayout; + layout->addRow(tr("Name"), m_deviceNameEdit); + setLayout(layout); + } + + virtual ~ProtocolSettingsWidget() { } + + Device::DeviceSettings getSettings() const override + { + // TODO should be = m_settings to follow the other patterns. + Device::DeviceSettings s; + s.name = m_deviceNameEdit->text(); + s.protocol = oscr::uuid_from_string(); + return s; + } + + void setSettings(const Device::DeviceSettings& settings) override + { + m_deviceNameEdit->setText(settings.name); + } + +private: + QLineEdit* m_deviceNameEdit{}; +}; + +template +class ProtocolFactory final : public Protocols::DefaultProtocolFactory +{ + UuidKey concreteKey() const noexcept override + { + return oscr::uuid_from_string(); + } + + QString prettyName() const noexcept override + { + return QString::fromUtf8(avnd::get_name().data()); + } + QString category() const noexcept override { return StandardCategories::software; } + Device::DeviceEnumerators + getEnumerators(const score::DocumentContext& ctx) const override + { + return {}; + } + + Device::DeviceInterface* makeDevice( + const Device::DeviceSettings& settings, + const Explorer::DeviceDocumentPlugin& plugin, + const score::DocumentContext& ctx) override + { + return new DeviceImplementation{settings, plugin, ctx}; + } + + const Device::DeviceSettings& defaultSettings() const noexcept override + { + static const Device::DeviceSettings& settings = [&] { + Device::DeviceSettings s; + s.protocol = concreteKey(); + s.name = "SpatGRIS"; + s.deviceSpecificSettings = QVariant{}; + return s; + }(); + + return settings; + } + + Device::ProtocolSettingsWidget* makeSettingsWidget() override + { + return new ProtocolSettingsWidget; + } + + QVariant makeProtocolSpecificSettings(const VisitorVariant& visitor) const override + { + return QVariant{}; + } + + void serializeProtocolSpecificSettings( + const QVariant& data, const VisitorVariant& visitor) const override + { + } + + bool checkCompatibility( + const Device::DeviceSettings& a, + const Device::DeviceSettings& b) const noexcept override + { + return true; + } +}; + +template +static void instantiate_device( + std::vector& v, const score::ApplicationContext& ctx, + const score::InterfaceKey& key) +{ + if(key == Device::ProtocolFactory::static_interfaceKey()) + { + (v.emplace_back( + static_cast(new oscr::ProtocolFactory())), + ...); + } +} +} diff --git a/src/plugins/score-plugin-avnd/CMakeLists.txt b/src/plugins/score-plugin-avnd/CMakeLists.txt index a10f12261f..7aeedee07a 100644 --- a/src/plugins/score-plugin-avnd/CMakeLists.txt +++ b/src/plugins/score-plugin-avnd/CMakeLists.txt @@ -117,6 +117,7 @@ add_library( Avnd/Factories.hpp + AvndDevices/AvndDevice.hpp Crousti/Attributes.hpp Crousti/Concepts.hpp @@ -319,7 +320,7 @@ set(AVND_ADDITIONAL_CLASSES) set(AVND_CUSTOM_FACTORIES) function(avnd_make_score) - cmake_parse_arguments(AVND "" "TARGET;MAIN_CLASS;NAMESPACE" "SOURCES" ${ARGN}) + cmake_parse_arguments(AVND "DEVICE" "TARGET;MAIN_CLASS;NAMESPACE" "SOURCES" ${ARGN}) if(AVND_NAMESPACE) set(AVND_QUALIFIED "${AVND_NAMESPACE}::${AVND_MAIN_CLASS}") @@ -339,8 +340,13 @@ function(avnd_make_score) ) endif() + if(AVND_DEVICE) + set(source_proto "${CMAKE_CURRENT_SOURCE_DIR}/device-prototype.cpp.in") + else() + set(source_proto "${CMAKE_CURRENT_SOURCE_DIR}/prototype.cpp.in") + endif() configure_file( - "${CMAKE_CURRENT_SOURCE_DIR}/prototype.cpp.in" + "${source_proto}" "${CMAKE_BINARY_DIR}/${AVND_TARGET}_avnd.cpp" @ONLY NEWLINE_STYLE LF diff --git a/src/plugins/score-plugin-avnd/device-prototype.cpp.in b/src/plugins/score-plugin-avnd/device-prototype.cpp.in new file mode 100644 index 0000000000..ed854d7c99 --- /dev/null +++ b/src/plugins/score-plugin-avnd/device-prototype.cpp.in @@ -0,0 +1,38 @@ +// clang-format off +#include + +#include "@AVND_MAIN_FILE@" + +#cmakedefine AVND_REFLECTION_HELPERS +#if defined(AVND_REFLECTION_HELPERS) +#include +#include +#include +#include + +#include "@AVND_REFLECTION_HELPERS_PRE@" + +#include +#include +#include +#include + +#include "@AVND_REFLECTION_HELPERS@" +#endif + +#include +#include + +namespace oscr +{ +template <> +void custom_factories<@AVND_QUALIFIED@>( + std::vector& fx, + const score::ApplicationContext& ctx, const score::InterfaceKey& key) +{ + using namespace oscr; + oscr::instantiate_device<@AVND_QUALIFIED@>(fx, ctx, key); +} +} + +// clang-format on