From 9b0ce556e79fac4f4281e7c20ceff4cee87f532a Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" <tony@highfidelity.io> Date: Wed, 23 Nov 2016 10:10:00 -0800 Subject: [PATCH 1/2] Added preferences for the Perception Neuron plugin. By default this plugin is disabled. The server address and port number are also configurable. --- .../hifi/dialogs/GeneralPreferencesDialog.qml | 2 +- plugins/hifiNeuron/src/NeuronPlugin.cpp | 119 ++++++++++++++---- plugins/hifiNeuron/src/NeuronPlugin.h | 4 +- 3 files changed, 99 insertions(+), 26 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml b/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml index 9e46d86ecd..fec836626f 100644 --- a/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml +++ b/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml @@ -17,7 +17,7 @@ PreferencesDialog { id: root objectName: "GeneralPreferencesDialog" title: "General Settings" - showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers"] + showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron"] property var settings: Settings { category: root.objectName property alias x: root.x diff --git a/plugins/hifiNeuron/src/NeuronPlugin.cpp b/plugins/hifiNeuron/src/NeuronPlugin.cpp index aec8bf072a..93e7da028f 100644 --- a/plugins/hifiNeuron/src/NeuronPlugin.cpp +++ b/plugins/hifiNeuron/src/NeuronPlugin.cpp @@ -18,6 +18,8 @@ #include <cassert> #include <NumericalConstants.h> #include <StreamUtils.h> +#include <Preferences.h> +#include <SettingHandle.h> Q_DECLARE_LOGGING_CATEGORY(inputplugins) Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") @@ -30,6 +32,10 @@ Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") const char* NeuronPlugin::NAME = "Neuron"; const char* NeuronPlugin::NEURON_ID_STRING = "Perception Neuron"; +const bool DEFAULT_ENABLED = false; +const QString DEFAULT_SERVER_ADDRESS = "localhost"; +const int DEFAULT_SERVER_PORT = 7001; + // indices of joints of the Neuron standard skeleton. // This is 'almost' the same as the High Fidelity standard skeleton. // It is missing a thumb joint. @@ -356,6 +362,39 @@ static void SocketStatusChangedCallback(void* context, SOCKET_REF sender, Socket // NeuronPlugin // +void NeuronPlugin::init() { + loadSettings(); + + auto preferences = DependencyManager::get<Preferences>(); + static const QString NEURON_PLUGIN { "Perception Neuron" }; + { + auto getter = [this]()->QString { return _serverAddress; }; + auto setter = [this](const QString& value) { _serverAddress = value; saveSettings(); }; + auto preference = new EditPreference(NEURON_PLUGIN, "Server Address", getter, setter); + preference->setPlaceholderText(""); + preferences->addPreference(preference); + } + { + static const int MIN_PORT_NUMBER { 0 }; + static const int MAX_PORT_NUMBER { 65535 }; + + auto getter = [this]()->float { return (float)_serverPort; }; + auto setter = [this](float value) { _serverPort = (int)value; saveSettings(); }; + auto preference = new SpinnerPreference(NEURON_PLUGIN, "Server Port", getter, setter); + + preference->setMin(MIN_PORT_NUMBER); + preference->setMax(MAX_PORT_NUMBER); + preference->setStep(1); + preferences->addPreference(preference); + } + { + auto getter = [this]()->bool { return _enabled; }; + auto setter = [this](bool value) { _enabled = value; saveSettings(); }; + auto preference = new CheckPreference(NEURON_PLUGIN, "Enabled", getter, setter); + preferences->addPreference(preference); + } +} + bool NeuronPlugin::isSupported() const { // Because it's a client/server network architecture, we can't tell // if the neuron is actually connected until we connect to the server. @@ -365,32 +404,37 @@ bool NeuronPlugin::isSupported() const { bool NeuronPlugin::activate() { InputPlugin::activate(); - // register with userInputMapper - auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); - userInputMapper->registerDevice(_inputDevice); + loadSettings(); - // register c-style callbacks - BRRegisterFrameDataCallback((void*)this, FrameDataReceivedCallback); - BRRegisterCommandDataCallback((void*)this, CommandDataReceivedCallback); - BRRegisterSocketStatusCallback((void*)this, SocketStatusChangedCallback); + if (_enabled) { - // TODO: Pull these from prefs dialog? - // localhost is fine for now. - _serverAddress = "localhost"; - _serverPort = 7001; // default port for TCP Axis Neuron server. + // register with userInputMapper + auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); + userInputMapper->registerDevice(_inputDevice); - _socketRef = BRConnectTo((char*)_serverAddress.c_str(), _serverPort); - if (!_socketRef) { - // error - qCCritical(inputplugins) << "NeuronPlugin: error connecting to " << _serverAddress.c_str() << ":" << _serverPort << ", error = " << BRGetLastErrorMessage(); - return false; + // register c-style callbacks + BRRegisterFrameDataCallback((void*)this, FrameDataReceivedCallback); + BRRegisterCommandDataCallback((void*)this, CommandDataReceivedCallback); + BRRegisterSocketStatusCallback((void*)this, SocketStatusChangedCallback); + + // convert _serverAddress into a c-string. + QByteArray serverAddressByteArray = _serverAddress.toUtf8(); + char* cstr = serverAddressByteArray.data(); + + _socketRef = BRConnectTo(cstr, _serverPort); + if (!_socketRef) { + qCWarning(inputplugins) << "NeuronPlugin: error connecting to " << _serverAddress << ":" << _serverPort << ", error = " << BRGetLastErrorMessage(); + return false; + } else { + qCDebug(inputplugins) << "NeuronPlugin: success connecting to " << _serverAddress << ":" << _serverPort; + + emit deviceConnected(getName()); + + BRRegisterAutoSyncParmeter(_socketRef, Cmd_CombinationMode); + return true; + } } else { - qCDebug(inputplugins) << "NeuronPlugin: success connecting to " << _serverAddress.c_str() << ":" << _serverPort; - - emit deviceConnected(getName()); - - BRRegisterAutoSyncParmeter(_socketRef, Cmd_CombinationMode); - return true; + return false; } } @@ -407,6 +451,7 @@ void NeuronPlugin::deactivate() { } InputPlugin::deactivate(); + saveSettings(); } void NeuronPlugin::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { @@ -426,11 +471,36 @@ void NeuronPlugin::pluginUpdate(float deltaTime, const controller::InputCalibrat } void NeuronPlugin::saveSettings() const { - InputPlugin::saveSettings(); + Settings settings; + QString idString = getID(); + settings.beginGroup(idString); + { + settings.setValue(QString("enabled"), _enabled); + settings.setValue(QString("serverAddress"), _serverAddress); + settings.setValue(QString("serverPort"), _serverPort); + } + settings.endGroup(); } void NeuronPlugin::loadSettings() { - InputPlugin::loadSettings(); + Settings settings; + QString idString = getID(); + settings.beginGroup(idString); + { + // enabled + _enabled = settings.value("enabled", QVariant(DEFAULT_ENABLED)).toBool(); + + // serverAddress + _serverAddress = settings.value("serverAddress", QVariant(DEFAULT_SERVER_ADDRESS)).toString(); + + // serverPort + bool canConvertPortToInt = false; + int port = settings.value("serverPort", QVariant(DEFAULT_SERVER_PORT)).toInt(&canConvertPortToInt); + if (canConvertPortToInt) { + _serverPort = port; + } + } + settings.endGroup(); } // @@ -478,3 +548,4 @@ void NeuronPlugin::InputDevice::update(float deltaTime, const controller::InputC _poseStateMap[controller::RIGHT_HAND_THUMB1] = controller::Pose(rightHandThumb1DefaultAbsTranslation, glm::quat(), glm::vec3(), glm::vec3()); _poseStateMap[controller::LEFT_HAND_THUMB1] = controller::Pose(leftHandThumb1DefaultAbsTranslation, glm::quat(), glm::vec3(), glm::vec3()); } + diff --git a/plugins/hifiNeuron/src/NeuronPlugin.h b/plugins/hifiNeuron/src/NeuronPlugin.h index 36dcf93c23..43b27d14dd 100644 --- a/plugins/hifiNeuron/src/NeuronPlugin.h +++ b/plugins/hifiNeuron/src/NeuronPlugin.h @@ -28,6 +28,7 @@ public: bool isHandController() const override { return false; } // Plugin functions + virtual void init() override; virtual bool isSupported() const override; virtual const QString getName() const override { return NAME; } const QString getID() const override { return NEURON_ID_STRING; } @@ -68,7 +69,8 @@ protected: static const char* NAME; static const char* NEURON_ID_STRING; - std::string _serverAddress; + bool _enabled; + QString _serverAddress; int _serverPort; void* _socketRef; From cbf514a738e0116d39b7d83e703af9dc7a5d9420 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" <tony@highfidelity.io> Date: Wed, 23 Nov 2016 10:11:28 -0800 Subject: [PATCH 2/2] PreferenceDialogs now preserve the sort order of their showCategories. Specifically, this was to fix the issue with the Neuron plugin preferences being the top section in the General Preferences dialog, now it is the last. --- .../qml/dialogs/PreferencesDialog.qml | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/interface/resources/qml/dialogs/PreferencesDialog.qml b/interface/resources/qml/dialogs/PreferencesDialog.qml index ac9aad0e4a..e16f3aa12d 100644 --- a/interface/resources/qml/dialogs/PreferencesDialog.qml +++ b/interface/resources/qml/dialogs/PreferencesDialog.qml @@ -55,22 +55,20 @@ ScrollingWindow { Component.onCompleted: { var categories = Preferences.categories; - var categoryMap; var i; - if (showCategories && showCategories.length) { - categoryMap = {}; - for (i = 0; i < showCategories.length; ++i) { - categoryMap[showCategories[i]] = true; - } + + // build a map of valid categories. + var categoryMap = {}; + for (i = 0; i < categories.length; i++) { + categoryMap[categories[i]] = true; } - for (i = 0; i < categories.length; ++i) { - var category = categories[i]; - if (categoryMap && !categoryMap[category]) { - continue; + // create a section for each valid category in showCategories + // NOTE: the sort order of items in the showCategories array is the same order in the dialog. + for (i = 0; i < showCategories.length; i++) { + if (categoryMap[showCategories[i]]) { + sections.push(sectionBuilder.createObject(prefControls, {name: showCategories[i]})); } - - sections.push(sectionBuilder.createObject(prefControls, { name: category })); } if (sections.length) {