[network] Added CallMethod command to call methods by <object_path>.<method_name>
Dieser Commit ist enthalten in:
Ursprung
30adef9e29
Commit
a79dfbb5d3
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* client/src/network/callmethod.hpp
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2020-2023 Reinder Feenstra
|
||||
* Copyright (C) 2020-2026 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -127,6 +126,34 @@ int callMethodR(Method& method, std::function<void(const R&, std::optional<const
|
||||
return request->requestId();
|
||||
}
|
||||
|
||||
template<class R, class... A>
|
||||
int callMethodR(Connection& connection, std::string_view methodPath, std::function<void(const R&, std::optional<const Error>)> callback, A... args)
|
||||
{
|
||||
auto request = Message::newRequest(Message::Command::CallMethod);
|
||||
request->write(methodPath);
|
||||
static_assert(value_type_v<R> != ValueType::Invalid);
|
||||
request->write(value_type_v<R>);
|
||||
request->write<uint8_t>(sizeof...(A)); // N arguments
|
||||
|
||||
if constexpr(sizeof...(A) > 0)
|
||||
writeArguments(*request, args...);
|
||||
|
||||
connection.send(request,
|
||||
[c=&connection, callback=std::move(callback)](const std::shared_ptr<Message> message)
|
||||
{
|
||||
if(!message->isError())
|
||||
{
|
||||
callback(getResult<R>(*c, *message), {});
|
||||
}
|
||||
else
|
||||
{
|
||||
callback(R(), *message);
|
||||
}
|
||||
});
|
||||
|
||||
return request->requestId();
|
||||
}
|
||||
|
||||
template<class... A>
|
||||
int callMethod(Method& method, std::function<void(std::optional<const Error>)> callback, A... args)
|
||||
{
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* server/src/network/session.cpp
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2025 Reinder Feenstra
|
||||
* Copyright (C) 2019-2026 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -47,6 +46,19 @@
|
||||
#undef GetObject // GetObject is defined by a winapi header
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
std::pair<std::string_view, std::string_view> splitOnLastDot(std::string_view sv)
|
||||
{
|
||||
if(const auto pos = sv.rfind('.'); pos != std::string_view::npos)
|
||||
{
|
||||
return {sv.substr(0, pos), sv.substr(pos + 1)};
|
||||
}
|
||||
return {{}, sv};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Session::Session(const std::shared_ptr<ClientConnection>& connection) :
|
||||
m_connection{connection},
|
||||
m_uuid{boost::uuids::random_generator()()}
|
||||
@ -377,109 +389,27 @@ bool Session::processMessage(const Message& message)
|
||||
{
|
||||
if(AbstractMethod* method = object->getMethod(message.read<std::string>()); method && !method->isInternal())
|
||||
{
|
||||
const auto resultType = message.read<ValueType>();
|
||||
const auto argumentCount = message.read<uint8_t>();
|
||||
|
||||
Arguments args;
|
||||
for(uint8_t i = 0; i < argumentCount; i++)
|
||||
if(callMethod(message, *method))
|
||||
{
|
||||
switch(message.read<ValueType>())
|
||||
{
|
||||
case ValueType::Boolean:
|
||||
args.push_back(message.read<bool>());
|
||||
break;
|
||||
|
||||
case ValueType::Enum:
|
||||
case ValueType::Integer:
|
||||
case ValueType::Set:
|
||||
args.push_back(message.read<int64_t>());
|
||||
break;
|
||||
|
||||
case ValueType::Float:
|
||||
args.push_back(message.read<double>());
|
||||
break;
|
||||
|
||||
case ValueType::String:
|
||||
{
|
||||
auto arg = message.read<std::string>();
|
||||
if(i < method->argumentTypeInfo().size() && method->argumentTypeInfo()[i].type == ValueType::Object)
|
||||
{
|
||||
if(arg.empty())
|
||||
args.push_back(ObjectPtr());
|
||||
else if(ObjectPtr obj = Traintastic::instance->world->getObjectByPath(arg))
|
||||
args.push_back(obj);
|
||||
else
|
||||
args.push_back(arg);
|
||||
}
|
||||
else
|
||||
args.push_back(arg);
|
||||
break;
|
||||
}
|
||||
case ValueType::Object:
|
||||
args.push_back(m_handles.getItem(message.read<Handle>()));
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Message::Command::CallMethod:
|
||||
{
|
||||
const auto path = message.read<std::string_view>();
|
||||
const auto [objectPath, methodName] = splitOnLastDot(path);
|
||||
|
||||
try
|
||||
if(const auto& world = Traintastic::instance->world.value())
|
||||
{
|
||||
if(ObjectPtr object = world->getObjectByPath(objectPath))
|
||||
{
|
||||
if(AbstractMethod* method = object->getMethod(methodName); method && !method->isInternal())
|
||||
{
|
||||
AbstractMethod::Result result = method->call(args);
|
||||
|
||||
if(message.isRequest())
|
||||
if(callMethod(message, *method))
|
||||
{
|
||||
auto response = Message::newResponse(message.command(), message.requestId());
|
||||
|
||||
switch(resultType)
|
||||
{
|
||||
case ValueType::Boolean:
|
||||
response->write(std::get<bool>(result));
|
||||
break;
|
||||
|
||||
case ValueType::Integer:
|
||||
case ValueType::Enum:
|
||||
case ValueType::Set:
|
||||
response->write(std::get<int64_t>(result));
|
||||
break;
|
||||
|
||||
case ValueType::Float:
|
||||
response->write(std::get<double>(result));
|
||||
break;
|
||||
|
||||
case ValueType::String:
|
||||
response->write(std::get<std::string>(result));
|
||||
break;
|
||||
|
||||
case ValueType::Object:
|
||||
writeObject(*response, std::get<ObjectPtr>(result));
|
||||
break;
|
||||
|
||||
case ValueType::Invalid:
|
||||
break;
|
||||
}
|
||||
|
||||
m_connection->sendMessage(std::move(response));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch(const LogMessageException& e)
|
||||
{
|
||||
if(message.isRequest())
|
||||
{
|
||||
m_connection->sendMessage(Message::newErrorResponse(message.command(), message.requestId(), e.message(), e.args()));
|
||||
return true;
|
||||
}
|
||||
// we can't report it back to the caller, so just log it.
|
||||
Log::log(*object, e.message(), e.args());
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
{
|
||||
if(message.isRequest())
|
||||
{
|
||||
m_connection->sendMessage(Message::newErrorResponse(message.command(), message.requestId(), LogMessage::C1018_EXCEPTION_X, e.what()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -780,6 +710,117 @@ bool Session::isSessionObject(const ObjectPtr& object)
|
||||
return dynamic_cast<ClientThrottle*>(object.get());
|
||||
}
|
||||
|
||||
bool Session::callMethod(const Message& message, AbstractMethod& method)
|
||||
{
|
||||
const auto resultType = message.read<ValueType>();
|
||||
const auto argumentCount = message.read<uint8_t>();
|
||||
|
||||
Arguments args;
|
||||
for(uint8_t i = 0; i < argumentCount; i++)
|
||||
{
|
||||
switch(message.read<ValueType>())
|
||||
{
|
||||
case ValueType::Boolean:
|
||||
args.push_back(message.read<bool>());
|
||||
break;
|
||||
|
||||
case ValueType::Enum:
|
||||
case ValueType::Integer:
|
||||
case ValueType::Set:
|
||||
args.push_back(message.read<int64_t>());
|
||||
break;
|
||||
|
||||
case ValueType::Float:
|
||||
args.push_back(message.read<double>());
|
||||
break;
|
||||
|
||||
case ValueType::String:
|
||||
{
|
||||
auto arg = message.read<std::string>();
|
||||
if(i < method.argumentTypeInfo().size() && method.argumentTypeInfo()[i].type == ValueType::Object)
|
||||
{
|
||||
if(arg.empty())
|
||||
args.push_back(ObjectPtr());
|
||||
else if(ObjectPtr obj = Traintastic::instance->world->getObjectByPath(arg))
|
||||
args.push_back(obj);
|
||||
else
|
||||
args.push_back(arg);
|
||||
}
|
||||
else
|
||||
args.push_back(arg);
|
||||
break;
|
||||
}
|
||||
case ValueType::Object:
|
||||
args.push_back(m_handles.getItem(message.read<Handle>()));
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
AbstractMethod::Result result = method.call(args);
|
||||
|
||||
if(message.isRequest())
|
||||
{
|
||||
auto response = Message::newResponse(message.command(), message.requestId());
|
||||
|
||||
switch(resultType)
|
||||
{
|
||||
case ValueType::Boolean:
|
||||
response->write(std::get<bool>(result));
|
||||
break;
|
||||
|
||||
case ValueType::Integer:
|
||||
case ValueType::Enum:
|
||||
case ValueType::Set:
|
||||
response->write(std::get<int64_t>(result));
|
||||
break;
|
||||
|
||||
case ValueType::Float:
|
||||
response->write(std::get<double>(result));
|
||||
break;
|
||||
|
||||
case ValueType::String:
|
||||
response->write(std::get<std::string>(result));
|
||||
break;
|
||||
|
||||
case ValueType::Object:
|
||||
writeObject(*response, std::get<ObjectPtr>(result));
|
||||
break;
|
||||
|
||||
case ValueType::Invalid:
|
||||
break;
|
||||
}
|
||||
|
||||
m_connection->sendMessage(std::move(response));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch(const LogMessageException& e)
|
||||
{
|
||||
if(message.isRequest())
|
||||
{
|
||||
m_connection->sendMessage(Message::newErrorResponse(message.command(), message.requestId(), e.message(), e.args()));
|
||||
return true;
|
||||
}
|
||||
// we can't report it back to the caller, so just log it.
|
||||
Log::log(method.object(), e.message(), e.args());
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
{
|
||||
if(message.isRequest())
|
||||
{
|
||||
m_connection->sendMessage(Message::newErrorResponse(message.command(), message.requestId(), LogMessage::C1018_EXCEPTION_X, e.what()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Session::writeObject(Message& message, const ObjectPtr& object)
|
||||
{
|
||||
message.writeBlock(); // object
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* server/src/network/session.hpp
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2025 Reinder Feenstra
|
||||
* Copyright (C) 2019-2026 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -40,6 +39,7 @@ class AbstractProperty;
|
||||
class AbstractVectorProperty;
|
||||
class AbstractAttribute;
|
||||
class AbstractEvent;
|
||||
class AbstractMethod;
|
||||
class InputMonitor;
|
||||
class OutputKeyboard;
|
||||
class Board;
|
||||
@ -73,6 +73,8 @@ class Session : public std::enable_shared_from_this<Session>
|
||||
|
||||
bool isSessionObject(const ObjectPtr& object);
|
||||
|
||||
bool callMethod(const Message& message, AbstractMethod& method);
|
||||
|
||||
void writeObject(Message& message, const ObjectPtr& object);
|
||||
void writeTableModel(Message& message, const TableModelPtr& model);
|
||||
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* shared/src/message.hpp
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2025 Reinder Feenstra
|
||||
* Copyright (C) 2019-2026 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -82,6 +81,7 @@ class Message
|
||||
ObjectSetVectorProperty = 46,
|
||||
|
||||
ObjectListGetObjects = 47,
|
||||
CallMethod = 48,
|
||||
|
||||
Discover = 255,
|
||||
};
|
||||
|
||||
Laden…
x
In neuem Issue referenzieren
Einen Benutzer sperren