added write support for VectorProperty

Dieser Commit ist enthalten in:
Reinder Feenstra 2024-01-22 22:33:00 +01:00
Ursprung f26513ca55
Commit 4497d75e7a
6 geänderte Dateien mit 288 neuen und 6 gelöschten Zeilen

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 Reinder Feenstra
* Copyright (C) 2021,2024 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -60,6 +60,12 @@ class AbstractVectorProperty : public BaseProperty
static_assert(is_set_v<T>);
return static_cast<T>(getInt64(index));
}
virtual void setBool(int /*index*/, bool /*value*/) { Q_ASSERT(false); }
virtual void setInt(int /*index*/, int /*value*/) { Q_ASSERT(false); }
virtual void setInt64(int /*index*/, qint64 /*value*/) { Q_ASSERT(false); }
virtual void setDouble(int /*index*/, double /*value*/) { Q_ASSERT(false); }
virtual void setString(int /*index*/, const QString& /*value*/) { Q_ASSERT(false); }
};
#endif

Datei anzeigen

@ -0,0 +1,84 @@
/**
* client/src/network/vectorproperty.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2024 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "vectorproperty.hpp"
#include "connection.hpp"
#include "object.hpp"
template<class T>
static void setVectorPropertyValue(VectorProperty& property, int index, const T& value)
{
auto event = Message::newEvent(Message::Command::ObjectSetVectorProperty);
event->write(static_cast<Object*>(property.parent())->handle());
event->write(property.name().toLatin1());
event->write<uint32_t>(index);
if constexpr(std::is_same_v<T, bool>)
{
event->write(ValueType::Boolean);
event->write(value);
}
else if constexpr(std::is_integral_v<T>)
{
event->write(ValueType::Integer);
event->write<int64_t>(value);
}
else if constexpr(std::is_floating_point_v<T>)
{
event->write(ValueType::Float);
event->write<double>(value);
}
else if constexpr(std::is_same_v<T, QString>)
{
event->write(ValueType::String);
event->write(value.toUtf8());
}
else
static_assert(sizeof(T) != sizeof(T));
property.object().connection()->send(event);
}
void VectorProperty::setBool(int index, bool value)
{
setVectorPropertyValue(*this, index, value);
}
void VectorProperty::setInt(int index, int value)
{
setVectorPropertyValue(*this, index, value);
}
void VectorProperty::setInt64(int index, qint64 value)
{
setVectorPropertyValue(*this, index, value);
}
void VectorProperty::setDouble(int index, double value)
{
setVectorPropertyValue(*this, index, value);
}
void VectorProperty::setString(int index, const QString& value)
{
setVectorPropertyValue(*this, index, value);
}

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 Reinder Feenstra
* Copyright (C) 2021,2024 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -99,6 +99,12 @@ class VectorProperty : public AbstractVectorProperty
Q_ASSERT(index >= 0 && index < size());
return m_values[index];
}
void setBool(int index, bool value) final;
void setInt(int index, int value) final;
void setInt64(int index, qint64 value) final;
void setDouble(int index, double value) final;
void setString(int index, const QString& value) final;
};
#endif

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021,2023 Reinder Feenstra
* Copyright (C) 2021,2023-2024 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -30,18 +30,48 @@
template<typename T>
class VectorProperty : public AbstractVectorProperty
{
public:
using OnChanged = std::function<void(uint32_t, std::conditional_t<valueTypeByRef<T>(), const T&, T> value)>;
using OnSet = std::function<bool(uint32_t, T& value)>;
private:
std::vector<T> m_values;
OnChanged m_onChanged;
OnSet m_onSet;
public:
using const_iterator = typename std::vector<T>::const_iterator;
VectorProperty(Object& object, std::string_view name, PropertyFlags flags)
: AbstractVectorProperty(object, name, value_type<T>::value, flags)
{
}
VectorProperty(Object& object, std::string_view name, std::initializer_list<T> values, PropertyFlags flags) :
AbstractVectorProperty(object, name, value_type<T>::value, flags),
m_values{values}
{
}
VectorProperty(Object& object, std::string_view name, std::initializer_list<T> values, PropertyFlags flags, OnChanged onChanged)
: VectorProperty(object, name, values, flags)
{
m_onChanged = std::move(onChanged);
}
VectorProperty(Object& object, std::string_view name, std::initializer_list<T> values, PropertyFlags flags, OnChanged onChanged, OnSet onSet)
: VectorProperty(object, name, values, flags)
{
m_onChanged = std::move(onChanged);
m_onSet = std::move(onSet);
}
VectorProperty(Object& object, std::string_view name, std::initializer_list<T> values, PropertyFlags flags, std::nullptr_t, OnSet onSet)
: VectorProperty(object, name, values, flags)
{
m_onSet = std::move(onSet);
}
inline const_iterator begin() const { return m_values.begin(); }
inline const_iterator end() const { return m_values.end(); }
@ -63,11 +93,81 @@ class VectorProperty : public AbstractVectorProperty
return;
else if(!isWriteable())
throw not_writable_error();
else
if constexpr(std::is_integral_v<T> || std::is_floating_point_v<T>)
{
if(auto it = m_attributes.find(AttributeName::Min); it != m_attributes.end())
{
const T min = static_cast<Attribute<T>&>(*it->second).value();
if(value < min)
{
if constexpr(std::is_floating_point_v<T>)
{
if(value > min - std::numeric_limits<T>::epsilon() * 100)
value = min;
else
throw out_of_range_error();
}
else
throw out_of_range_error();
}
}
if(auto it = m_attributes.find(AttributeName::Max); it != m_attributes.end())
{
const T max = static_cast<Attribute<T>&>(*it->second).value();
if(value > max)
{
if constexpr(std::is_floating_point_v<T>)
{
if(value < max + std::numeric_limits<T>::epsilon() * 100)
value = max;
else
throw out_of_range_error();
}
else
throw out_of_range_error();
}
}
}
if constexpr(std::is_enum_v<T> && !is_set_v<T>)
{
if(auto it = m_attributes.find(AttributeName::Values); it != m_attributes.end())
{
if(auto* span = dynamic_cast<SpanAttribute<T>*>(it->second.get()))
{
const auto values = span->values();
if(std::find(values.begin(), values.end(), value) == values.end())
throw out_of_range_error();
}
else if(auto* vectorRef = dynamic_cast<VectorRefAttribute<T>*>(it->second.get()))
{
const auto* values = vectorRef->values();
if(!values || std::find(values->begin(), values->end(), value) == values->end())
throw out_of_range_error();
}
else if(auto* vector = dynamic_cast<VectorAttribute<T>*>(it->second.get()))
{
const auto& values = vector->values();
if(std::find(values.begin(), values.end(), value) == values.end())
throw out_of_range_error();
}
}
}
if(!m_onSet || m_onSet(index, value))
{
m_values[index] = value;
if(m_onChanged)
m_onChanged(index, m_values[index]);
changed();
return;
}
throw invalid_value_error();
}
void setValuesInternal(std::vector<T> values)
@ -112,6 +212,12 @@ class VectorProperty : public AbstractVectorProperty
changed();
}
void clearInternal()
{
m_values.clear();
changed();
}
std::string_view enumName() const final
{
if constexpr(std::is_enum_v<T> && !is_set_v<T>)
@ -130,6 +236,26 @@ class VectorProperty : public AbstractVectorProperty
return "";
}
inline const T& front() const
{
return m_values.front();
}
inline T& front()
{
return m_values.front();
}
inline const T& back() const
{
return m_values.back();
}
inline T& back()
{
return m_values.back();
}
inline size_t size() const final
{
return m_values.size();

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2023 Reinder Feenstra
* Copyright (C) 2019-2024 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -195,6 +195,65 @@ bool Session::processMessage(const Message& message)
}
return true;
}
case Message::Command::ObjectSetVectorProperty:
{
if(message.isRequest() || message.isEvent())
{
if(ObjectPtr object = m_handles.getItem(message.read<Handle>()))
{
if(AbstractVectorProperty* property = object->getVectorProperty(message.read<std::string>()); property && !property->isInternal())
{
try
{
const size_t index = message.read<uint32_t>();
switch(message.read<ValueType>())
{
case ValueType::Boolean:
property->setBool(index, message.read<bool>());
break;
case ValueType::Integer:
property->setInt64(index, message.read<int64_t>());
break;
case ValueType::Float:
property->setDouble(index, message.read<double>());
break;
case ValueType::String:
property->setString(index, message.read<std::string>());
break;
default:
throw std::runtime_error("invalid value type");
}
}
catch(const std::exception& e) // set property failed
{
if(message.isRequest()) // send error response
{
m_connection->sendMessage(Message::newErrorResponse(message.command(), message.requestId(), LogMessage::C1018_EXCEPTION_X, e.what()));
}
else // send changed event with current value:
objectPropertyChanged(*property);
}
if(message.isRequest()) // send success response
m_connection->sendMessage(Message::newResponse(message.command(), message.requestId()));
}
else if(message.isRequest()) // send error response
{
m_connection->sendMessage(Message::newErrorResponse(message.command(), message.requestId(), LogMessage::C1016_UNKNOWN_PROPERTY));
}
}
else if(message.isRequest()) // send error response
{
m_connection->sendMessage(Message::newErrorResponse(message.command(), message.requestId(), LogMessage::C1015_UNKNOWN_OBJECT));
}
}
return true;
}
case Message::Command::ObjectSetUnitPropertyUnit:
{
if(ObjectPtr object = m_handles.getItem(message.read<Handle>()))

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2023 Reinder Feenstra
* Copyright (C) 2019-2024 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -88,6 +88,7 @@ class Message
ObjectGetObjectPropertyObject = 44,
ObjectGetObjectVectorPropertyObject = 45,
ObjectSetVectorProperty = 46,
Discover = 255,
};