From 722d6d7ad5a5780aebec4204c2566b664385fccd Mon Sep 17 00:00:00 2001
From: LordGrey <48840279+Lord-Grey@users.noreply.github.com>
Date: Mon, 29 Apr 2024 20:34:15 +0200
Subject: [PATCH] Support direct or multiple instance addressing via single
requests
---
CHANGELOG.md | 2 +-
.../JSON-API _Commands_Overview.md | 135 ++++++-------
include/api/API.h | 6 +
include/api/JsonAPI.h | 2 +
include/api/JsonApiCommand.h | 23 +--
include/hyperion/HyperionIManager.h | 8 +-
.../api/JSONRPC_schema/schema-adjustment.json | 7 +-
libsrc/api/JSONRPC_schema/schema-clear.json | 7 +-
.../api/JSONRPC_schema/schema-clearall.json | 7 +-
libsrc/api/JSONRPC_schema/schema-color.json | 7 +-
.../JSONRPC_schema/schema-componentstate.json | 7 +-
libsrc/api/JSONRPC_schema/schema-effect.json | 7 +-
libsrc/api/JSONRPC_schema/schema-image.json | 7 +-
.../api/JSONRPC_schema/schema-processing.json | 7 +-
.../JSONRPC_schema/schema-sourceselect.json | 6 +
libsrc/api/JsonAPI.cpp | 183 +++++++++++-------
libsrc/hyperion/HyperionIManager.cpp | 5 +
17 files changed, 255 insertions(+), 171 deletions(-)
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))