diff --git a/CHANGELOG.md b/CHANGELOG.md index b93f8a98..75577551 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 **JSON-API** - New subscription support for event updates, i.e. `Suspend, Resume, Idle, idleResume, Restart, Quit`. -- Support direct instance addressing via individual requests (#809) +- Support direct or multiple instance addressing via single requests (#809) - Support of `serverinfo` subcommands: `getInfo, subscribe, unsubscribe, getSubscriptions, getSubscriptionCommands` - [Overview](https://github.com/hyperion-project/hyperion.ng/blob/API_Auth/doc/development/JSON-API%20_Commands_Overview.md) of API commands and subscription updates diff --git a/doc/development/JSON-API _Commands_Overview.md b/doc/development/JSON-API _Commands_Overview.md index ce94092e..549c5e62 100644 --- a/doc/development/JSON-API _Commands_Overview.md +++ b/doc/development/JSON-API _Commands_Overview.md @@ -13,6 +13,7 @@ _Authorization (via password or bearer token)_ _Instance specific_ **Yes** - A specific instance can be addressed
+**Multi** - Multiple instances can be addressed via one request
**No** - The command is not instance related _http/s Support_ @@ -20,73 +21,73 @@ _http/s Support_ **Yes** - Command can be used by individual http/s requests
**No** - Applies only to WebSocket or http/s sessions -| Command | Sub-Command | Authorization | Instance specifc | http/s Support | -|:---------------|-------------------------|:--------------|:-----------------|:---------------| -| adjustment | - | Yes | Yes | Yes | -| authorize | adminRequired | No | No | Yes | -| authorize | answerRequest | Admin | No | No | -| authorize | createToken | Admin | No | No | -| authorize | deleteToken | Admin | No | Yes | -| authorize | getPendingTokenRequests | Admin | No | No | -| authorize | getTokenList | Admin | No | Yes | -| authorize | login | No | No | No | -| authorize | logout | No | No | No | -| authorize | newPassword | Admin | No | Yes | -| authorize | newPasswordRequired | No | No | Yes | -| authorize | renameToken | Admin | No | Yes | -| authorize | requestToken | No | No | Yes | -| authorize | tokenRequired | No | No | Yes | -| clear | - | Yes | Yes | Yes | -| clearall | - | Yes | Yes | Yes | -| color | - | Yes | Yes | Yes | -| componentstate | - | Yes | Yes | Yes | -| config | getconfig | Admin | Yes | Yes | -| config | getschema | Admin | Yes | Yes | -| config | reload | Admin | Yes | Yes | -| config | restoreconfig | Admin | Yes | Yes | -| config | setconfig | Admin | Yes | Yes | -| correction | - | Yes | Yes | Yes | -| create-effect | - | Yes | Yes | Yes | -| delete-effect | - | Yes | Yes | Yes | -| effect | - | Yes | Yes | Yes | -| image | - | Yes | Yes | Yes | -| inputsource | discover | Yes | No | Yes | -| inputsource | getProperties | Yes | No | Yes | -| instance | createInstance | Admin | No | Yes | -| instance | deleteInstance | Admin | No | Yes | -| instance | saveName | Admin | No | Yes | -| instance | startInstance | Yes | No | Yes | -| instance | stopInstance | Yes | No | Yes | -| instance | switchTo | Yes | No | Yes | -| ledcolors | imagestream-start | Yes | Yes | Yes | -| ledcolors | imagestream-stop | Yes | Yes | Yes | -| ledcolors | ledstream-start | Yes | Yes | Yes | -| ledcolors | ledstream-stop | Yes | Yes | Yes | -| leddevice | addAuthorization | Yes | Yes | Yes | -| leddevice | discover | Yes | Yes | Yes | -| leddevice | getProperties | Yes | Yes | Yes | -| leddevice | identify | Yes | Yes | Yes | -| logging | start | Yes | No | Yes | -| logging | stop | Yes | No | Yes | -| processing | - | Yes | Yes | Yes | -| serverinfo | - | Yes | Yes | Yes | -| serverinfo | getInfo | Yes | Yes | Yes | -| serverinfo | subscribe | Yes | Yes | No | -| serverinfo | unsubscribe | Yes | Yes | No | -| serverinfo | getSubscriptions | Yes | Yes | No | -| serverinfo | getSubscriptionCommands | No | No | No | -| service | discover | Yes | No | Yes | -| sourceselect | - | Yes | No | Yes | -| sysinfo | - | Yes | No | Yes | -| system | restart | Yes | No | Yes | -| system | resume | Yes | No | Yes | -| system | suspend | Yes | No | Yes | -| system | toggleSuspend | Yes | No | Yes | -| system | idle | Yes | No | Yes | -| system | toggleIdle | Yes | No | Yes | -| temperature | - | Yes | Yes | Yes | -| transform | - | Yes | Yes | Yes | -| videomode | - | Yes | No | Yes | +| Command | Sub-Command | Authorization | Instance specific | http/s Support | +|:---------------|:------------------------|:--------------|:------------------|:---------------| +| adjustment | - | Yes | Multi | Yes | +| authorize | adminRequired | No | No | Yes | +| authorize | answerRequest | Admin | No | No | +| authorize | createToken | Admin | No | No | +| authorize | deleteToken | Admin | No | Yes | +| authorize | getPendingTokenRequests | Admin | No | No | +| authorize | getTokenList | Admin | No | Yes | +| authorize | login | No | No | No | +| authorize | logout | No | No | No | +| authorize | newPassword | Admin | No | Yes | +| authorize | newPasswordRequired | No | No | Yes | +| authorize | renameToken | Admin | No | Yes | +| authorize | requestToken | No | No | Yes | +| authorize | tokenRequired | No | No | Yes | +| clear | - | Yes | Multi | Yes | +| clearall | - | Yes | Multi | Yes | +| color | - | Yes | Multi | Yes | +| componentstate | - | Yes | Multi | Yes | +| config | getconfig | Admin | Yes | Yes | +| config | getschema | Admin | Yes | Yes | +| config | reload | Admin | Yes | Yes | +| config | restoreconfig | Admin | Yes | Yes | +| config | setconfig | Admin | Yes | Yes | +| correction | - | Yes | Yes | Yes | +| create-effect | - | Yes | Yes | Yes | +| delete-effect | - | Yes | Yes | Yes | +| effect | - | Yes | Multi | Yes | +| image | - | Yes | Multi | Yes | +| inputsource | discover | Yes | No | Yes | +| inputsource | getProperties | Yes | No | Yes | +| instance | createInstance | Admin | No | Yes | +| instance | deleteInstance | Admin | No | Yes | +| instance | saveName | Admin | No | Yes | +| instance | startInstance | Yes | No | Yes | +| instance | stopInstance | Yes | No | Yes | +| instance | switchTo | Yes | No | Yes | +| ledcolors | imagestream-start | Yes | Yes | Yes | +| ledcolors | imagestream-stop | Yes | Yes | Yes | +| ledcolors | ledstream-start | Yes | Yes | Yes | +| ledcolors | ledstream-stop | Yes | Yes | Yes | +| leddevice | addAuthorization | Yes | Yes | Yes | +| leddevice | discover | Yes | Yes | Yes | +| leddevice | getProperties | Yes | Yes | Yes | +| leddevice | identify | Yes | Yes | Yes | +| logging | start | Yes | No | Yes | +| logging | stop | Yes | No | Yes | +| processing | - | Yes | Multi | Yes | +| serverinfo | - | Yes | Yes | Yes | +| serverinfo | getInfo | Yes | Yes | Yes | +| serverinfo | subscribe | Yes | Yes | No | +| serverinfo | unsubscribe | Yes | Yes | No | +| serverinfo | getSubscriptions | Yes | Yes | No | +| serverinfo | getSubscriptionCommands | No | No | No | +| service | discover | Yes | No | Yes | +| sourceselect | - | Yes | Multi | Yes | +| sysinfo | - | Yes | No | Yes | +| system | restart | Yes | No | Yes | +| system | resume | Yes | No | Yes | +| system | suspend | Yes | No | Yes | +| system | toggleSuspend | Yes | No | Yes | +| system | idle | Yes | No | Yes | +| system | toggleIdle | Yes | No | Yes | +| temperature | - | Yes | Yes | Yes | +| transform | - | Yes | Yes | Yes | +| videomode | - | Yes | No | Yes | ## Subscription updates diff --git a/include/api/API.h b/include/api/API.h index f50f5d2c..96e1f9b8 100644 --- a/include/api/API.h +++ b/include/api/API.h @@ -176,6 +176,12 @@ protected: /// QVector getAllInstanceData() const; + /// + /// @brief Get the current instances index + /// @return The instance index set + /// + quint8 getCurrentInstanceIndex() const { return _currInstanceIndex; } + /// /// @brief Start instance /// @param index The instance index diff --git a/include/api/JsonAPI.h b/include/api/JsonAPI.h index 714e2bfd..3f585ff4 100644 --- a/include/api/JsonAPI.h +++ b/include/api/JsonAPI.h @@ -308,6 +308,8 @@ private: void handleLedDeviceIdentify(LedDevice& ledDevice, const QJsonObject& message, const JsonApiCommand& cmd); void handleLedDeviceAddAuthorization(LedDevice& ledDevice, const QJsonObject& message, const JsonApiCommand& cmd); + QJsonObject getBasicCommandReply(bool success, const QString &command, int tan, InstanceCmd::Type isInstanceCmd) const; + /// /// Send a standard reply indicating success /// diff --git a/include/api/JsonApiCommand.h b/include/api/JsonApiCommand.h index ace11c87..c63d968b 100644 --- a/include/api/JsonApiCommand.h +++ b/include/api/JsonApiCommand.h @@ -196,7 +196,8 @@ class InstanceCmd { public: enum Type { No, - Yes + Yes, + Multi }; }; @@ -227,7 +228,7 @@ public: Command::Type getCommand() const { return command; } SubCommand::Type getSubCommand() const { return subCommand; } - InstanceCmd::Type instanceCmd() const { return isInstanceCmd; }; + InstanceCmd::Type instanceCmd() const { return isInstanceCmd; } int getTan() const { return tan; } QString toString() const { @@ -254,7 +255,7 @@ public: static const CommandLookupMap& getCommandLookup() { static const CommandLookupMap commandLookup { - { {"adjustment", ""}, { Command::Adjustment, SubCommand::Empty, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} }, + { {"adjustment", ""}, { Command::Adjustment, SubCommand::Empty, Authorization::Yes, InstanceCmd::Multi, NoListenerCmd::Yes} }, { {"authorize", "adminRequired"}, { Command::Authorize, SubCommand::AdminRequired, Authorization::No, InstanceCmd::No, NoListenerCmd::Yes} }, { {"authorize", "answerRequest"}, { Command::Authorize, SubCommand::AnswerRequest, Authorization::Admin, InstanceCmd::No, NoListenerCmd::No} }, { {"authorize", "createToken"}, { Command::Authorize, SubCommand::CreateToken, Authorization::Admin, InstanceCmd::No, NoListenerCmd::No} }, @@ -268,10 +269,10 @@ public: { {"authorize", "renameToken"}, { Command::Authorize, SubCommand::RenameToken, Authorization::Admin, InstanceCmd::No, NoListenerCmd::Yes} }, { {"authorize", "requestToken"}, { Command::Authorize, SubCommand::RequestToken, Authorization::No, InstanceCmd::No, NoListenerCmd::Yes} }, { {"authorize", "tokenRequired"}, { Command::Authorize, SubCommand::TokenRequired, Authorization::No, InstanceCmd::No, NoListenerCmd::Yes} }, - { {"clear", ""}, { Command::Clear, SubCommand::Empty, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} }, - { {"clearall", ""}, { Command::ClearAll, SubCommand::Empty, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} }, - { {"color", ""}, { Command::Color, SubCommand::Empty, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} }, - { {"componentstate", ""}, { Command::ComponentState, SubCommand::Empty, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} }, + { {"clear", ""}, { Command::Clear, SubCommand::Empty, Authorization::Yes, InstanceCmd::Multi, NoListenerCmd::Yes} }, + { {"clearall", ""}, { Command::ClearAll, SubCommand::Empty, Authorization::Yes, InstanceCmd::Multi, NoListenerCmd::Yes} }, + { {"color", ""}, { Command::Color, SubCommand::Empty, Authorization::Yes, InstanceCmd::Multi, NoListenerCmd::Yes} }, + { {"componentstate", ""}, { Command::ComponentState, SubCommand::Empty, Authorization::Yes, InstanceCmd::Multi, NoListenerCmd::Yes} }, { {"config", "getconfig"}, { Command::Config, SubCommand::GetConfig, Authorization::Admin, InstanceCmd::Yes, NoListenerCmd::Yes} }, { {"config", "getschema"}, { Command::Config, SubCommand::GetSchema, Authorization::Admin, InstanceCmd::Yes, NoListenerCmd::Yes} }, { {"config", "reload"}, { Command::Config, SubCommand::Reload, Authorization::Admin, InstanceCmd::Yes, NoListenerCmd::Yes} }, @@ -280,8 +281,8 @@ public: { {"correction", ""}, { Command::Correction, SubCommand::Empty, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} }, { {"create-effect", ""}, { Command::CreateEffect, SubCommand::Empty, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} }, { {"delete-effect", ""}, { Command::DeleteEffect, SubCommand::Empty, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} }, - { {"effect", ""}, { Command::Effect, SubCommand::Empty, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} }, - { {"image", ""}, { Command::Image, SubCommand::Empty, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} }, + { {"effect", ""}, { Command::Effect, SubCommand::Empty, Authorization::Yes, InstanceCmd::Multi, NoListenerCmd::Yes} }, + { {"image", ""}, { Command::Image, SubCommand::Empty, Authorization::Yes, InstanceCmd::Multi, NoListenerCmd::Yes} }, { {"inputsource", "discover"}, { Command::InputSource, SubCommand::Discover, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} }, { {"inputsource", "getProperties"}, { Command::InputSource, SubCommand::GetProperties, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} }, { {"instance", "createInstance"}, { Command::Instance, SubCommand::CreateInstance, Authorization::Admin, InstanceCmd::No, NoListenerCmd::Yes} }, @@ -300,7 +301,7 @@ public: { {"leddevice", "identify"}, { Command::LedDevice, SubCommand::Identify, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} }, { {"logging", "start"}, { Command::Logging, SubCommand::Start, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} }, { {"logging", "stop"}, { Command::Logging, SubCommand::Stop, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} }, - { {"processing", ""}, { Command::Processing, SubCommand::Empty, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} }, + { {"processing", ""}, { Command::Processing, SubCommand::Empty, Authorization::Yes, InstanceCmd::Multi, NoListenerCmd::Yes} }, { {"serverinfo", ""}, { Command::ServerInfo, SubCommand::Empty, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} }, { {"serverinfo", "getInfo"}, { Command::ServerInfo, SubCommand::GetInfo, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::Yes} }, { {"serverinfo", "subscribe"}, { Command::ServerInfo, SubCommand::Subscribe, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::No} }, @@ -308,7 +309,7 @@ public: { {"serverinfo", "getSubscriptions"}, { Command::ServerInfo, SubCommand::GetSubscriptions, Authorization::Yes, InstanceCmd::Yes, NoListenerCmd::No} }, { {"serverinfo", "getSubscriptionCommands"}, { Command::ServerInfo, SubCommand::GetSubscriptionCommands, Authorization::No, InstanceCmd::No, NoListenerCmd::No} }, { {"service", "discover"}, { Command::Service, SubCommand::Discover, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} }, - { {"sourceselect", ""}, { Command::SourceSelect, SubCommand::Empty, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} }, + { {"sourceselect", ""}, { Command::SourceSelect, SubCommand::Empty, Authorization::Yes, InstanceCmd::Multi, NoListenerCmd::Yes} }, { {"sysinfo", ""}, { Command::SysInfo, SubCommand::Empty, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} }, { {"system", "restart"}, { Command::System, SubCommand::Restart, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} }, { {"system", "resume"}, { Command::System, SubCommand::Resume, Authorization::Yes, InstanceCmd::No, NoListenerCmd::Yes} }, diff --git a/include/hyperion/HyperionIManager.h b/include/hyperion/HyperionIManager.h index 03e71690..d7610d5b 100644 --- a/include/hyperion/HyperionIManager.h +++ b/include/hyperion/HyperionIManager.h @@ -55,10 +55,16 @@ public slots: Hyperion* getHyperionInstance(quint8 instance = 0); /// - /// @brief Get instance data of all instaces in db + running state + /// @brief Get instance data of all instances in db + running state /// QVector getInstanceData() const; + + /// + /// @brief Get all instance indicies of running instances + /// + QList getRunningInstanceIdx() const; + /// /// @brief Start a Hyperion instance /// @param instance Instance index diff --git a/libsrc/api/JSONRPC_schema/schema-adjustment.json b/libsrc/api/JSONRPC_schema/schema-adjustment.json index ae5c1ecc..5efed868 100644 --- a/libsrc/api/JSONRPC_schema/schema-adjustment.json +++ b/libsrc/api/JSONRPC_schema/schema-adjustment.json @@ -8,9 +8,10 @@ "enum" : ["adjustment"] }, "instance" : { - "type" : "integer", - "minimum": 0, - "maximum": 255 + "type": "array", + "required": false, + "items" : {}, + "minItems": 1 }, "tan" : { "type" : "integer" diff --git a/libsrc/api/JSONRPC_schema/schema-clear.json b/libsrc/api/JSONRPC_schema/schema-clear.json index a0c2e23c..b55be0a1 100644 --- a/libsrc/api/JSONRPC_schema/schema-clear.json +++ b/libsrc/api/JSONRPC_schema/schema-clear.json @@ -8,9 +8,10 @@ "enum" : ["clear"] }, "instance" : { - "type" : "integer", - "minimum": 0, - "maximum": 255 + "type": "array", + "required": false, + "items" : {}, + "minItems": 1 }, "tan" : { "type" : "integer" diff --git a/libsrc/api/JSONRPC_schema/schema-clearall.json b/libsrc/api/JSONRPC_schema/schema-clearall.json index 391dca4d..5d5d2d22 100644 --- a/libsrc/api/JSONRPC_schema/schema-clearall.json +++ b/libsrc/api/JSONRPC_schema/schema-clearall.json @@ -8,9 +8,10 @@ "enum" : ["clearall"] }, "instance" : { - "type" : "integer", - "minimum": 0, - "maximum": 255 + "type": "array", + "required": false, + "items" : {}, + "minItems": 1 }, "tan" : { "type" : "integer" diff --git a/libsrc/api/JSONRPC_schema/schema-color.json b/libsrc/api/JSONRPC_schema/schema-color.json index ff424674..eeeba069 100644 --- a/libsrc/api/JSONRPC_schema/schema-color.json +++ b/libsrc/api/JSONRPC_schema/schema-color.json @@ -8,9 +8,10 @@ "enum" : ["color"] }, "instance" : { - "type" : "integer", - "minimum": 0, - "maximum": 255 + "type": "array", + "required": false, + "items" : {}, + "minItems": 1 }, "tan" : { "type" : "integer" diff --git a/libsrc/api/JSONRPC_schema/schema-componentstate.json b/libsrc/api/JSONRPC_schema/schema-componentstate.json index 96edbac1..10ca3bb6 100644 --- a/libsrc/api/JSONRPC_schema/schema-componentstate.json +++ b/libsrc/api/JSONRPC_schema/schema-componentstate.json @@ -10,9 +10,10 @@ "enum" : ["componentstate"] }, "instance" : { - "type" : "integer", - "minimum": 0, - "maximum": 255 + "type": "array", + "required": false, + "items" : {}, + "minItems": 1 }, "tan" : { "type" : "integer" diff --git a/libsrc/api/JSONRPC_schema/schema-effect.json b/libsrc/api/JSONRPC_schema/schema-effect.json index 6a66ad6c..5bd0aff6 100644 --- a/libsrc/api/JSONRPC_schema/schema-effect.json +++ b/libsrc/api/JSONRPC_schema/schema-effect.json @@ -8,9 +8,10 @@ "enum" : ["effect"] }, "instance" : { - "type" : "integer", - "minimum": 0, - "maximum": 255 + "type": "array", + "required": false, + "items" : {}, + "minItems": 1 }, "tan" : { "type" : "integer" diff --git a/libsrc/api/JSONRPC_schema/schema-image.json b/libsrc/api/JSONRPC_schema/schema-image.json index 12c39697..fbd2ff40 100644 --- a/libsrc/api/JSONRPC_schema/schema-image.json +++ b/libsrc/api/JSONRPC_schema/schema-image.json @@ -8,9 +8,10 @@ "enum" : ["image"] }, "instance" : { - "type" : "integer", - "minimum": 0, - "maximum": 255 + "type": "array", + "required": false, + "items" : {}, + "minItems": 1 }, "tan" : { "type" : "integer" diff --git a/libsrc/api/JSONRPC_schema/schema-processing.json b/libsrc/api/JSONRPC_schema/schema-processing.json index 6681d3b1..0ca7616d 100644 --- a/libsrc/api/JSONRPC_schema/schema-processing.json +++ b/libsrc/api/JSONRPC_schema/schema-processing.json @@ -8,9 +8,10 @@ "enum" : ["processing"] }, "instance" : { - "type" : "integer", - "minimum": 0, - "maximum": 255 + "type": "array", + "required": false, + "items" : {}, + "minItems": 1 }, "tan" : { "type" : "integer" diff --git a/libsrc/api/JSONRPC_schema/schema-sourceselect.json b/libsrc/api/JSONRPC_schema/schema-sourceselect.json index 14f9aaea..8763595c 100644 --- a/libsrc/api/JSONRPC_schema/schema-sourceselect.json +++ b/libsrc/api/JSONRPC_schema/schema-sourceselect.json @@ -7,6 +7,12 @@ "required" : true, "enum" : ["sourceselect"] }, + "instance" : { + "type": "array", + "required": false, + "items" : {}, + "minItems": 1 + }, "tan" : { "type" : "integer" }, diff --git a/libsrc/api/JsonAPI.cpp b/libsrc/api/JsonAPI.cpp index df7c16c3..9ba8845a 100644 --- a/libsrc/api/JsonAPI.cpp +++ b/libsrc/api/JsonAPI.cpp @@ -230,18 +230,80 @@ void JsonAPI::handleMessage(const QString &messageString, const QString &httpAut return; } - if ( message.contains("instance")) + if (!message.contains("instance") || cmd.isInstanceCmd == InstanceCmd::No) { - const quint8 instance = static_cast(message.value("instance").toInt()); - if (!setHyperionInstance(instance)) + handleCommand(cmd, message); + } + else + { + const QJsonValue instanceElement = message.value("instance"); + + QList runningInstanceIdxs = _instanceManager->getRunningInstanceIdx(); + + QList intanceIdxList; + QStringList errorDetails; + + QJsonArray instances; + + if (instanceElement.isDouble()) + { + instances.append(instanceElement); + } else if (instanceElement.isArray()) + { + instances = instanceElement.toArray(); + } + + if (instances.contains("all")) + { + for (const auto& instanceIdx : runningInstanceIdxs) + { + intanceIdxList.append(instanceIdx); + } + } + else + { + for (const auto &instance : std::as_const(instances)) { + + quint8 instanceIdx = static_cast(instance.toInt()); + if (instance.isDouble() && runningInstanceIdxs.contains(instanceIdx)) + { + intanceIdxList.append(instanceIdx); + } + else + { + errorDetails.append("Not a running or valid instance: " + instance.toVariant().toString()); + } + } + } + + if (intanceIdxList.isEmpty() || !errorDetails.isEmpty() ) { cmd.isInstanceCmd = InstanceCmd::No; - sendErrorReply(QString("Invalid or stopped instance: %1").arg(instance), cmd); + sendErrorReply("Invalid instance(s) given", errorDetails, cmd); return; } + + quint8 currentInstanceIdx = getCurrentInstanceIndex(); + if (intanceIdxList.size() > 1) + { + if (cmd.isInstanceCmd != InstanceCmd::Multi) + { + sendErrorReply("Command does not support multiple instances", cmd); + return; + } + } + + for (const auto &instanceIdx : intanceIdxList) + { + if (setHyperionInstance(instanceIdx)) + { + handleCommand(cmd, message); + } + } + + setHyperionInstance(currentInstanceIdx); } - handleCommand(cmd, message); } void JsonAPI::handleCommand(const JsonApiCommand& cmd, const QJsonObject &message) @@ -1314,6 +1376,20 @@ void JsonAPI::handleSystemCommand(const QJsonObject& /*message*/, const JsonApiC sendSuccessReply(cmd); } +QJsonObject JsonAPI::getBasicCommandReply(bool success, const QString &command, int tan, InstanceCmd::Type isInstanceCmd) const +{ + QJsonObject reply; + reply["success"] = success; + reply["command"] = command; + reply["tan"] = tan; + + if (isInstanceCmd != InstanceCmd::No && !_noListener) + { + reply["instance"] = _hyperion->getInstanceIndex(); + } + return reply; +} + void JsonAPI::sendSuccessReply(const JsonApiCommand& cmd) { sendSuccessReply(cmd.toString(), cmd.tan, cmd.isInstanceCmd); @@ -1321,17 +1397,7 @@ void JsonAPI::sendSuccessReply(const JsonApiCommand& cmd) void JsonAPI::sendSuccessReply(const QString &command, int tan, InstanceCmd::Type isInstanceCmd) { - QJsonObject reply; - reply["success"] = true; - reply["command"] = command; - reply["tan"] = tan; - - if (isInstanceCmd == InstanceCmd::Yes) - { - reply["instance"] = _hyperion->getInstanceIndex(); - } - - emit callbackMessage(reply); + emit callbackMessage(getBasicCommandReply(true, command, tan , isInstanceCmd)); } void JsonAPI::sendSuccessDataReply(const QJsonValue &infoData, const JsonApiCommand& cmd) @@ -1349,24 +1415,45 @@ void JsonAPI::sendSuccessDataReplyWithError(const QJsonValue &infoData, const Js sendSuccessDataReplyWithError(infoData, cmd.toString(), cmd.tan, errorDetails, cmd.isInstanceCmd); } -void JsonAPI::sendSuccessDataReplyWithError(const QJsonValue &infoData, const QString &command, int tan, const QStringList& errorDetails, InstanceCmd::Type isInstanceCmd) +void JsonAPI::sendSuccessDataReplyWithError(const QJsonValue &infoData, const QString &command, int tan, const QStringList& errorDetails, InstanceCmd::Type isInstanceCmd) { - QJsonObject reply; - reply["success"] = true; - reply["command"] = command; - reply["tan"] = tan; - - if (isInstanceCmd == InstanceCmd::Yes) - { - reply["instance"] = _hyperion->getInstanceIndex(); - } - + QJsonObject reply {getBasicCommandReply(true, command, tan , isInstanceCmd)}; reply["info"] = infoData; if (!errorDetails.isEmpty()) { QJsonArray errorsArray; - for (const QString& errorString : errorDetails) { + for (const QString& errorString : errorDetails) + { + QJsonObject errorObject; + errorObject["description"] = errorString; + errorsArray.append(errorObject); + } + reply["errorData"] = errorsArray; + } + + emit callbackMessage(reply); +} + +void JsonAPI::sendErrorReply(const QString &error, const JsonApiCommand& cmd) +{ + sendErrorReply(error, {}, cmd.toString(), cmd.tan, cmd.isInstanceCmd); +} + +void JsonAPI::sendErrorReply(const QString &error, const QStringList& errorDetails, const JsonApiCommand& cmd) +{ + sendErrorReply(error, errorDetails, cmd.toString(), cmd.tan, cmd.isInstanceCmd); +} + +void JsonAPI::sendErrorReply(const QString &error, const QStringList& errorDetails, const QString &command, int tan, InstanceCmd::Type isInstanceCmd) +{ + QJsonObject reply {getBasicCommandReply(true, command, tan , isInstanceCmd)}; + reply["error"] = error; + if (!errorDetails.isEmpty()) + { + QJsonArray errorsArray; + for (const QString& errorString : errorDetails) + { QJsonObject errorObject; errorObject["description"] = errorString; errorsArray.append(errorObject); @@ -1387,7 +1474,7 @@ void JsonAPI::sendNewRequest(const QJsonValue &infoData, const QString &command, QJsonObject request; request["command"] = command; - if (isInstanceCmd == InstanceCmd::Yes) + if (isInstanceCmd != InstanceCmd::No) { request["instance"] = _hyperion->getInstanceIndex(); } @@ -1397,49 +1484,11 @@ void JsonAPI::sendNewRequest(const QJsonValue &infoData, const QString &command, emit callbackMessage(request); } -void JsonAPI::sendErrorReply(const QString &error, const JsonApiCommand& cmd) -{ - sendErrorReply(error, {}, cmd.toString(), cmd.tan, cmd.isInstanceCmd); -} - -void JsonAPI::sendErrorReply(const QString &error, const QStringList& errorDetails, const JsonApiCommand& cmd) -{ - sendErrorReply(error, errorDetails, cmd.toString(), cmd.tan, cmd.isInstanceCmd); -} - -void JsonAPI::sendErrorReply(const QString &error, const QStringList& errorDetails, const QString &command, int tan, InstanceCmd::Type isInstanceCmd) -{ - QJsonObject reply; - reply["success"] = false; - reply["command"] = command; - reply["tan"] = tan; - - if (isInstanceCmd == InstanceCmd::Yes) - { - reply["instance"] = _hyperion->getInstanceIndex(); - } - - reply["error"] = error; - if (!errorDetails.isEmpty()) - { - QJsonArray errorsArray; - for (const QString& errorString : errorDetails) { - QJsonObject errorObject; - errorObject["description"] = errorString; - errorsArray.append(errorObject); - } - reply["errorData"] = errorsArray; - } - - emit callbackMessage(reply); -} - void JsonAPI::sendNoAuthorization(const JsonApiCommand& cmd) { sendErrorReply(NO_AUTHORIZATION, cmd); } - void JsonAPI::handleInstanceStateChange(InstanceState state, quint8 instance, const QString& /*name */) { switch (state) diff --git a/libsrc/hyperion/HyperionIManager.cpp b/libsrc/hyperion/HyperionIManager.cpp index b49d3ddf..268cbf75 100644 --- a/libsrc/hyperion/HyperionIManager.cpp +++ b/libsrc/hyperion/HyperionIManager.cpp @@ -45,6 +45,11 @@ QVector HyperionIManager::getInstanceData() const return instances; } +QList HyperionIManager::getRunningInstanceIdx() const +{ + return _runningInstances.keys(); +} + void HyperionIManager::startAll() { for(const auto & entry : _instanceTable->getAllInstances(true))