values.\r
o MIXER CHROMA syntax deprecated (still supported) in favour of the more\r
advanced syntax required by the rewritten chroma key code.\r
+ o Added special command REQ that can be prepended before any command to\r
+ identify the response with a client specified request id, allowing a client\r
+ to know exactly what asynchronous response matched a specific request.\r
\r
\r
\r
int min_num_params_;
std::wstring name_;
std::wstring replyString_;
+ std::wstring request_id_;
public:
AMCPCommand(const command_context& ctx, const amcp_command_func& command, int min_num_params, const std::wstring& name)
: ctx_(ctx)
return name_;
}
+ void set_request_id(std::wstring request_id)
+ {
+ request_id_ = std::move(request_id);
+ }
+
void SetReplyString(const std::wstring& str)
{
- replyString_ = str;
+ if (request_id_.empty())
+ replyString_ = str;
+ else
+ replyString_ = L"RES " + request_id_ + L" " + str;
}
};
}}}
CASPAR_THROW_EXCEPTION(file_not_found() << msg_info(L"Unknown LOCK command " + command));
}
+void req_describer(core::help_sink& sink, const core::help_repository& repo)
+{
+ sink.short_description(L"Perform any command with an additional request id identifying the response.");
+ sink.syntax(L"REQ [request_id:string] COMMAND...");
+ sink.para()
+ ->text(L"This special command modifies the AMCP protocol a little bit to prepend ")
+ ->code(L"RES request_id")->text(L" to the response, in order to see what asynchronous response matches what request.");
+ sink.para()->text(L"Examples:");
+ sink.example(L"REQ unique PLAY 1-0 AMB\n");
+ sink.example(
+ L">> REQ unique PLAY 1-0 AMB\n"
+ L"<< RES unique 202 PLAY OK");
+}
+
+
void register_commands(amcp_command_repository& repo)
{
repo.register_channel_command( L"Basic Commands", L"LOADBG", loadbg_describer, loadbg_command, 1);
repo.register_command( L"Query Commands", L"HELP", help_describer, help_command, 0);
repo.register_command( L"Query Commands", L"HELP PRODUCER", help_producer_describer, help_producer_command, 0);
repo.register_command( L"Query Commands", L"HELP CONSUMER", help_consumer_describer, help_consumer_command, 0);
+
+ repo.help_repo()->register_item({ L"AMCP", L"Protocol Commands" }, L"REQ", req_describer);
}
} //namespace amcp
* Author: Nicklas P Andersson
*/
-
+
#include "../StdAfx.h"
#include "AMCPProtocolStrategy.h"
void Parse(const std::wstring& message, ClientInfoPtr client)
{
CASPAR_LOG_COMMUNICATION(info) << L"Received message from " << client->address() << ": " << message << L"\\r\\n";
-
+
command_interpreter_result result;
if(interpret_command_string(message, result, client))
{
else
result.queue->AddCommand(result.command);
}
-
+
if (result.error != error_state::no_error)
{
std::wstringstream answer;
if (!tokens.empty() && tokens.front().at(0) == L'/')
tokens.pop_front();
+ std::wstring request_id;
+
+ if (boost::iequals(tokens.front(), L"REQ"))
+ {
+ tokens.pop_front();
+
+ if (tokens.empty())
+ {
+ result.error = error_state::parameters_error;
+ return false;
+ }
+
+ request_id = tokens.front();
+ tokens.pop_front();
+ }
+
// Fail if no more tokens.
if (tokens.empty())
{
if (result.command->parameters().size() < result.command->minimum_parameters())
result.error = error_state::parameters_error;
}
+
+ if (result.command)
+ result.command->set_request_id(std::move(request_id));
}
catch (std::out_of_range&)
{
self.channel_commands.insert(std::make_pair(std::move(name), std::make_pair(std::move(command), min_num_params)));
}
+spl::shared_ptr<core::help_repository> amcp_command_repository::help_repo() const
+{
+ return impl_->help_repo;
+}
+
}}}
void register_command(std::wstring category, std::wstring name, core::help_item_describer describer, amcp_command_func command, int min_num_params);
void register_channel_command(std::wstring category, std::wstring name, core::help_item_describer describer, amcp_command_func command, int min_num_params);
+ spl::shared_ptr<core::help_repository> help_repo() const;
private:
struct impl;
spl::shared_ptr<impl> impl_;