Merge remote-tracking branch 'origin/master' into 178-add-web-based-throttle-for-phone

Dieser Commit ist enthalten in:
Reinder Feenstra 2025-04-01 22:43:49 +02:00
Commit 43292e3a69
4407 geänderte Dateien mit 1422 neuen und 871088 gelöschten Zeilen

Datei anzeigen

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="96"
height="96"
viewBox="0 0 25.399999 25.400001"
version="1.1"
id="svg8"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="add.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.6"
inkscape:cx="14.375"
inkscape:cy="40.446429"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1016"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:showpageshadow="2"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1">
<inkscape:grid
type="xygrid"
id="grid3713"
spacingx="0.26458333"
empspacing="4" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-nc/4.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-nc/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:prohibits
rdf:resource="http://creativecommons.org/ns#CommercialUse" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
</cc:License>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-271.59998)">
<circle
style="fill:#ea80fc;fill-opacity:1;stroke:none;stroke-width:2.51975;stroke-linecap:round;stroke-linejoin:round"
id="path252"
cx="12.7"
cy="284.29996"
r="12.7" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 12.7,274.77498 c -1.172633,0 -2.116667,0.94403 -2.116667,2.11666 v 5.29167 H 5.2916667 c -1.1726347,0 -2.1166667,0.94404 -2.1166667,2.11667 0,1.17263 0.944032,2.11667 2.1166667,2.11667 h 5.2916663 v 5.29167 c 0,1.17263 0.944034,2.11666 2.116667,2.11666 1.172633,0 2.116666,-0.94403 2.116666,-2.11666 v -5.29167 h 5.291667 c 1.172635,0 2.116667,-0.94404 2.116667,-2.11667 0,-1.17263 -0.944032,-2.11667 -2.116667,-2.11667 h -5.291667 v -5.29167 c 0,-1.17263 -0.944033,-2.11666 -2.116666,-2.11666 z"
id="rect843"
sodipodi:nodetypes="sscssscssscssscss" />
</g>
</svg>

Nachher

Breite:  |  Höhe:  |  Größe: 3.5 KiB

Datei anzeigen

@ -94,5 +94,6 @@
<file>board_tile.misc.label.svg</file>
<file>clear_persistent_variables.svg</file>
<file>swap.svg</file>
<file>circle/add.svg</file>
</qresource>
</RCC>

Datei anzeigen

@ -56,8 +56,14 @@ ConnectDialog::ConnectDialog(QWidget* parent, const QString& url) :
m_password->setEchoMode(QLineEdit::Password);
m_connectAutomatically->setChecked(GeneralSettings::instance().connectAutomaticallyToDiscoveredServer.value());
#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
connect(m_connectAutomatically, &QCheckBox::stateChanged, this,
[](int state)
#else
connect(m_connectAutomatically, &QCheckBox::checkStateChanged, this,
[](Qt::CheckState state)
#endif
{
GeneralSettings::instance().connectAutomaticallyToDiscoveredServer.setValue(state == Qt::Checked);
});

Datei anzeigen

@ -33,6 +33,7 @@
#include "../widget/tablewidget.hpp"
#include "../widget/alertwidget.hpp"
#include <traintastic/locale/locale.hpp>
#include <unordered_set>
ObjectSelectListDialog::ObjectSelectListDialog(Method& method, bool multiSelect, QWidget* parent) :
ObjectSelectListDialog(static_cast<InterfaceItem&>(method), multiSelect, parent)
@ -147,9 +148,16 @@ void ObjectSelectListDialog::acceptRows(const QModelIndexList& indexes)
{
if(auto* m = dynamic_cast<Method*>(&m_item))
{
// Call method only once for each row, not for every index in row
std::unordered_set<int> rowsDone;
for(const auto& index : indexes)
{
if(rowsDone.find(index.row()) != rowsDone.end())
continue;
callMethod(*m, nullptr, m_tableWidget->getRowObjectId(index.row()));
rowsDone.insert(index.row());
}
}

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2020,2023 Reinder Feenstra
* Copyright (C) 2019-2020,2023,2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -25,8 +25,13 @@
#include <QTableView>
#include <QDialogButtonBox>
#include <QPushButton>
#include <QLineEdit>
#include <QListView>
#include <QSortFilterProxyModel>
#include <QItemDelegate>
#include <QPainter>
#include <QApplication>
#include <QtWaitingSpinner/waitingspinnerwidget.h>
#include "../widget/tablewidget.hpp"
#include "../network/connection.hpp"
#include "../network/object.hpp"
#include "../network/tablemodel.hpp"
@ -35,14 +40,70 @@
#include "../widget/alertwidget.hpp"
#include <traintastic/locale/locale.hpp>
constexpr int columnUUID = 1;
class WorldListItemDelegate : public QItemDelegate
{
public:
inline WorldListItemDelegate(QListView* parent)
: QItemDelegate(parent)
{
}
inline void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const final
{
static const QSize iconSize{40, 40};
auto* model = qobject_cast<QListView*>(parent())->model();
const auto name = model->data(model->index(index.row(), 0)).toString();
const auto uuid = model->data(model->index(index.row(), 1)).toString();
const auto r = option.rect.adjusted(5, 5, -5, -5);
const int iconOffset = (r.height() - iconSize.height()) / 2;
const auto palette = QApplication::palette();
if((option.state & QStyle::State_Selected) != 0)
{
painter->fillRect(option.rect, palette.brush(QPalette::Highlight));
}
QTextOption textOption;
textOption.setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
const auto classIcon = Theme::getIcon("world");
painter->drawPixmap(r.topLeft() + QPoint(iconOffset, iconOffset), classIcon.pixmap(iconSize));
painter->setPen(palette.color(QPalette::Disabled, QPalette::Text));
painter->drawText(r.adjusted(r.height() + 10, r.height() / 2, 0, 0), uuid, textOption);
painter->setPen(palette.color(QPalette::Active, QPalette::Text));
painter->drawText(r.adjusted(r.height() + 10, 0, 0, -r.height() / 2), name, textOption);
painter->setPen(QColor(0x80, 0x80, 0x80, 0x30));
painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
}
inline QSize sizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/) const final
{
return QSize(-1, 50);
}
};
WorldListDialog::WorldListDialog(std::shared_ptr<Connection> connection, QWidget* parent) :
QDialog(parent, Qt::Dialog | Qt::WindowTitleHint | Qt::WindowCloseButtonHint),
m_connection{std::move(connection)},
m_buttons{new QDialogButtonBox(this)},
m_tableWidget{new TableWidget()}
m_search{new QLineEdit(this)},
m_list{new QListView(this)}
{
setWindowTitle(Locale::tr("qtapp.world_list_dialog:world_list"));
setWindowIcon(Theme::getIcon("world_load"));
resize(500, 400);
m_search->setPlaceholderText(Locale::tr("qtapp.world_list_dialog:search"));
m_list->setItemDelegate(new WorldListItemDelegate(m_list));
m_buttons->setStandardButtons(QDialogButtonBox::Open | QDialogButtonBox::Cancel);
m_buttons->button(QDialogButtonBox::Open)->setText(Locale::tr("qtapp.world_list_dialog:load"));
@ -50,14 +111,15 @@ WorldListDialog::WorldListDialog(std::shared_ptr<Connection> connection, QWidget
connect(m_buttons->button(QDialogButtonBox::Open), &QPushButton::clicked, this,
[this]()
{
m_uuid = m_tableWidget->getRowObjectId(m_tableWidget->selectionModel()->selectedIndexes().first().row());
m_uuid = m_list->model()->data(m_list->model()->index(m_list->selectionModel()->selectedIndexes().first().row(), columnUUID)).toString();
accept();
});
m_buttons->button(QDialogButtonBox::Cancel)->setText(Locale::tr("qtapp.world_list_dialog:cancel"));
connect(m_buttons->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &WorldListDialog::reject);
QVBoxLayout* layout = new QVBoxLayout();
layout->addWidget(m_tableWidget);
layout->addWidget(m_search);
layout->addWidget(m_list);
layout->addWidget(m_buttons);
setLayout(layout);
@ -78,20 +140,26 @@ WorldListDialog::WorldListDialog(std::shared_ptr<Connection> connection, QWidget
{
m_requestId = Connection::invalidRequestId;
m_tableWidget->setTableModel(tableModel);
m_tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
connect(m_tableWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this,
m_tableModel = tableModel;
m_tableModel->setRegionAll(true);
auto* filter = new QSortFilterProxyModel(this);
filter->setSourceModel(m_tableModel.get());
filter->setFilterCaseSensitivity(Qt::CaseInsensitive);
connect(m_search, &QLineEdit::textChanged, filter, &QSortFilterProxyModel::setFilterFixedString);
filter->setFilterFixedString(m_search->text());
m_list->setModel(filter);
m_list->setSelectionBehavior(QAbstractItemView::SelectRows);
connect(m_list->selectionModel(), &QItemSelectionModel::selectionChanged, this,
[this](const QItemSelection&, const QItemSelection&)
{
m_buttons->button(QDialogButtonBox::Open)->setEnabled(m_tableWidget->selectionModel()->selectedRows().count() == 1);
m_buttons->button(QDialogButtonBox::Open)->setEnabled(m_list->selectionModel()->selectedRows().count() == 1);
});
connect(m_tableWidget, &TableWidget::doubleClicked, this,
connect(m_list, &QListView::doubleClicked, this,
[this](const QModelIndex& index)
{
m_uuid = m_tableWidget->getRowObjectId(index.row());
m_uuid = m_list->model()->data(m_list->model()->index(index.row(), columnUUID)).toString();
accept();
});
delete spinner;
}
else if(err)

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2020 Reinder Feenstra
* Copyright (C) 2019-2020,2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -25,9 +25,11 @@
#include <QDialog>
#include "../network/objectptr.hpp"
#include "../network/tablemodelptr.hpp"
class QDialogButtonBox;
class TableWidget;
class QLineEdit;
class QListView;
class Connection;
class WorldListDialog final : public QDialog
@ -39,8 +41,10 @@ class WorldListDialog final : public QDialog
int m_requestId;
ObjectPtr m_object;
QDialogButtonBox* m_buttons; // TODO: m_buttonLoad;
TableWidget* m_tableWidget;
QLineEdit* m_search;
QListView* m_list;
QString m_uuid;
TableModelPtr m_tableModel;
public:
explicit WorldListDialog(std::shared_ptr<Connection> connection, QWidget* parent = nullptr);

Datei anzeigen

@ -133,7 +133,10 @@ void MainWindowStatusBar::updateStatuses()
m_mainWindow.connection()->cancelRequest(m_statusesRequest);
if(statuses->empty())
{
clearStatuses();
return;
}
m_statusesRequest = statuses->getObjects(0, statuses->size() - 1,
[this](const std::vector<ObjectPtr>& objects, std::optional<const Error> error)

Datei anzeigen

@ -425,7 +425,7 @@ void Connection::releaseTableModel(TableModel* tableModel)
tableModel->m_handle = invalidHandle;
}
void Connection::setTableModelRegion(TableModel* tableModel, int columnMin, int columnMax, int rowMin, int rowMax)
void Connection::setTableModelRegion(TableModel* tableModel, uint32_t columnMin, uint32_t columnMax, uint32_t rowMin, uint32_t rowMax)
{
auto event = Message::newEvent(Message::Command::TableModelSetRegion);
event->write(tableModel->handle());

Datei anzeigen

@ -152,7 +152,7 @@ class Connection : public QObject, public std::enable_shared_from_this<Connectio
[[nodiscard]] int getTableModel(const ObjectPtr& object, std::function<void(const TableModelPtr&, std::optional<const Error>)> callback);
void releaseTableModel(TableModel* tableModel);
void setTableModelRegion(TableModel* tableModel, int columnMin, int columnMax, int rowMin, int rowMax);
void setTableModelRegion(TableModel* tableModel, uint32_t columnMin, uint32_t columnMax, uint32_t rowMin, uint32_t rowMax);
[[nodiscard]] int getTileData(Board& object);

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021,2023 Reinder Feenstra
* Copyright (C) 2019-2021,2023,2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -72,7 +72,16 @@ QString TableModel::getValue(int column, int row) const
return m_texts.value(ColumnRow(column, row));
}
void TableModel::setRegion(int columnMin, int columnMax, int rowMin, int rowMax)
void TableModel::setRegionAll(bool enable)
{
m_regionAll = enable;
if(m_regionAll)
{
updateRegionAll();
}
}
void TableModel::setRegion(uint32_t columnMin, uint32_t columnMax, uint32_t rowMin, uint32_t rowMax)
{
if(m_region.columnMin != columnMin ||
m_region.columnMax != columnMax ||
@ -94,6 +103,10 @@ void TableModel::setColumnHeaders(const QVector<QString>& values)
{
beginResetModel();
m_columnHeaders = values;
if(m_regionAll)
{
updateRegionAll();
}
endResetModel();
}
}
@ -104,6 +117,22 @@ void TableModel::setRowCount(int value)
{
beginResetModel();
m_rowCount = value;
if(m_regionAll)
{
updateRegionAll();
}
endResetModel();
}
}
void TableModel::updateRegionAll()
{
if(columnCount() == 0 || rowCount() == 0)
{
setRegion(1, 0, 1, 0); // select nothing
}
else
{
setRegion(0, columnCount() - 1, 0, rowCount() - 1);
}
}

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021,2023-2024 Reinder Feenstra
* Copyright (C) 2019-2021,2023-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -45,16 +45,20 @@ class TableModel final : public QAbstractTableModel
int m_rowCount;
struct Region
{
int rowMin = 0;
int rowMax = -1;
int columnMin = 0;
int columnMax = -1;
// Default to invalid region
uint32_t rowMin = 1;
uint32_t rowMax = 0;
uint32_t columnMin = 1;
uint32_t columnMax = 0;
} m_region;
bool m_regionAll = false;
QMap<ColumnRow, QString> m_texts;
void setColumnHeaders(const QVector<QString>& values);
void setRowCount(int value);
void updateRegionAll();
public:
explicit TableModel(std::shared_ptr<Connection> connection, Handle handle, const QString& classId, QObject* parent = nullptr);
~TableModel() final;
@ -71,7 +75,8 @@ class TableModel final : public QAbstractTableModel
QString getRowObjectId(int row) const;
QString getValue(int column, int row) const;
void setRegion(int columnMin, int columnMax, int rowMin, int rowMax);
void setRegionAll(bool enable);
void setRegion(uint32_t columnMin, uint32_t columnMax, uint32_t rowMin, uint32_t rowMax);
};
#endif

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2020-2024 Reinder Feenstra
* Copyright (C) 2020-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -23,6 +23,7 @@
#include "createwidget.hpp"
#include "list/marklincanlocomotivelistwidget.hpp"
#include "objectlist/boardlistwidget.hpp"
#include "objectlist/interfacelistwidget.hpp"
#include "objectlist/throttleobjectlistwidget.hpp"
#include "objectlist/trainlistwidget.hpp"
#include "object/luascripteditwidget.hpp"
@ -47,8 +48,10 @@ QWidget* createWidgetIfCustom(const ObjectPtr& object, QWidget* parent)
{
const QString& classId = object->classId();
if(classId == "command_station_list")
return new ObjectListWidget(object, parent); // todo remove
if(classId == "list.interface")
{
return new InterfaceListWidget(object, parent);
}
else if(classId == "decoder_list")
return new ThrottleObjectListWidget(object, parent); // todo remove
else if(classId == "controller_list")

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2024 Reinder Feenstra
* Copyright (C) 2024-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -57,6 +57,12 @@ MethodIcon::MethodIcon(Method& method, QIcon icon, QWidget* parent) :
});
}
MethodIcon::MethodIcon(Method& item, QIcon icon, std::function<void()> triggered, QWidget* parent)
: MethodIcon(item, icon, parent)
{
m_triggered = std::move(triggered);
}
void MethodIcon::mousePressEvent(QMouseEvent* event)
{
if(event->button() == Qt::LeftButton)
@ -76,7 +82,14 @@ void MethodIcon::mouseReleaseEvent(QMouseEvent* event)
if(rect().contains(event->position().toPoint())) // test if mouse release in widget
#endif
{
m_method.call();
if(m_triggered)
{
m_triggered();
}
else
{
m_method.call();
}
}
}
}

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2024 Reinder Feenstra
* Copyright (C) 2024-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -31,6 +31,7 @@ class MethodIcon : public QLabel
{
protected:
Method& m_method;
std::function<void()> m_triggered;
bool m_mouseLeftButtonPressed;
void mousePressEvent(QMouseEvent* event) final;
@ -38,6 +39,7 @@ class MethodIcon : public QLabel
public:
MethodIcon(Method& item, QIcon icon, QWidget* parent = nullptr);
MethodIcon(Method& item, QIcon icon, std::function<void()> triggered, QWidget* parent = nullptr);
};
#endif

Datei anzeigen

@ -0,0 +1,119 @@
/**
* client/src/widget/objectlist/interfacelistwidget.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2025 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 "interfacelistwidget.hpp"
#include <QApplication>
#include <QItemDelegate>
#include <QListView>
#include <QMenu>
#include <QPainter>
#include <QtMath>
#include <traintastic/enum/interfacestate.hpp>
#include <traintastic/locale/locale.hpp>
#include "../methodicon.hpp"
#include "../../theme/theme.hpp"
#include "../../mainwindow.hpp"
class InterfaceListItemDelegate : public QItemDelegate
{
public:
inline InterfaceListItemDelegate(QListView* parent)
: QItemDelegate(parent)
{
}
inline void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const final
{
static const QSize classIconSize{40, 40};
static const QSize stateIconSize{16, 16};
auto* model = qobject_cast<QListView*>(parent())->model();
const auto id = model->data(model->index(index.row(), 0)).toString().prepend('#');
const auto name = model->data(model->index(index.row(), 1)).toString();
const auto state = model->data(model->index(index.row(), 2)).toString();
const auto classId = model->data(model->index(index.row(), 3)).toString();
const auto r = option.rect.adjusted(5, 5, -5, -5);
const int iconOffset = (r.height() - classIconSize.height()) / 2;
const auto palette = QApplication::palette();
QTextOption textOption;
textOption.setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
const auto classIcon = Theme::getIconForClassId(classId);
if(!classIcon.isNull())
{
painter->drawPixmap(r.topLeft() + QPoint(iconOffset, iconOffset), classIcon.pixmap(classIconSize));
}
painter->setPen(palette.color(QPalette::Disabled, QPalette::Text));
painter->drawText(r.adjusted((classIcon.isNull() ? 0 : r.height() + 10), r.height() / 2, 0, 0), id, textOption);
painter->setPen(palette.color(QPalette::Active, QPalette::Text));
painter->drawText(r.adjusted((classIcon.isNull() ? 0 : r.height() + 10), 0, 0, -r.height() / 2), name, textOption);
textOption.setAlignment(Qt::AlignRight | Qt::AlignVCenter);
const auto stateText = Locale::tr(QString(EnumName<InterfaceState>::value).append(":").append(state));
const auto stateRect = r.adjusted(0, r.height() / 2, 0, 0);
const auto stateTextWidth = qCeil(painter->boundingRect(stateRect, stateText, textOption).width());
painter->drawText(stateRect, stateText, textOption);
auto stateIcon = QIcon(Theme::getIconFile(QString("interface_state.").append(state)));
painter->drawPixmap(
stateRect.topRight() - QPoint(stateTextWidth + stateIconSize.width() + 5, -(stateRect.height() - stateIconSize.height()) / 2),
stateIcon.pixmap(stateIconSize));
painter->setPen(QColor(0x80, 0x80, 0x80, 0x30));
painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
}
inline QSize sizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/) const final
{
return QSize(-1, 50);
}
};
InterfaceListWidget::InterfaceListWidget(const ObjectPtr& object, QWidget* parent)
: StackedObjectListWidget(object, parent)
{
m_list->setItemDelegate(new InterfaceListItemDelegate(m_list));
m_listEmptyLabel->setText(Locale::tr("interface_list:list_is_empty"));
if(m_create) /*[[likely]]*/
{
m_create->setToolTip(Locale::tr("interface_list:create"));
}
if(m_createMenu) /*[[likely]]*/
{
m_createMenu->addSeparator();
m_createMenu->addAction(Theme::getIcon("wizard"), Locale::tr("list:setup_using_wizard") + "...",
[]()
{
MainWindow::instance->showAddInterfaceWizard();
});
}
}

Datei anzeigen

@ -0,0 +1,34 @@
/**
* client/src/widget/objectlist/interfacelistwidget.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2025 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.
*/
#ifndef TRAINTASTIC_CLIENT_WIDGET_OBJECTLIST_INTERFACELISTWIDGET_HPP
#define TRAINTASTIC_CLIENT_WIDGET_OBJECTLIST_INTERFACELISTWIDGET_HPP
#include "stackedobjectlistwidget.hpp"
class InterfaceListWidget : public StackedObjectListWidget
{
public:
explicit InterfaceListWidget(const ObjectPtr& object, QWidget* parent = nullptr);
};
#endif

Datei anzeigen

@ -0,0 +1,291 @@
/**
* client/src/widget/objectlist/stackedobjectlistwidget.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2025 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 "stackedobjectlistwidget.hpp"
#include <QEvent>
#include <QLabel>
#include <QListView>
#include <QMenu>
#include <QMessageBox>
#include <QStackedWidget>
#include <QToolBar>
#include <QVBoxLayout>
#include <QIdentityProxyModel>
#include <QGuiApplication>
#include <traintastic/locale/locale.hpp>
#include "../createwidget.hpp"
#include "../tablewidget.hpp"
#include "../methodicon.hpp"
#include "../../mainwindow.hpp"
#include "../../network/object.hpp"
#include "../../network/method.hpp"
#include "../../network/connection.hpp"
#include "../../network/error.hpp"
#include "../../network/callmethod.hpp"
#include "../../network/tablemodel.hpp"
#include "../../theme/theme.hpp"
#include "../../misc/methodaction.hpp"
namespace
{
class StackedObjectListProxyModel final : public QIdentityProxyModel
{
public:
StackedObjectListProxyModel(QAbstractItemModel* sourceModel)
: QIdentityProxyModel()
{
setSourceModel(sourceModel);
}
QVariant data(const QModelIndex &index, int role) const final
{
if (role == Qt::ToolTipRole)
{
return Locale::tr("stacked_object_list:click_to_edit_ctrl_click_to_open_in_a_new_window");
}
return QIdentityProxyModel::data(index, role);
}
};
}
StackedObjectListWidget::StackedObjectListWidget(const ObjectPtr& object, QWidget* parent)
: QWidget(parent)
, m_object{object}
, m_navBar{new QToolBar(this)}
, m_stack{new QStackedWidget(this)}
, m_list{new QListView(this)}
, m_listEmptyLabel{new QLabel(Locale::tr("stacked_object_list:list_is_empty"), m_list)}
, m_requestId{Connection::invalidRequestId}
{
m_navBar->hide();
m_navBar->addAction(Theme::getIcon("previous_page"), Locale::tr("stacked_object_list:back"), this, &StackedObjectListWidget::back);
m_navLabel = new QLabel(this);
m_navLabel->setAlignment(Qt::AlignCenter);
m_navLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
m_navLabel->show();
m_navBar->addWidget(m_navLabel);
if(auto* method = object->getMethod("delete"))
{
m_actionRemove = new MethodAction(Theme::getIcon("delete"), *method,
[this]()
{
if(!m_listObjectId.isEmpty())
{
callMethod(m_actionRemove->method(), nullptr, m_listObjectId);
back();
}
});
m_navBar->addAction(m_actionRemove);
}
connect(m_stack, &QStackedWidget::currentChanged,
[this](int index)
{
m_navBar->setVisible(index > 0);
});
m_requestId = object->connection()->getTableModel(object,
[this](const TableModelPtr& tableModel, std::optional<const Error> error)
{
m_requestId = Connection::invalidRequestId;
if(tableModel)
{
m_tableModel = tableModel;
m_tableModel->setRegionAll(true);
m_list->setModel(new StackedObjectListProxyModel(m_tableModel.get()));
connect(m_tableModel.get(), &TableModel::modelReset,
[this]()
{
m_listEmptyLabel->setVisible(m_tableModel->rowCount() == 0);
});
}
else if(error)
{
QMessageBox::critical(this, "Error", error->toString());
}
});
if(auto* create = object->getMethod("create"))
{
if(create->argumentTypes().size() == 0) // Create method witout argument
{
m_create = new MethodIcon(*create, Theme::getIcon("circle/add"), m_list);
}
else if(create->argumentTypes().size() == 1)
{
m_createMenu = new QMenu(this);
m_createMenu->installEventFilter(this);
QStringList classList = create->getAttribute(AttributeName::ClassList, QVariant()).toStringList();
for(const QString& classId : classList)
{
QAction* action = m_createMenu->addAction(Locale::tr("class_id:" + classId));
action->setData(classId);
connect(action, &QAction::triggered, this,
[this, create, action]()
{
cancelRequest();
m_requestId = create->call(action->data().toString(),
[this](const ObjectPtr& addedObject, std::optional<const Error> error)
{
m_requestId = Connection::invalidRequestId;
if(addedObject)
{
show(addedObject);
}
else if(error)
{
QMessageBox::critical(this, "Error", error->toString());
}
});
});
}
m_create = new MethodIcon(*create, Theme::getIcon("circle/add"),
[this]()
{
m_createMenu->popup(m_create->mapToGlobal(m_create->rect().topRight()));
}, m_list);
}
m_create->setEnabled(create->getAttributeBool(AttributeName::Enabled, true));
if(!create->getAttributeBool(AttributeName::Visible, true))
{
m_create->hide();
}
m_create->installEventFilter(this);
}
m_list->installEventFilter(this);
m_list->setSelectionMode(QListView::NoSelection);
connect(m_list, &QListView::clicked,
[this](const QModelIndex &index)
{
if(!m_tableModel) /*[[unlikely]]*/
{
return;
}
const bool openInSubWindow = (QGuiApplication::queryKeyboardModifiers() & Qt::ControlModifier);
cancelRequest();
m_requestId = m_object->connection()->getObject(m_tableModel->getRowObjectId(index.row()),
[this, openInSubWindow](const ObjectPtr& selectedObject, std::optional<const Error> error)
{
m_requestId = Connection::invalidRequestId;
if(selectedObject)
{
if(openInSubWindow)
{
MainWindow::instance->showObject(selectedObject);
}
else
{
show(selectedObject);
}
}
else if(error)
{
QMessageBox::critical(this, "Error", error->toString());
}
});
});
m_listEmptyLabel->installEventFilter(this);
m_listEmptyLabel->setWordWrap(true);
m_stack->addWidget(m_list);
auto* l = new QVBoxLayout();
l->setContentsMargins(0, 0, 0, 0);
l->addWidget(m_navBar);
l->addWidget(m_stack);
setLayout(l);
}
StackedObjectListWidget::~StackedObjectListWidget()
{
cancelRequest();
}
bool StackedObjectListWidget::eventFilter(QObject* object, QEvent* event)
{
if(m_listEmptyLabel->isVisible() && ((object == m_list && event->type() == QEvent::Resize) || (object == m_listEmptyLabel && event->type() == QEvent::Show)))
{
m_listEmptyLabel->setMaximumWidth(qRound(width() * 0.9f));
m_listEmptyLabel->adjustSize();
m_listEmptyLabel->setFixedHeight(m_listEmptyLabel->heightForWidth(m_listEmptyLabel->maximumWidth()));
m_listEmptyLabel->move((rect().bottomRight() - m_listEmptyLabel->rect().bottomRight()) / 2);
}
if(m_create && ((object == m_list && event->type() == QEvent::Resize) || (object == m_create && event->type() == QEvent::Show)))
{
auto pnt = m_create->rect().bottomRight();
pnt = m_list->rect().bottomRight() - pnt - pnt / 3;
m_create->move(pnt.x(), pnt.y());
}
if(m_createMenu && object == m_createMenu && event->type() == QEvent::Show)
{
m_createMenu->move(m_createMenu->pos() - QPoint{m_createMenu->width(), m_createMenu->height()});
return true;
}
return QWidget::eventFilter(object, event);
}
void StackedObjectListWidget::cancelRequest()
{
if(m_requestId != Connection::invalidRequestId)
{
m_object->connection()->cancelRequest(m_requestId);
}
}
void StackedObjectListWidget::back()
{
if(m_stack->currentIndex() > 0)
{
m_listObjectId.clear();
delete m_stack->currentWidget();
}
}
void StackedObjectListWidget::show(const ObjectPtr& listObject)
{
if(auto* w = createWidget(listObject, this)) /*[[likely]]*/
{
m_listObjectId = listObject->getPropertyValueString("id");
connect(w, &QWidget::windowTitleChanged, m_navLabel, &QLabel::setText);
m_navLabel->setText(w->windowTitle());
m_stack->setCurrentIndex(m_stack->addWidget(w));
}
}

Datei anzeigen

@ -0,0 +1,67 @@
/**
* client/src/widget/objectlist/stackedobjectlistwidget.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2025 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.
*/
#ifndef TRAINTASTIC_CLIENT_WIDGET_OBJECTLIST_STACKEDOBJECTLISTWIDGET_HPP
#define TRAINTASTIC_CLIENT_WIDGET_OBJECTLIST_STACKEDOBJECTLISTWIDGET_HPP
#include <QWidget>
#include "../../network/objectptr.hpp"
#include "../../network/tablemodelptr.hpp"
class QToolBar;
class QStackedWidget;
class QListView;
class QLabel;
class QMenu;
class MethodIcon;
class MethodAction;
class StackedObjectListWidget : public QWidget
{
protected:
ObjectPtr m_object;
TableModelPtr m_tableModel;
QToolBar* m_navBar;
QLabel* m_navLabel;
QStackedWidget* m_stack;
QListView* m_list;
QLabel* m_listEmptyLabel;
MethodIcon* m_create = nullptr;
QMenu* m_createMenu = nullptr;
MethodAction* m_actionRemove = nullptr;
QString m_listObjectId;
int m_requestId;
void cancelRequest();
void back();
void show(const ObjectPtr& listObject);
bool eventFilter(QObject* object, QEvent* event) override;
public:
explicit StackedObjectListWidget(const ObjectPtr& object, QWidget* parent = nullptr);
~StackedObjectListWidget() override;
};
#endif

Datei anzeigen

@ -93,27 +93,39 @@ void TableWidget::updateRegion()
const int columnCount = m_model->columnCount();
const int rowCount = m_model->rowCount();
if(columnCount == 0 || rowCount == 0)
return;
const QRect r = viewport()->rect();
const QModelIndex topLeft = indexAt(r.topLeft());
int rowMin = qMax(topLeft.row(), 0);
int rowMax = indexAt(r.bottomLeft()).row();
if(rowMax == -1)
if(rowCount == 0)
{
// Invalid region to represent empty model
rowMin = 1;
rowMax = 0;
}
else if(rowMax == -1)
rowMax = rowCount - 1;
else
rowMax = qMin(rowMax + 1, rowCount - 1);
int columnMin = qMax(topLeft.column(), 0);
int columnMax = indexAt(r.topRight()).column();
if(columnCount == 0)
{
// Invalid region to represent empty model
columnMin = 1;
columnMax = 0;
}
if(columnMax == -1)
columnMax = columnCount - 1;
else
columnMax = qMin(columnMax + 1, columnCount - 1);
m_model->setRegion(columnMin, columnMax, rowMin, rowMax);
m_model->setRegion(uint32_t(columnMin), uint32_t(columnMax),
uint32_t(rowMin), uint32_t(rowMax));
}
void TableWidget::mouseMoveEvent(QMouseEvent* event)

Datei anzeigen

@ -54,9 +54,6 @@ Name: "firewall_wlanmaus"; Description: "{cm:firewall_allow_wlanmaus_z21}"; Grou
[Files]
; Server
Source: "..\..\server\build\{#ServerExeName}"; DestDir: "{app}\server"; Flags: ignoreversion; Check: InstallServer
Source: "..\..\server\thirdparty\lua5.4\bin\win64\lua54.dll"; DestDir: "{app}\server"; Flags: ignoreversion; Check: InstallServer
Source: "..\..\server\thirdparty\libarchive\bin\archive.dll"; DestDir: "{app}\server"; Flags: ignoreversion; Check: InstallServer
Source: "..\..\server\thirdparty\zlib\bin\zlib1.dll"; DestDir: "{app}\server"; Flags: ignoreversion; Check: InstallServer
; Client
Source: "..\..\client\build\Release\{#ClientExeName}"; DestDir: "{app}\client"; Flags: ignoreversion; Check: InstallClient
Source: "..\..\client\build\Release\*.dll"; DestDir: "{app}\client"; Flags: ignoreversion; Check: InstallClient
@ -88,6 +85,11 @@ Type: files; Name: "{commonappdata}\traintastic\translations\en-us.txt"
Type: files; Name: "{commonappdata}\traintastic\translations\nl-nl.txt"
Type: files; Name: "{commonappdata}\traintastic\translations\de-de.txt"
Type: files; Name: "{commonappdata}\traintastic\translations\it-it.txt"
; Delete unused DLLs, now statically linked (TODO: remove in 0.4)
Type: files; Name: "{app}\server\lua53.dll"
Type: files; Name: "{app}\server\lua54.dll"
Type: files; Name: "{app}\server\archive.dll"
Type: files; Name: "{app}\server\zlib1.dll"
[UninstallRun]
Filename: {sys}\netsh.exe; Parameters: "advfirewall firewall delete rule name=""Traintastic server (TCP)"""; Flags: runhidden; Check: InstallServer; Tasks: firewall_traintastic

Datei anzeigen

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.15)
cmake_minimum_required(VERSION 3.18)
include(../shared/traintastic.cmake)
project(traintastic-server VERSION ${TRAINTASTIC_VERSION} DESCRIPTION "Traintastic server")
include(GNUInstallDirs)
@ -23,7 +23,7 @@ endif()
add_executable(traintastic-server src/main.cpp src/options.hpp)
add_dependencies(traintastic-server traintastic-lang)
set_target_properties(traintastic-server PROPERTIES CXX_STANDARD 17)
set_target_properties(traintastic-server PROPERTIES CXX_STANDARD 20)
target_include_directories(traintastic-server PRIVATE
${CMAKE_CURRENT_BINARY_DIR}
../shared/src)
@ -34,14 +34,17 @@ target_include_directories(traintastic-server SYSTEM PRIVATE
if(BUILD_TESTING)
add_subdirectory(thirdparty/catch2)
set_target_properties(Catch2 PROPERTIES
CXX_STANDARD 17
CXX_STANDARD 20
CXX_CLANG_TIDY ""
)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_options(Catch2 PRIVATE -Wno-restrict) # workaround GCC bug
endif()
add_executable(traintastic-server-test test/main.cpp)
add_dependencies(traintastic-server-test traintastic-lang)
target_compile_definitions(traintastic-server-test PRIVATE -DTRAINTASTIC_TEST)
set_target_properties(traintastic-server-test PROPERTIES
CXX_STANDARD 17
CXX_STANDARD 20
CXX_CLANG_TIDY ""
)
target_include_directories(traintastic-server-test PRIVATE
@ -326,110 +329,21 @@ if(BUILD_TESTING)
endif()
# zlib
if(WIN32)
set(ZLIB_INCLUDE_DIRS "thirdparty/zlib/include")
if(MSVC)
set(ZLIB_LIBRARIES zlib1)
add_custom_command(TARGET traintastic-server PRE_LINK
COMMAND lib "/def:${PROJECT_SOURCE_DIR}/thirdparty/zlib/bin/zlib1.def" /out:zlib1.lib /machine:x64)
add_custom_command(TARGET traintastic-server-test PRE_LINK
COMMAND lib "/def:${PROJECT_SOURCE_DIR}/thirdparty/zlib/bin/zlib1.def" /out:zlib1.lib /machine:x64)
else()
# MinGW can directly link .dll without import lib
set(ZLIB_LIBRARIES "${PROJECT_SOURCE_DIR}/thirdparty/zlib/bin/zlib1.dll")
endif()
# copy zlib1.dll to build directory:
add_custom_command(TARGET traintastic-server POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_SOURCE_DIR}/thirdparty/zlib/bin/zlib1.dll" .)
else()
find_package(ZLIB REQUIRED)
endif()
target_include_directories(traintastic-server PRIVATE ${ZLIB_INCLUDE_DIRS})
target_link_libraries(traintastic-server PRIVATE ${ZLIB_LIBRARIES})
find_package(ZLIB REQUIRED)
target_link_libraries(traintastic-server PRIVATE ZLIB::ZLIB)
if(BUILD_TESTING)
target_include_directories(traintastic-server-test PRIVATE ${ZLIB_INCLUDE_DIRS})
target_link_libraries(traintastic-server-test PRIVATE ${ZLIB_LIBRARIES})
target_link_libraries(traintastic-server-test PRIVATE ZLIB::ZLIB)
endif()
# libarchive
if(WIN32)
set(LibArchive_INCLUDE_DIRS "thirdparty/libarchive/include")
if(MSVC)
set(LibArchive_LIBRARIES archive)
add_custom_command(TARGET traintastic-server PRE_LINK
COMMAND lib "/def:${PROJECT_SOURCE_DIR}/thirdparty/libarchive/bin/archive.def" /out:archive.lib /machine:x64)
add_custom_command(TARGET traintastic-server-test PRE_LINK
COMMAND lib "/def:${PROJECT_SOURCE_DIR}/thirdparty/libarchive/bin/archive.def" /out:archive.lib /machine:x64)
else()
# MinGW can directly link .dll without import lib
set(LibArchive_LIBRARIES "${PROJECT_SOURCE_DIR}/thirdparty/libarchive/bin/archive.dll")
endif()
# copy archive.dll to build directory:
add_custom_command(TARGET traintastic-server POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_SOURCE_DIR}/thirdparty/libarchive/bin/archive.dll" .)
elseif(APPLE)
find_path(LibArchive_INCLUDE_DIRS
NAMES archive.h
PATHS
"/usr/local/opt/libarchive/include" # x86_64
"/opt/homebrew/opt/libarchive/include" # arm64
)
find_library(LibArchive_LIBRARIES
NAMES archive libarchive
PATHS
"/usr/local/opt/libarchive/lib/" # x86_64
"/opt/homebrew/opt/libarchive/lib" # arm64
)
else()
find_package(LibArchive REQUIRED)
endif()
target_include_directories(traintastic-server PRIVATE ${LibArchive_INCLUDE_DIRS})
target_link_libraries(traintastic-server PRIVATE ${LibArchive_LIBRARIES})
find_package(LibArchive REQUIRED)
target_link_libraries(traintastic-server PRIVATE LibArchive::LibArchive)
if(BUILD_TESTING)
target_include_directories(traintastic-server-test PRIVATE ${LibArchive_INCLUDE_DIRS})
target_link_libraries(traintastic-server-test PRIVATE ${LibArchive_LIBRARIES})
target_link_libraries(traintastic-server-test PRIVATE LibArchive::LibArchive)
endif()
# liblua5.4
if(WIN32)
add_definitions(-DLUA_BUILD_AS_DLL)
set(LUA_INCLUDE_DIR "thirdparty/lua5.4/include")
if(MSVC)
set(LUA_LIBRARIES lua54)
add_custom_command(TARGET traintastic-server PRE_LINK
COMMAND lib "/def:${PROJECT_SOURCE_DIR}/thirdparty/lua5.4/bin/win64/lua54.def" /out:lua54.lib /machine:x64)
add_custom_command(TARGET traintastic-server-test PRE_LINK
COMMAND lib "/def:${PROJECT_SOURCE_DIR}/thirdparty/lua5.4/bin/win64/lua54.def" /out:lua54.lib /machine:x64)
else()
# MinGW can directly link .dll without import lib
set(LUA_LIBRARIES "${PROJECT_SOURCE_DIR}/thirdparty/lua5.4/bin/win64/lua54.dll")
endif()
# copy lua54.dll to build directory, to be able to run the tests:
add_custom_command(TARGET traintastic-server-test POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_SOURCE_DIR}/thirdparty/lua5.4/bin/win64/lua54.dll" .)
elseif(APPLE)
find_path(LUA_INCLUDE_DIR
NAMES lua.h
PATHS
"/usr/local/opt/lua@5.4/include/lua" # x86_64
"/opt/homebrew/opt/lua@5.4/include/lua" # arm64
)
find_library(LUA_LIBRARIES
NAMES lua5.4 liblua5.4
PATHS
"/usr/local/opt/lua@5.4/lib" # x86_64
"/opt/homebrew/opt/lua@5.4/lib" # arm64
)
else()
find_package(Lua 5.4 REQUIRED)
endif()
# lua
find_package(Lua REQUIRED)
target_include_directories(traintastic-server PRIVATE ${LUA_INCLUDE_DIR})
target_link_libraries(traintastic-server PRIVATE ${LUA_LIBRARIES})
if(BUILD_TESTING)

Datei anzeigen

@ -1,122 +0,0 @@
# Locate Lua library
# This module defines
# LUA_EXECUTABLE, if found
# LUA_FOUND, if false, do not try to link to Lua
# LUA_LIBRARIES
# LUA_INCLUDE_DIR, where to find lua.h
# LUA_VERSION_STRING, the version of Lua found (since CMake 2.8.8)
#
# Note that the expected include convention is
# #include "lua.h"
# and not
# #include <lua/lua.h>
# This is because, the lua location is not standardized and may exist
# in locations other than lua/
#=============================================================================
# Copyright 2007-2009 Kitware, Inc.
# Modified to support Lua 5.2 by LuaDist 2012
# Modified to support Lua 5.3 by Reinder Feenstra 2019
# Modified to support Lua 5.4 by Reinder Feenstra 2024
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
#
# The required version of Lua can be specified using the
# standard syntax, e.g. FIND_PACKAGE(Lua 5.1)
# Otherwise the module will search for any available Lua implementation
# Always search for non-versioned lua first (recommended)
SET(_POSSIBLE_LUA_INCLUDE include include/lua)
SET(_POSSIBLE_LUA_EXECUTABLE lua)
SET(_POSSIBLE_LUA_LIBRARY lua)
# Determine possible naming suffixes (there is no standard for this)
IF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR)
SET(_POSSIBLE_SUFFIXES "${Lua_FIND_VERSION_MAJOR}${Lua_FIND_VERSION_MINOR}" "${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}" "-${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}")
ELSE(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR)
SET(_POSSIBLE_SUFFIXES "54" "5.4" "-5.4" "53" "5.3" "-5.3" "52" "5.2" "-5.2" "51" "5.1" "-5.1")
ENDIF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR)
# Set up possible search names and locations
FOREACH(_SUFFIX ${_POSSIBLE_SUFFIXES})
LIST(APPEND _POSSIBLE_LUA_INCLUDE "include/lua${_SUFFIX}")
LIST(APPEND _POSSIBLE_LUA_EXECUTABLE "lua${_SUFFIX}")
LIST(APPEND _POSSIBLE_LUA_LIBRARY "lua${_SUFFIX}")
ENDFOREACH(_SUFFIX)
# Find the lua executable
FIND_PROGRAM(LUA_EXECUTABLE
NAMES ${_POSSIBLE_LUA_EXECUTABLE}
)
# Find the lua header
FIND_PATH(LUA_INCLUDE_DIR lua.h
HINTS
$ENV{LUA_DIR}
PATH_SUFFIXES ${_POSSIBLE_LUA_INCLUDE}
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/sw # Fink
/opt/homebrew # MacOS Apple Silicone
/opt/local # DarwinPorts
/opt/csw # Blastwave
/opt
)
# Find the lua library
FIND_LIBRARY(LUA_LIBRARY
NAMES ${_POSSIBLE_LUA_LIBRARY}
HINTS
$ENV{LUA_DIR}
PATH_SUFFIXES lib64 lib
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/sw
/opt/homebrew # MacOS Apple Silicone
/opt/local
/opt/csw
/opt
)
IF(LUA_LIBRARY)
# include the math library for Unix
IF(UNIX AND NOT APPLE)
FIND_LIBRARY(LUA_MATH_LIBRARY m)
SET( LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}" CACHE STRING "Lua Libraries")
# For Windows and Mac, don't need to explicitly include the math library
ELSE(UNIX AND NOT APPLE)
SET( LUA_LIBRARIES "${LUA_LIBRARY}" CACHE STRING "Lua Libraries")
ENDIF(UNIX AND NOT APPLE)
ENDIF(LUA_LIBRARY)
# Determine Lua version
IF(LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/lua.h")
FILE(STRINGS "${LUA_INCLUDE_DIR}/lua.h" lua_version_str REGEX "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua .+\"")
STRING(REGEX REPLACE "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua ([^\"]+)\".*" "\\1" LUA_VERSION_STRING "${lua_version_str}")
UNSET(lua_version_str)
ENDIF()
INCLUDE(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if
# all listed variables are TRUE
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lua
REQUIRED_VARS LUA_LIBRARIES LUA_INCLUDE_DIR
VERSION_VAR LUA_VERSION_STRING)
MARK_AS_ADVANCED(LUA_INCLUDE_DIR LUA_LIBRARIES LUA_LIBRARY LUA_MATH_LIBRARY LUA_EXECUTABLE)

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2022-2024 Reinder Feenstra
* Copyright (C) 2022-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -64,14 +64,6 @@ AbstractSignalPath::AbstractSignalPath(SignalRailTile& signal, size_t blocksAhea
}
}
AbstractSignalPath::~AbstractSignalPath()
{
for(auto& connection : m_connections)
{
connection.disconnect();
}
}
void AbstractSignalPath::evaluate()
{
const bool stop = !signal().hasReservedPath() && requireReservation();
@ -114,7 +106,7 @@ std::tuple<const AbstractSignalPath::BlockItem*, const AbstractSignalPath::Signa
return {nullptr, nullptr};
}
void AbstractSignalPath::getBlockStates(tcb::span<BlockState> blockStates) const
void AbstractSignalPath::getBlockStates(std::span<BlockState> blockStates) const
{
size_t i = 0;
const Item* item = m_root.get();

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2022-2023 Reinder Feenstra
* Copyright (C) 2022-2023,2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -155,7 +155,7 @@ class AbstractSignalPath : public Path
private:
std::unique_ptr<const Item> m_root;
bool m_requireReservation = false;
std::vector<boost::signals2::connection> m_connections;
std::vector<boost::signals2::scoped_connection> m_connections;
std::unique_ptr<const Item> findBlocks(const Node& node, const Link& link, size_t blocksAhead);
@ -193,13 +193,13 @@ class AbstractSignalPath : public Path
return nextBlockOrSignal(root());
}
void getBlockStates(tcb::span<BlockState> blockStates) const;
void getBlockStates(std::span<BlockState> blockStates) const;
std::shared_ptr<BlockRailTile> getBlock(size_t index) const;
public:
AbstractSignalPath(SignalRailTile& signal);
AbstractSignalPath(SignalRailTile& signal, size_t blocksAhead);
virtual ~AbstractSignalPath();
virtual ~AbstractSignalPath() = default;
void evaluate();
};

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2023-2024 Reinder Feenstra
* Copyright (C) 2023-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -37,7 +37,7 @@ const std::shared_ptr<Link>& Path::otherLink(const Node& node, const Link& link)
return noLink;
}
tcb::span<const Path::TurnoutPositionLink> Path::getTurnoutLinks(TurnoutRailTile& turnout, const Link& link)
std::span<const Path::TurnoutPositionLink> Path::getTurnoutLinks(TurnoutRailTile& turnout, const Link& link)
{
static constexpr std::array<TurnoutPositionLink, 1> straight0{{{TurnoutPosition::Straight, 0}}};
static constexpr std::array<TurnoutPositionLink, 1> left0{{{TurnoutPosition::Left, 0}}};

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2023 Reinder Feenstra
* Copyright (C) 2023,2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -24,7 +24,7 @@
#define TRAINTASTIC_SERVER_BOARD_MAP_PATH_HPP
#include <memory>
#include <tcb/span.hpp>
#include <span>
class TurnoutRailTile;
enum class TurnoutPosition : uint8_t;
@ -41,7 +41,7 @@ protected:
};
static const std::shared_ptr<Link>& otherLink(const Node& node, const Link& link);
static tcb::span<const TurnoutPositionLink> getTurnoutLinks(TurnoutRailTile& turnout, const Link& link);
static std::span<const TurnoutPositionLink> getTurnoutLinks(TurnoutRailTile& turnout, const Link& link);
};
#endif

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2024 Reinder Feenstra
* Copyright (C) 2024-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -74,7 +74,7 @@ SwitchTile::SwitchTile(World& world, std::string_view _id)
m_interfaceItems.add(colorOff);
Attributes::addObjectEditor(value, false);
Attributes::addAliases(value, tcb::span<const bool>(valueAliasKeys), tcb::span<const std::string>(valueAliasValues));
Attributes::addAliases(value, std::span<const bool>(valueAliasKeys), std::span<const std::string>(valueAliasValues));
m_interfaceItems.add(value);
Attributes::addDisplayName(outputMap, DisplayName::BoardTile::outputMap);

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2020-2024 Reinder Feenstra
* Copyright (C) 2020-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -71,7 +71,7 @@ Signal2AspectRailTile::Signal2AspectRailTile(World& world, std::string_view _id)
SignalRailTile(world, _id, TileId::RailSignal2Aspect)
{
// Skip Unknown aspect
tcb::span<const SignalAspect, 2> setAspectValues = tcb::make_span(aspectValues).subspan<1>();
std::span<const SignalAspect, 2> setAspectValues = std::span(aspectValues).subspan<1>();
outputMap.setValueInternal(std::make_shared<SignalOutputMap>(*this, outputMap.name(), std::initializer_list<SignalAspect>{SignalAspect::Stop, SignalAspect::Proceed}, getDefaultActionValue));

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2020-2024 Reinder Feenstra
* Copyright (C) 2020-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -114,7 +114,7 @@ Signal3AspectRailTile::Signal3AspectRailTile(World& world, std::string_view _id)
SignalRailTile(world, _id, TileId::RailSignal3Aspect)
{
// Skip Unknown aspect
tcb::span<const SignalAspect, 3> setAspectValues = tcb::make_span(aspectValues).subspan<1>();
std::span<const SignalAspect, 3> setAspectValues = std::span(aspectValues).subspan<1>();
outputMap.setValueInternal(std::make_shared<SignalOutputMap>(*this, outputMap.name(), std::initializer_list<SignalAspect>{SignalAspect::Stop, SignalAspect::ProceedReducedSpeed, SignalAspect::Proceed}, getDefaultActionValue));

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2020-2022,2024 Reinder Feenstra
* Copyright (C) 2020-2022,2024-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -63,7 +63,7 @@ Turnout3WayRailTile::Turnout3WayRailTile(World& world, std::string_view _id)
: TurnoutRailTile(world, _id, TileId::RailTurnout3Way, 4)
{
// Skip Unknown position
tcb::span<const TurnoutPosition, 3> setPositionValues = tcb::make_span(positionValues).subspan<1>();
std::span<const TurnoutPosition, 3> setPositionValues = std::span(positionValues).subspan<1>();
outputMap.setValueInternal(std::make_shared<TurnoutOutputMap>(*this, outputMap.name(), std::initializer_list<TurnoutPosition>{TurnoutPosition::Straight, TurnoutPosition::Left, TurnoutPosition::Right}, getDefaultActionValue));

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2020-2022,2024 Reinder Feenstra
* Copyright (C) 2020-2022,2024-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -37,10 +37,10 @@ namespace PositionValues
};
}
static constexpr tcb::span<const TurnoutPosition> positionValuesSingleMotor = tcb::make_span(PositionValues::singleMotor);
static constexpr tcb::span<const TurnoutPosition> positionValuesDualMotor = tcb::make_span(PositionValues::dualMotor);
static constexpr tcb::span<const TurnoutPosition> setPositionValuesSingleMotor = tcb::make_span(PositionValues::singleMotor).subspan<1>();
static constexpr tcb::span<const TurnoutPosition> setPositionValuesDualMotor = tcb::make_span(PositionValues::dualMotor).subspan<1>();
static constexpr std::span<const TurnoutPosition> positionValuesSingleMotor = std::span(PositionValues::singleMotor);
static constexpr std::span<const TurnoutPosition> positionValuesDualMotor = std::span(PositionValues::dualMotor);
static constexpr std::span<const TurnoutPosition> setPositionValuesSingleMotor = std::span(PositionValues::singleMotor).subspan<1>();
static constexpr std::span<const TurnoutPosition> setPositionValuesDualMotor = std::span(PositionValues::dualMotor).subspan<1>();
TurnoutDoubleSlipRailTile::TurnoutDoubleSlipRailTile(World& world, std::string_view _id)
: TurnoutSlipRailTile(world, _id, TileId::RailTurnoutDoubleSlip)

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2020-2022,2024 Reinder Feenstra
* Copyright (C) 2020-2022,2024-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -67,7 +67,7 @@ TurnoutLeftRailTile::TurnoutLeftRailTile(World& world, std::string_view _id, Til
: TurnoutRailTile(world, _id, tileId_, 3)
{
// Skip Unknown position
tcb::span<const TurnoutPosition, 2> setPositionValues = tcb::make_span(positionValues).subspan<1>();
std::span<const TurnoutPosition, 2> setPositionValues = std::span(positionValues).subspan<1>();
outputMap.setValueInternal(std::make_shared<TurnoutOutputMap>(*this, outputMap.name(), std::initializer_list<TurnoutPosition>{TurnoutPosition::Straight, TurnoutPosition::Left}, getDefaultActionValue));

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2020-2022,2024 Reinder Feenstra
* Copyright (C) 2020-2022,2024-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -67,7 +67,7 @@ TurnoutRightRailTile::TurnoutRightRailTile(World& world, std::string_view _id, T
: TurnoutRailTile(world, _id, tileId_, 3)
{
// Skip Unknown position
tcb::span<const TurnoutPosition, 2> setPositionValues = tcb::make_span(positionValues).subspan<1>();
std::span<const TurnoutPosition, 2> setPositionValues = std::span(positionValues).subspan<1>();
outputMap.setValueInternal(std::make_shared<TurnoutOutputMap>(*this, outputMap.name(), std::initializer_list<TurnoutPosition>{TurnoutPosition::Straight, TurnoutPosition::Right}, getDefaultActionValue));

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2020-2022,2024 Reinder Feenstra
* Copyright (C) 2020-2022,2024-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -37,10 +37,10 @@ namespace PositionValues
};
}
static constexpr tcb::span<const TurnoutPosition> positionValuesSingleMotor = tcb::make_span(PositionValues::singleMotor);
static constexpr tcb::span<const TurnoutPosition> positionValuesDualMotor = tcb::make_span(PositionValues::dualMotor);
static constexpr tcb::span<const TurnoutPosition> setPositionValuesSingleMotor = tcb::make_span(PositionValues::singleMotor).subspan<1>();
static constexpr tcb::span<const TurnoutPosition> setPositionValuesDualMotor = tcb::make_span(PositionValues::dualMotor).subspan<1>();
static constexpr std::span<const TurnoutPosition> positionValuesSingleMotor = std::span(PositionValues::singleMotor);
static constexpr std::span<const TurnoutPosition> positionValuesDualMotor = std::span(PositionValues::dualMotor);
static constexpr std::span<const TurnoutPosition> setPositionValuesSingleMotor = std::span(PositionValues::singleMotor).subspan<1>();
static constexpr std::span<const TurnoutPosition> setPositionValuesDualMotor = std::span(PositionValues::dualMotor).subspan<1>();
TurnoutSingleSlipRailTile::TurnoutSingleSlipRailTile(World& world, std::string_view _id)
: TurnoutSlipRailTile(world, _id, TileId::RailTurnoutSingleSlip)

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2020-2022,2024 Reinder Feenstra
* Copyright (C) 2020-2022,2024-2025 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,7 +40,7 @@ TurnoutWyeRailTile::TurnoutWyeRailTile(World& world, std::string_view _id)
: TurnoutRailTile(world, _id, TileId::RailTurnoutWye, 3)
{
// Skip Unknown position
tcb::span<const TurnoutPosition, 2> setPositionValues = tcb::make_span(positionValues).subspan<1>();
std::span<const TurnoutPosition, 2> setPositionValues = std::span(positionValues).subspan<1>();
outputMap.setValueInternal(std::make_shared<TurnoutOutputMap>(*this, outputMap.name(), std::initializer_list<TurnoutPosition>{TurnoutPosition::Left, TurnoutPosition::Right}, getDefaultActionValue));

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2020-2021,2023-2024 Reinder Feenstra
* Copyright (C) 2020-2021,2023-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -37,7 +37,7 @@ Tile::Tile(World& world, std::string_view _id, TileId tileId_)
, width{this, "width", 1, PropertyFlags::ReadOnly | PropertyFlags::Store}
{
Attributes::addObjectEditor(tileId, false);
Attributes::addValues(tileId, tcb::span<const TileId, 0>{});
Attributes::addValues(tileId, std::span<const TileId, 0>{});
m_interfaceItems.add(tileId);
Attributes::addObjectEditor(x, false);

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021-2022 Reinder Feenstra
* Copyright (C) 2021-2022,2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -25,7 +25,7 @@
#include "interfaceitem.hpp"
#include <list>
#include <tcb/span.hpp>
#include <span>
#include "eventflags.hpp"
#include "argument.hpp"
#include "typeinfo.hpp"
@ -54,7 +54,7 @@ class AbstractEvent : public InterfaceItem
inline EventFlags flags() const { return m_flags; }
virtual tcb::span<const TypeInfo> argumentTypeInfo() const = 0;
virtual std::span<const TypeInfo> argumentTypeInfo() const = 0;
void connect(std::shared_ptr<AbstractEventHandler> handler);
bool disconnect(const std::shared_ptr<AbstractEventHandler>& handler);

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2022 Reinder Feenstra
* Copyright (C) 2019-2022,2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -28,7 +28,7 @@
#include <vector>
#include <variant>
#include <stdexcept>
#include <tcb/span.hpp>
#include <span>
#include "argument.hpp"
#include "typeinfo.hpp"
@ -113,7 +113,7 @@ class AbstractMethod : public InterfaceItem
inline MethodFlags flags() const { return m_flags; }
virtual tcb::span<const TypeInfo> argumentTypeInfo() const = 0;
virtual std::span<const TypeInfo> argumentTypeInfo() const = 0;
virtual TypeInfo resultTypeInfo() const = 0;
virtual Result call(const Arguments& args) = 0;
};

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,2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -24,7 +24,7 @@
#define TRAINTASTIC_SERVER_CORE_ABSTRACTVECTORPROPERTY_HPP
#include "baseproperty.hpp"
#include <tcb/span.hpp>
#include <span>
#include "objectptr.hpp"
class AbstractVectorProperty : public BaseProperty
@ -52,7 +52,7 @@ class AbstractVectorProperty : public BaseProperty
virtual void setObject(size_t index, const ObjectPtr& value) = 0;
virtual void loadJSON(const nlohmann::json& values) = 0;
virtual void loadObjects(tcb::span<ObjectPtr> values) = 0;
virtual void loadObjects(std::span<ObjectPtr> values) = 0;
};
#endif

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2024 Reinder Feenstra
* Copyright (C) 2019-2025 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,7 +30,7 @@
#include "property.hpp"
#include "unitproperty.hpp"
#include "vectorproperty.hpp"
#include <tcb/span.hpp>
#include <span>
struct Attributes
{
@ -59,7 +59,7 @@ struct Attributes
}
template<class T>
static inline void addAliases(Property<T>& property, tcb::span<const T> keys, tcb::span<const std::string> values)
static inline void addAliases(Property<T>& property, std::span<const T> keys, std::span<const std::string> values)
{
assert(keys.size() == values.size());
property.addAttribute(AttributeName::AliasKeys, keys);
@ -67,7 +67,7 @@ struct Attributes
}
template<class T>
static inline void setAliases(Property<T>& property, tcb::span<const T> keys, tcb::span<const std::string> values)
static inline void setAliases(Property<T>& property, std::span<const T> keys, std::span<const std::string> values)
{
assert(keys.size() == values.size());
property.setAttribute(AttributeName::AliasKeys, keys);
@ -75,7 +75,7 @@ struct Attributes
}
template<class T, typename Unit>
static inline void addAliases(UnitProperty<T, Unit>& property, tcb::span<const T> keys, tcb::span<const std::string> values)
static inline void addAliases(UnitProperty<T, Unit>& property, std::span<const T> keys, std::span<const std::string> values)
{
assert(keys.size() == values.size());
property.addAttribute(AttributeName::AliasKeys, keys);
@ -88,7 +88,7 @@ struct Attributes
}
template<size_t N>
static inline void addClassList(InterfaceItem& item, tcb::span<const std::string_view, N> classList)
static inline void addClassList(InterfaceItem& item, std::span<const std::string_view, N> classList)
{
item.addAttribute(AttributeName::ClassList, classList);
}
@ -316,19 +316,19 @@ struct Attributes
}
template<class R, class T, size_t N>
static inline void addValues(Method<R(T)>& method, tcb::span<const T, N> values)
static inline void addValues(Method<R(T)>& method, std::span<const T, N> values)
{
method.addAttribute(AttributeName::Values, values);
}
template<typename T, size_t N>
static inline void addValues(Property<T>& property, tcb::span<const T, N> values)
static inline void addValues(Property<T>& property, std::span<const T, N> values)
{
property.addAttribute(AttributeName::Values, values);
}
template<typename T, typename Unit, size_t N>
static inline void addValues(UnitProperty<T, Unit>& property, tcb::span<const T, N> values)
static inline void addValues(UnitProperty<T, Unit>& property, std::span<const T, N> values)
{
property.addAttribute(AttributeName::Values, values);
}
@ -370,13 +370,13 @@ struct Attributes
}
template<typename T, size_t N>
static inline void setValues(Property<T>& property, tcb::span<const T, N> values)
static inline void setValues(Property<T>& property, std::span<const T, N> values)
{
property.setAttribute(AttributeName::Values, values);
}
template<typename T, typename Unit, size_t N>
static inline void setValues(UnitProperty<T, Unit>& property, tcb::span<const T, N> values)
static inline void setValues(UnitProperty<T, Unit>& property, std::span<const T, N> values)
{
property.setAttribute(AttributeName::Values, values);
}
@ -400,7 +400,7 @@ struct Attributes
}
template<class R, class T, size_t N>
static inline void setValues(Method<R(T)>& method, tcb::span<const T, N> values)
static inline void setValues(Method<R(T)>& method, std::span<const T, N> values)
{
method.setAttribute(AttributeName::Values, values);
}

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,2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -67,7 +67,7 @@ class Event : public AbstractEvent
{
}
tcb::span<const TypeInfo> argumentTypeInfo() const final
std::span<const TypeInfo> argumentTypeInfo() const final
{
return {typeInfoArray<Args...>};
}

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2022 Reinder Feenstra
* Copyright (C) 2019-2022,2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -54,7 +54,7 @@ class InterfaceItem
}
template<typename T, size_t N>
void addAttribute(AttributeName name, tcb::span<const T, N> values)
void addAttribute(AttributeName name, std::span<const T, N> values)
{
assert(m_attributes.find(name) == m_attributes.end());
m_attributes.emplace(name, std::make_unique<SpanAttribute<T, N>>(*this, name, values));
@ -63,7 +63,7 @@ class InterfaceItem
template<typename T, size_t N>
void addAttribute(AttributeName name, const std::array<T, N>& values)
{
addAttribute(name, tcb::span<const T, N>{values.data(), values.size()});
addAttribute(name, std::span<const T, N>{values.data(), values.size()});
}
template<typename T>
@ -88,7 +88,7 @@ class InterfaceItem
}
template<typename T, size_t N>
void setAttribute(AttributeName name, tcb::span<const T, N> values)
void setAttribute(AttributeName name, std::span<const T, N> values)
{
assert(m_attributes.find(name) != m_attributes.end());
static_cast<SpanAttribute<T, N>*>(m_attributes[name].get())->setValues(values);

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2023 Reinder Feenstra
* Copyright (C) 2019-2023,2025 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,7 +47,7 @@ class Method<R(A...)> : public AbstractMethod
R operator()(A... args);
tcb::span<const TypeInfo> argumentTypeInfo() const final;
std::span<const TypeInfo> argumentTypeInfo() const final;
TypeInfo resultTypeInfo() const final;

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2023 Reinder Feenstra
* Copyright (C) 2023,2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -115,7 +115,7 @@ R Method<R(A...)>::operator()(A... args)
}
template<class R, class... A>
tcb::span<const TypeInfo> Method<R(A...)>::argumentTypeInfo() const
std::span<const TypeInfo> Method<R(A...)>::argumentTypeInfo() const
{
return {typeInfoArray<A...>};
}

Datei anzeigen

@ -42,7 +42,7 @@ class ObjectList : public AbstractObjectList
protected:
Items m_items;
std::unordered_map<Object*, boost::signals2::connection> m_propertyChanged;
std::unordered_map<Object*, boost::signals2::scoped_connection> m_propertyChanged;
std::vector<ObjectListTableModel<T>*> m_models;
void deleteMethodHandler(const std::shared_ptr<T>& object)
@ -121,12 +121,6 @@ class ObjectList : public AbstractObjectList
static_assert(std::is_base_of_v<Object, T>);
}
~ObjectList()
{
for(auto& it : m_propertyChanged)
it.second.disconnect();
}
inline const_iterator begin() const noexcept { return m_items.begin(); }
inline const_iterator end() const noexcept { return m_items.end(); }
inline const std::shared_ptr<T>& front() const noexcept { return m_items.front(); }

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,2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -159,7 +159,7 @@ class ObjectVectorProperty : public AbstractObjectVectorProperty
m_values = std::move(values);
}
void loadObjects(tcb::span<ObjectPtr> values) final;
void loadObjects(std::span<ObjectPtr> values) 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,2025 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,7 +47,7 @@ void ObjectVectorProperty<T>::setObject(size_t index, const ObjectPtr& value)
}
template<class T>
void ObjectVectorProperty<T>::loadObjects(tcb::span<ObjectPtr> values)
void ObjectVectorProperty<T>::loadObjects(std::span<ObjectPtr> values)
{
std::vector<std::shared_ptr<T>> objects;
objects.reserve(values.size());

Datei anzeigen

@ -40,8 +40,3 @@ SerialDeviceProperty::SerialDeviceProperty(Object* object, std::string_view name
static_cast<VectorRefAttribute<std::string>&>(*it->second).internalChanged();
});
}
SerialDeviceProperty::~SerialDeviceProperty()
{
m_serialPortListChanged.disconnect();
}

Datei anzeigen

@ -29,11 +29,11 @@
class SerialDeviceProperty final : public Property<std::string>
{
private:
boost::signals2::connection m_serialPortListChanged;
boost::signals2::scoped_connection m_serialPortListChanged;
public:
SerialDeviceProperty(Object* object, std::string_view name, const std::string& value, PropertyFlags flags);
~SerialDeviceProperty() final;
~SerialDeviceProperty() final = default;
};
#endif

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2020,2023-2024 Reinder Feenstra
* Copyright (C) 2019-2020,2023-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -23,15 +23,15 @@
#ifndef TRAINTASTIC_SERVER_CORE_SPANATTRIBUTE_HPP
#define TRAINTASTIC_SERVER_CORE_SPANATTRIBUTE_HPP
#include <tcb/span.hpp>
#include <span>
#include "abstractvaluesattribute.hpp"
#include "to.hpp"
template<typename T, size_t N = tcb::dynamic_extent>
template<typename T, size_t N = std::dynamic_extent>
class SpanAttribute : public AbstractValuesAttribute
{
public:
typedef tcb::span<const T, N> Span;
typedef std::span<const T, N> Span;
protected:
Span m_values;

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2024 Reinder Feenstra
* Copyright (C) 2024-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -27,7 +27,7 @@
constexpr std::array<double, 1> aliasKeys = {{SpeedLimitProperty::noLimitValue}};
inline static const std::array<std::string, 1> aliasValues{{"$speed_limit_property:no_limit$"}};
static tcb::span<const double> getValues(SpeedUnit unit)
static std::span<const double> getValues(SpeedUnit unit)
{
switch(unit)
{
@ -97,7 +97,7 @@ void SpeedLimitProperty::addAttributes()
auto values = getValues(unit());
::Attributes::addMin(*this, values.front());
::Attributes::addValues(*this, values);
::Attributes::addAliases(*this, tcb::span<const double>(aliasKeys), tcb::span<const std::string>(aliasValues));
::Attributes::addAliases(*this, std::span<const double>(aliasKeys), std::span<const std::string>(aliasValues));
m_attributeUnit = m_unit;
}

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021,2023-2024 Reinder Feenstra
* Copyright (C) 2021,2023-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -332,7 +332,7 @@ class VectorProperty : public AbstractVectorProperty
throw conversion_error();
}
void loadObjects(tcb::span<ObjectPtr> /*values*/) final
void loadObjects(std::span<ObjectPtr> /*values*/) final
{
throw conversion_error();
}

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2023 Reinder Feenstra
* Copyright (C) 2019-2023,2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -125,7 +125,7 @@ Decoder::Decoder(World& world, std::string_view _id) :
m_interfaceItems.add(interface);
Attributes::addEnabled(protocol, false);
Attributes::addValues(protocol, tcb::span<const DecoderProtocol>{});
Attributes::addValues(protocol, std::span<const DecoderProtocol>{});
Attributes::addVisible(protocol, false);
m_interfaceItems.add(protocol);
@ -151,7 +151,7 @@ Decoder::Decoder(World& world, std::string_view _id) :
Attributes::addDisplayName(speedSteps, DisplayName::Hardware::speedSteps);
Attributes::addEnabled(speedSteps, false);
Attributes::addValues(speedSteps, tcb::span<const uint8_t>{});
Attributes::addValues(speedSteps, std::span<const uint8_t>{});
Attributes::addVisible(speedSteps, false);
m_interfaceItems.add(speedSteps);
@ -381,7 +381,7 @@ void Decoder::protocolChanged()
checkSpeedSteps();
}
else
Attributes::setValues(speedSteps, tcb::span<const uint8_t>{});
Attributes::setValues(speedSteps, std::span<const uint8_t>{});
}
else
{
@ -395,7 +395,7 @@ bool Decoder::checkProtocol()
{
const auto protocols = protocol.getSpanAttribute<DecoderProtocol>(AttributeName::Values).values();
assert(!protocols.empty());
if(const auto* it = std::find(protocols.begin(), protocols.end(), protocol); it == protocols.end())
if(const auto it = std::find(protocols.begin(), protocols.end(), protocol); it == protocols.end())
{
protocol = protocols.front();
return true;
@ -418,7 +418,7 @@ bool Decoder::checkAddress()
bool Decoder::checkSpeedSteps()
{
const auto values = speedSteps.getSpanAttribute<uint8_t>(AttributeName::Values).values();
if(const auto* it = std::find(values.begin(), values.end(), speedSteps); it == values.end())
if(const auto it = std::find(values.begin(), values.end(), speedSteps); it == values.end())
{
speedSteps = values.back();
return true;

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,2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -65,7 +65,7 @@ std::pair<uint16_t, uint16_t> DecoderController::decoderAddressMinMax(DecoderPro
return noAddressMinMax;
}
tcb::span<const uint8_t> DecoderController::decoderSpeedSteps(DecoderProtocol protocol) const
std::span<const uint8_t> DecoderController::decoderSpeedSteps(DecoderProtocol protocol) const
{
static constexpr std::array<uint8_t, 3> dccSpeedSteps{{14, 28, 128}};
static constexpr std::array<uint8_t, 3> motorolaSpeedSteps{{14, 27, 28}};

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,2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -26,7 +26,7 @@
#include <cstdint>
#include <vector>
#include <memory>
#include <tcb/span.hpp>
#include <span>
#include "../../core/objectproperty.hpp"
#ifdef interface
@ -70,7 +70,7 @@ class DecoderController
//! \brief Get supported protocols
//! \return Supported protocols, may not be empty and must be constant for the instance!
virtual tcb::span<const DecoderProtocol> decoderProtocols() const = 0;
virtual std::span<const DecoderProtocol> decoderProtocols() const = 0;
//! \brief Get address range for given protocol
//! \param[in] protocol The decoder protocol
@ -80,7 +80,7 @@ class DecoderController
//! \brief Get speed step options for given protocol
//! \param[in] protocol The decoder protocol
//! \return Speed step options for the given protocol
virtual tcb::span<const uint8_t> decoderSpeedSteps(DecoderProtocol protocol) const;
virtual std::span<const uint8_t> decoderSpeedSteps(DecoderProtocol protocol) const;
[[nodiscard]] bool addDecoder(Decoder& decoder);
[[nodiscard]] bool removeDecoder(Decoder& decoder);

Datei anzeigen

@ -44,7 +44,7 @@ DecoderFunctions::DecoderFunctions(Object& _parent, std::string_view parentPrope
}
auto function = std::make_shared<DecoderFunction>(decoder, number);
function->name = "F" + std::to_string(number);
function->name = std::to_string(number).insert(0, 1, 'F');
function->number = number;
if(number == 0) // F0 is (almost) always the light function
function->function = DecoderFunctionFunction::Light;

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021-2024 Reinder Feenstra
* Copyright (C) 2021-2025 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,10 +127,10 @@ DCCEXInterface::DCCEXInterface(World& world, std::string_view _id)
updateVisible();
}
tcb::span<const DecoderProtocol> DCCEXInterface::decoderProtocols() const
std::span<const DecoderProtocol> DCCEXInterface::decoderProtocols() const
{
static constexpr std::array<DecoderProtocol, 2> protocols{DecoderProtocol::DCCShort, DecoderProtocol::DCCLong};
return tcb::span<const DecoderProtocol>{protocols.data(), protocols.size()};
return std::span<const DecoderProtocol>{protocols.data(), protocols.size()};
}
std::pair<uint16_t, uint16_t> DCCEXInterface::decoderAddressMinMax(DecoderProtocol protocol) const
@ -140,7 +140,7 @@ std::pair<uint16_t, uint16_t> DCCEXInterface::decoderAddressMinMax(DecoderProtoc
return DecoderController::decoderAddressMinMax(protocol);
}
tcb::span<const uint8_t> DCCEXInterface::decoderSpeedSteps(DecoderProtocol protocol) const
std::span<const uint8_t> DCCEXInterface::decoderSpeedSteps(DecoderProtocol protocol) const
{
(void)protocol; // silence unused warning for release build
assert(protocol == DecoderProtocol::DCCShort || protocol == DecoderProtocol::DCCLong);
@ -169,7 +169,7 @@ void DCCEXInterface::inputSimulateChange(uint32_t channel, uint32_t address, Sim
m_kernel->simulateInputChange(address, action);
}
tcb::span<const OutputChannel> DCCEXInterface::outputChannels() const
std::span<const OutputChannel> DCCEXInterface::outputChannels() const
{
static const auto values = makeArray(OutputChannel::Accessory, OutputChannel::Turnout, OutputChannel::Output, OutputChannel::DCCext);
return values;

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021-2024 Reinder Feenstra
* Copyright (C) 2021-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -80,9 +80,9 @@ class DCCEXInterface final
DCCEXInterface(World& world, std::string_view _id);
// DecoderController:
tcb::span<const DecoderProtocol> decoderProtocols() const final;
std::span<const DecoderProtocol> decoderProtocols() const final;
std::pair<uint16_t, uint16_t> decoderAddressMinMax(DecoderProtocol protocol) const final;
tcb::span<const uint8_t> decoderSpeedSteps(DecoderProtocol protocol) const final;
std::span<const uint8_t> decoderSpeedSteps(DecoderProtocol protocol) const final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
// InputController:
@ -90,7 +90,7 @@ class DCCEXInterface final
void inputSimulateChange(uint32_t channel, uint32_t address, SimulateInputAction action) final;
// OutputController:
tcb::span<const OutputChannel> outputChannels() const final;
std::span<const OutputChannel> outputChannels() const final;
std::pair<uint32_t, uint32_t> outputAddressMinMax(OutputChannel channel) const final;
[[nodiscard]] bool setOutputValue(OutputChannel channel, uint32_t address, OutputValue value) final;
};

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021-2024 Reinder Feenstra
* Copyright (C) 2021-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -74,10 +74,10 @@ ECoSInterface::ECoSInterface(World& world, std::string_view _id)
m_interfaceItems.insertBefore(outputs, notes);
}
tcb::span<const DecoderProtocol> ECoSInterface::decoderProtocols() const
std::span<const DecoderProtocol> ECoSInterface::decoderProtocols() const
{
static constexpr std::array<DecoderProtocol, 4> protocols{DecoderProtocol::DCCShort, DecoderProtocol::DCCLong, DecoderProtocol::Motorola, DecoderProtocol::Selectrix};
return tcb::span<const DecoderProtocol>{protocols.data(), protocols.size()};
return std::span<const DecoderProtocol>{protocols.data(), protocols.size()};
}
void ECoSInterface::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
@ -119,13 +119,13 @@ void ECoSInterface::inputSimulateChange(uint32_t channel, uint32_t address, Simu
m_kernel->simulateInputChange(channel, address, action);
}
tcb::span<const OutputChannel> ECoSInterface::outputChannels() const
std::span<const OutputChannel> ECoSInterface::outputChannels() const
{
static const auto values = makeArray(OutputChannel::AccessoryDCC, OutputChannel::AccessoryMotorola, OutputChannel::ECoSObject);
return values;
}
std::pair<tcb::span<const uint16_t>, tcb::span<const std::string>> ECoSInterface::getOutputECoSObjects(OutputChannel channel) const
std::pair<std::span<const uint16_t>, std::span<const std::string>> ECoSInterface::getOutputECoSObjects(OutputChannel channel) const
{
if(channel == OutputChannel::ECoSObject) /*[[likely]]*/
{

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021-2024 Reinder Feenstra
* Copyright (C) 2021-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -76,7 +76,7 @@ class ECoSInterface final
ECoSInterface(World& world, std::string_view _id);
// DecoderController:
tcb::span<const DecoderProtocol> decoderProtocols() const final;
std::span<const DecoderProtocol> decoderProtocols() const final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
// InputController:
@ -86,8 +86,8 @@ class ECoSInterface final
void inputSimulateChange(uint32_t channel, uint32_t address, SimulateInputAction action) final;
// OutputController:
tcb::span<const OutputChannel> outputChannels() const final;
std::pair<tcb::span<const uint16_t>, tcb::span<const std::string>> getOutputECoSObjects(OutputChannel channel) const final;
std::span<const OutputChannel> outputChannels() const final;
std::pair<std::span<const uint16_t>, std::span<const std::string>> getOutputECoSObjects(OutputChannel channel) const final;
bool isOutputId(OutputChannel channel, uint32_t id) const final;
[[nodiscard]] bool setOutputValue(OutputChannel channel, uint32_t outputId, OutputValue value) final;
};

Datei anzeigen

@ -54,12 +54,6 @@ InterfaceList::InterfaceList(Object& _parent, std::string_view parentPropertyNam
m_interfaceItems.add(delete_);
}
InterfaceList::~InterfaceList()
{
for(auto& it : m_statusPropertyChanged)
it.second.disconnect();
}
TableModelPtr InterfaceList::getModel()
{
return std::make_shared<InterfaceListTableModel>(*this);

Datei anzeigen

@ -30,7 +30,7 @@
class InterfaceList final : public ObjectList<Interface>
{
private:
std::unordered_map<Object*, boost::signals2::connection> m_statusPropertyChanged;
std::unordered_map<Object*, boost::signals2::scoped_connection> m_statusPropertyChanged;
void statusPropertyChanged(BaseProperty& property);
@ -48,7 +48,7 @@ class InterfaceList final : public ObjectList<Interface>
Method<void(const std::shared_ptr<Interface>&)> delete_;
InterfaceList(Object& _parent, std::string_view parentPropertyName);
~InterfaceList() final;
~InterfaceList() final = default;
TableModelPtr getModel() final;
};

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,2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -27,6 +27,7 @@
constexpr uint32_t columnId = 0;
constexpr uint32_t columnName = 1;
constexpr uint32_t columnClassId = 3;
bool InterfaceListTableModel::isListedProperty(std::string_view name)
{
@ -43,6 +44,7 @@ InterfaceListTableModel::InterfaceListTableModel(InterfaceList& list) :
DisplayName::Object::id,
DisplayName::Object::name,
DisplayName::Interface::status,
"class_id"
});
}
@ -61,10 +63,15 @@ std::string InterfaceListTableModel::getText(uint32_t column, uint32_t row) cons
return interface.name;
case columnStatus:
if(const auto* it = EnumValues<InterfaceState>::value.find(interface.status->state); it != EnumValues<InterfaceState>::value.end())
return std::string("$").append(EnumName<InterfaceState>::value).append(":").append(it->second).append("$");
if(const auto* it = EnumValues<InterfaceState>::value.find(interface.status->state); it != EnumValues<InterfaceState>::value.end()) [[likely]]
{
return it->second;
}
break;
case columnClassId:
return std::string{interface.getClassId()};
default:
assert(false);
break;

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021-2024 Reinder Feenstra
* Copyright (C) 2021-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -36,7 +36,7 @@
#include "xpressnetinterface.hpp"
#include "z21interface.hpp"
tcb::span<const std::string_view> Interfaces::classList()
std::span<const std::string_view> Interfaces::classList()
{
static constexpr auto classes = makeArray(
DCCEXInterface::classId,

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021-2024 Reinder Feenstra
* Copyright (C) 2021-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -29,7 +29,7 @@ struct Interfaces
{
static constexpr std::string_view classIdPrefix = "interface.";
static tcb::span<const std::string_view> classList();
static std::span<const std::string_view> classList();
static std::shared_ptr<Interface> create(World& world, std::string_view classId, std::string_view id = std::string_view{});
};

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2024 Reinder Feenstra
* Copyright (C) 2019-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -120,24 +120,24 @@ LocoNetInterface::LocoNetInterface(World& world, std::string_view _id)
typeChanged();
}
bool LocoNetInterface::send(tcb::span<uint8_t> packet)
bool LocoNetInterface::send(std::span<uint8_t> packet)
{
if(m_kernel)
return m_kernel->send(packet);
return false;
}
bool LocoNetInterface::immPacket(tcb::span<uint8_t> dccPacket, uint8_t repeat)
bool LocoNetInterface::immPacket(std::span<uint8_t> dccPacket, uint8_t repeat)
{
if(m_kernel)
return m_kernel->immPacket(dccPacket, repeat);
return false;
}
tcb::span<const DecoderProtocol> LocoNetInterface::decoderProtocols() const
std::span<const DecoderProtocol> LocoNetInterface::decoderProtocols() const
{
static constexpr std::array<DecoderProtocol, 2> protocols{DecoderProtocol::DCCShort, DecoderProtocol::DCCLong};
return tcb::span<const DecoderProtocol>{protocols.data(), protocols.size()};
return std::span<const DecoderProtocol>{protocols.data(), protocols.size()};
}
std::pair<uint16_t, uint16_t> LocoNetInterface::decoderAddressMinMax(DecoderProtocol protocol) const
@ -166,7 +166,7 @@ void LocoNetInterface::inputSimulateChange(uint32_t channel, uint32_t address, S
m_kernel->simulateInputChange(address, action);
}
tcb::span<const OutputChannel> LocoNetInterface::outputChannels() const
std::span<const OutputChannel> LocoNetInterface::outputChannels() const
{
static const auto values = makeArray(OutputChannel::Accessory, OutputChannel::DCCext);
return values;

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2024 Reinder Feenstra
* Copyright (C) 2019-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -33,7 +33,7 @@
#include "../../core/objectproperty.hpp"
#include "../../enum/loconetinterfacetype.hpp"
#include "../../enum/serialflowcontrol.hpp"
#include <tcb/span.hpp>
#include <span>
namespace LocoNet {
class Kernel;
@ -83,16 +83,16 @@ class LocoNetInterface final
//! \brief Send LocoNet packet
//! \param[in] packet LocoNet packet bytes, exluding checksum.
//! \return \c true if send, \c false otherwise.
bool send(tcb::span<uint8_t> packet);
bool send(std::span<uint8_t> packet);
//! \brief Send immediate DCC packet
//! \param[in] dccPacket DCC packet byte, exluding checksum. Length is limited to 5.
//! \param[in] repeat DCC packet repeat count 0..7
//! \return \c true if send to command station, \c false otherwise.
bool immPacket(tcb::span<uint8_t> dccPacket, uint8_t repeat);
bool immPacket(std::span<uint8_t> dccPacket, uint8_t repeat);
// DecoderController:
tcb::span<const DecoderProtocol> decoderProtocols() const final;
std::span<const DecoderProtocol> decoderProtocols() const final;
std::pair<uint16_t, uint16_t> decoderAddressMinMax(DecoderProtocol protocol) const final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
@ -101,7 +101,7 @@ class LocoNetInterface final
void inputSimulateChange(uint32_t channel, uint32_t address, SimulateInputAction action) final;
// OutputController:
tcb::span<const OutputChannel> outputChannels() const final;
std::span<const OutputChannel> outputChannels() const final;
std::pair<uint32_t, uint32_t> outputAddressMinMax(OutputChannel /*channel*/) const final;
[[nodiscard]] bool setOutputValue(OutputChannel channel, uint32_t address, OutputValue value) final;

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2023-2024 Reinder Feenstra
* Copyright (C) 2023-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -118,13 +118,13 @@ MarklinCANInterface::MarklinCANInterface(World& world, std::string_view _id)
typeChanged();
}
tcb::span<const DecoderProtocol> MarklinCANInterface::decoderProtocols() const
std::span<const DecoderProtocol> MarklinCANInterface::decoderProtocols() const
{
static constexpr std::array<DecoderProtocol, 4> protocols{DecoderProtocol::DCCShort, DecoderProtocol::DCCLong, DecoderProtocol::MFX, DecoderProtocol::Motorola};
return tcb::span<const DecoderProtocol>{protocols.data(), protocols.size()};
return std::span<const DecoderProtocol>{protocols.data(), protocols.size()};
}
tcb::span<const uint8_t> MarklinCANInterface::decoderSpeedSteps(DecoderProtocol protocol) const
std::span<const uint8_t> MarklinCANInterface::decoderSpeedSteps(DecoderProtocol protocol) const
{
static constexpr std::array<uint8_t, 2> dccLongSpeedSteps{{28, 128}}; // 14 not supported for long addresses
@ -149,7 +149,7 @@ std::pair<uint32_t, uint32_t> MarklinCANInterface::inputAddressMinMax(uint32_t /
return {MarklinCAN::Kernel::s88AddressMin, MarklinCAN::Kernel::s88AddressMax};
}
tcb::span<const OutputChannel> MarklinCANInterface::outputChannels() const
std::span<const OutputChannel> MarklinCANInterface::outputChannels() const
{
static const auto values = makeArray(OutputChannel::AccessoryMotorola, OutputChannel::AccessoryDCC);
return values;

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2023-2024 Reinder Feenstra
* Copyright (C) 2023-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -78,15 +78,15 @@ class MarklinCANInterface final
MarklinCANInterface(World& world, std::string_view _id);
// DecoderController:
tcb::span<const DecoderProtocol> decoderProtocols() const final;
tcb::span<const uint8_t> decoderSpeedSteps(DecoderProtocol protocol) const final;
std::span<const DecoderProtocol> decoderProtocols() const final;
std::span<const uint8_t> decoderSpeedSteps(DecoderProtocol protocol) const final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
// InputController:
std::pair<uint32_t, uint32_t> inputAddressMinMax(uint32_t /*channel*/) const final;
// OutputController:
tcb::span<const OutputChannel> outputChannels() const final;
std::span<const OutputChannel> outputChannels() const final;
[[nodiscard]] bool setOutputValue(OutputChannel channel, uint32_t address, OutputValue value) final;
};

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2022-2024 Reinder Feenstra
* Copyright (C) 2022-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -113,7 +113,7 @@ void TraintasticDIYInterface::inputSimulateChange(uint32_t channel, uint32_t add
m_kernel->simulateInputChange(address, action);
}
tcb::span<const OutputChannel> TraintasticDIYInterface::outputChannels() const
std::span<const OutputChannel> TraintasticDIYInterface::outputChannels() const
{
static const auto values = makeArray(OutputChannel::Output);
return values;

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2022-2024 Reinder Feenstra
* Copyright (C) 2022-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -77,7 +77,7 @@ class TraintasticDIYInterface final
void inputSimulateChange(uint32_t channel, uint32_t address, SimulateInputAction action) final;
// OutputController:
tcb::span<const OutputChannel> outputChannels() const final;
std::span<const OutputChannel> outputChannels() const final;
std::pair<uint32_t, uint32_t> outputAddressMinMax(OutputChannel /*channel*/) const final;
[[nodiscard]] bool setOutputValue(OutputChannel channel, uint32_t address, OutputValue value) final;
};

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2024 Reinder Feenstra
* Copyright (C) 2019-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -158,10 +158,10 @@ XpressNetInterface::XpressNetInterface(World& world, std::string_view _id)
updateVisible();
}
tcb::span<const DecoderProtocol> XpressNetInterface::decoderProtocols() const
std::span<const DecoderProtocol> XpressNetInterface::decoderProtocols() const
{
static constexpr std::array<DecoderProtocol, 2> protocols{DecoderProtocol::DCCShort, DecoderProtocol::DCCLong};
return tcb::span<const DecoderProtocol>{protocols.data(), protocols.size()};
return std::span<const DecoderProtocol>{protocols.data(), protocols.size()};
}
std::pair<uint16_t, uint16_t> XpressNetInterface::decoderAddressMinMax(DecoderProtocol protocol) const
@ -179,7 +179,7 @@ std::pair<uint16_t, uint16_t> XpressNetInterface::decoderAddressMinMax(DecoderPr
}
}
tcb::span<const uint8_t> XpressNetInterface::decoderSpeedSteps(DecoderProtocol protocol) const
std::span<const uint8_t> XpressNetInterface::decoderSpeedSteps(DecoderProtocol protocol) const
{
static constexpr std::array<uint8_t, 4> dccSpeedSteps{{14, 27, 28, 128}}; // XpressNet also support 27 steps
@ -211,7 +211,7 @@ void XpressNetInterface::inputSimulateChange(uint32_t channel, uint32_t address,
m_kernel->simulateInputChange(address, action);
}
tcb::span<const OutputChannel> XpressNetInterface::outputChannels() const
std::span<const OutputChannel> XpressNetInterface::outputChannels() const
{
static const auto values = makeArray(OutputChannel::Accessory);
return values;
@ -290,27 +290,41 @@ bool XpressNetInterface::setOnline(bool& value, bool simulation)
setState(InterfaceState::Error);
online = false; // communication no longer possible
});
m_kernel->setOnNormalOperationResumed(
[this]()
m_kernel->setOnTrackPowerChanged(
[this](bool powerOn, bool isStopped)
{
if(!contains(m_world.state.value(), WorldState::PowerOn))
m_world.powerOn();
if(!contains(m_world.state.value(), WorldState::Run))
m_world.run();
});
m_kernel->setOnTrackPowerOff(
[this]()
{
if(contains(m_world.state.value(), WorldState::PowerOn))
m_world.powerOff();
if(contains(m_world.state.value(), WorldState::Run))
m_world.stop();
});
m_kernel->setOnEmergencyStop(
[this]()
{
if(contains(m_world.state.value(), WorldState::Run))
m_world.stop();
if(powerOn)
{
/* NOTE:
* Setting stop and powerOn together is not an atomic operation,
* so it would trigger 2 state changes with in the middle state.
* Fortunately this does not happen because at least one of the state is already set.
* Because if we are in Run state we go to PowerOn,
* and if we are on PowerOff then we go to PowerOn.
*/
// First of all, stop if we have to, otherwhise we might inappropiately run trains
if(isStopped && contains(m_world.state.value(), WorldState::Run))
{
m_world.stop();
}
else if(!contains(m_world.state.value(), WorldState::Run) && !isStopped)
{
m_world.run(); // Run trains yay!
}
// EmergencyStop in XpressNet also means power is still on
if(!contains(m_world.state.value(), WorldState::PowerOn) && isStopped)
{
m_world.powerOn(); // Just power on but keep stopped
}
}
else
{
// Power off regardless of stop state
if(contains(m_world.state.value(), WorldState::PowerOn))
m_world.powerOff();
}
});
m_kernel->setDecoderController(this);
@ -324,12 +338,13 @@ bool XpressNetInterface::setOnline(bool& value, bool simulation)
m_kernel->setConfig(xpressnet->config());
});
// Avoid to set multiple power states in rapid succession
if(!contains(m_world.state.value(), WorldState::PowerOn))
m_kernel->stopOperations();
m_kernel->stopOperations(); // Stop by powering off
else if(!contains(m_world.state.value(), WorldState::Run))
m_kernel->stopAllLocomotives();
m_kernel->stopAllLocomotives(); // Emergency stop with power on
else
m_kernel->resumeOperations();
m_kernel->resumeOperations(); // Run trains
Attributes::setEnabled({type, serialInterfaceType, device, baudrate, flowControl, hostname, port, s88StartAddress, s88ModuleCount}, false);
}
@ -386,24 +401,38 @@ void XpressNetInterface::worldEvent(WorldState state, WorldEvent event)
switch(event)
{
case WorldEvent::PowerOff:
{
m_kernel->stopOperations();
break;
}
case WorldEvent::PowerOn:
m_kernel->resumeOperations();
if(!contains(state, WorldState::Run))
m_kernel->stopAllLocomotives();
{
if(contains(state, WorldState::Run))
m_kernel->resumeOperations();
else
m_kernel->stopAllLocomotives(); // In XpressNet E-Stop means power on but not running
break;
}
case WorldEvent::Stop:
m_kernel->stopAllLocomotives();
{
if(contains(state, WorldState::PowerOn))
{
// In XpressNet E-Stop means power is on but trains are not running
m_kernel->stopAllLocomotives();
}
else
{
// This Stops everything by removing power
m_kernel->stopOperations();
}
break;
}
case WorldEvent::Run:
{
if(contains(state, WorldState::PowerOn))
m_kernel->resumeOperations();
break;
}
default:
break;
}

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2024 Reinder Feenstra
* Copyright (C) 2019-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -80,9 +80,9 @@ class XpressNetInterface final
XpressNetInterface(World& world, std::string_view _id);
// DecoderController:
tcb::span<const DecoderProtocol> decoderProtocols() const final;
std::span<const DecoderProtocol> decoderProtocols() const final;
std::pair<uint16_t, uint16_t> decoderAddressMinMax(DecoderProtocol protocol) const final;
tcb::span<const uint8_t> decoderSpeedSteps(DecoderProtocol protocol) const final;
std::span<const uint8_t> decoderSpeedSteps(DecoderProtocol protocol) const final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
// InputController:
@ -90,7 +90,7 @@ class XpressNetInterface final
void inputSimulateChange(uint32_t channel, uint32_t address, SimulateInputAction action) final;
// OutputController:
tcb::span<const OutputChannel> outputChannels() const final;
std::span<const OutputChannel> outputChannels() const final;
std::pair<uint32_t, uint32_t> outputAddressMinMax(OutputChannel /*channel*/) const final;
[[nodiscard]] bool setOutputValue(OutputChannel channel, uint32_t address, OutputValue value) final;
};

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2024 Reinder Feenstra
* Copyright (C) 2019-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -90,10 +90,10 @@ Z21Interface::Z21Interface(World& world, std::string_view _id)
m_interfaceItems.insertBefore(firmwareVersion, notes);
}
tcb::span<const DecoderProtocol> Z21Interface::decoderProtocols() const
std::span<const DecoderProtocol> Z21Interface::decoderProtocols() const
{
static constexpr std::array<DecoderProtocol, 3> protocols{DecoderProtocol::DCCShort, DecoderProtocol::DCCLong, DecoderProtocol::Motorola};
return tcb::span<const DecoderProtocol>{protocols.data(), protocols.size()};
return std::span<const DecoderProtocol>{protocols.data(), protocols.size()};
}
std::pair<uint16_t, uint16_t> Z21Interface::decoderAddressMinMax(DecoderProtocol protocol) const
@ -144,7 +144,7 @@ void Z21Interface::inputSimulateChange(uint32_t channel, uint32_t address, Simul
m_kernel->simulateInputChange(channel, address, action);
}
tcb::span<const OutputChannel> Z21Interface::outputChannels() const
std::span<const OutputChannel> Z21Interface::outputChannels() const
{
static const auto values = makeArray(OutputChannel::Accessory, OutputChannel::DCCext);
return values;
@ -215,35 +215,35 @@ bool Z21Interface::setOnline(bool& value, bool simulation)
{
if(powerOn)
{
/* NOTE:
* Setting stop and powerOn together is not an atomic operation,
* so it would trigger 2 state changes with in the middle state.
* Fortunately this does not happen because at least one of the state is already set.
* Because if we are in Run state we go to PowerOn,
* and if we are on PowerOff then we go to PowerOn.
*/
/* NOTE:
* Setting stop and powerOn together is not an atomic operation,
* so it would trigger 2 state changes with in the middle state.
* Fortunately this does not happen because at least one of the state is already set.
* Because if we are in Run state we go to PowerOn,
* and if we are on PowerOff then we go to PowerOn.
*/
// First of all, stop if we have to, otherwhise we might inappropiately run trains
if(isStopped && contains(m_world.state.value(), WorldState::Run))
{
m_world.stop();
}
else if(!contains(m_world.state.value(), WorldState::Run) && !isStopped)
{
m_world.run(); // Run trains yay!
}
// First of all, stop if we have to, otherwhise we might inappropiately run trains
if(isStopped && contains(m_world.state.value(), WorldState::Run))
{
m_world.stop();
}
else if(!contains(m_world.state.value(), WorldState::Run) && !isStopped)
{
m_world.run(); // Run trains yay!
}
// EmergencyStop in Z21 also means power is still on
if(!contains(m_world.state.value(), WorldState::PowerOn) && isStopped)
{
m_world.powerOn(); // Just power on but keep stopped
}
// EmergencyStop in Z21 also means power is still on
if(!contains(m_world.state.value(), WorldState::PowerOn) && isStopped)
{
m_world.powerOn(); // Just power on but keep stopped
}
}
else
{
// Power off regardless of stop state
if(contains(m_world.state.value(), WorldState::PowerOn))
m_world.powerOff();
// Power off regardless of stop state
if(contains(m_world.state.value(), WorldState::PowerOn))
m_world.powerOff();
}
});

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2024 Reinder Feenstra
* Copyright (C) 2019-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -71,7 +71,7 @@ class Z21Interface final
Z21Interface(World& world, std::string_view _id);
// DecoderController:
tcb::span<const DecoderProtocol> decoderProtocols() const final;
std::span<const DecoderProtocol> decoderProtocols() const final;
std::pair<uint16_t, uint16_t> decoderAddressMinMax(DecoderProtocol protocol) const final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
@ -82,7 +82,7 @@ class Z21Interface final
void inputSimulateChange(uint32_t channel, uint32_t address, SimulateInputAction action) final;
// OutputController:
tcb::span<const OutputChannel> outputChannels() const final;
std::span<const OutputChannel> outputChannels() const final;
std::pair<uint32_t, uint32_t> outputAddressMinMax(OutputChannel /*channel*/) const final;
[[nodiscard]] bool setOutputValue(OutputChannel channel, uint32_t address, OutputValue value) final;
};

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021,2023-2024 Reinder Feenstra
* Copyright (C) 2021,2023-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -85,7 +85,7 @@ OutputMap::OutputMap(Object& _parent, std::string_view parentPropertyName)
{
// New interface doesn't support channel or channel has different output type.
const auto channels = newValue->outputChannels();
const auto* it = std::find_if(channels.begin(), channels.end(),
const auto it = std::find_if(channels.begin(), channels.end(),
[&controller=*newValue, outputType=interface->outputType(channel)](OutputChannel outputChannel)
{
return controller.outputType(outputChannel) == outputType;
@ -331,7 +331,7 @@ OutputMap::OutputMap(Object& _parent, std::string_view parentPropertyName)
Attributes::addDisplayName(channel, DisplayName::Hardware::channel);
Attributes::addEnabled(channel, editable);
Attributes::addValues(channel, tcb::span<const OutputChannel>());
Attributes::addValues(channel, std::span<const OutputChannel>());
Attributes::addVisible(channel, false);
m_interfaceItems.add(channel);
@ -341,10 +341,10 @@ OutputMap::OutputMap(Object& _parent, std::string_view parentPropertyName)
Attributes::addMinMax<uint32_t>(addresses, 0, 0);
m_interfaceItems.add(addresses);
Attributes::addAliases(ecosObject, tcb::span<const uint16_t>{}, tcb::span<const std::string>{});
Attributes::addAliases(ecosObject, std::span<const uint16_t>{}, std::span<const std::string>{});
Attributes::addDisplayName(ecosObject, "output_map:ecos_object");
Attributes::addEnabled(ecosObject, editable);
Attributes::addValues(ecosObject, tcb::span<const uint16_t>{});
Attributes::addValues(ecosObject, std::span<const uint16_t>{});
Attributes::addVisible(ecosObject, false);
m_interfaceItems.add(ecosObject);
@ -367,11 +367,7 @@ OutputMap::OutputMap(Object& _parent, std::string_view parentPropertyName)
updateEnabled();
}
OutputMap::~OutputMap()
{
m_interfaceDestroying.disconnect();
m_outputECoSObjectsChanged.disconnect();
}
OutputMap::~OutputMap() = default;
void OutputMap::load(WorldLoader& loader, const nlohmann::json& data)
{
@ -423,13 +419,13 @@ void OutputMap::worldEvent(WorldState state, WorldEvent event)
void OutputMap::interfaceChanged()
{
const auto outputChannels = interface ? interface->outputChannels() : tcb::span<const OutputChannel>{};
const auto outputChannels = interface ? interface->outputChannels() : std::span<const OutputChannel>{};
Attributes::setValues(channel, outputChannels);
Attributes::setVisible(channel, interface);
m_outputECoSObjectsChanged.disconnect();
if(std::find(outputChannels.begin(), outputChannels.end(), OutputChannel::ECoSObject))
if(std::find(outputChannels.begin(), outputChannels.end(), OutputChannel::ECoSObject) != outputChannels.end())
{
m_outputECoSObjectsChanged = interface->outputECoSObjectsChanged.connect(
[this]()
@ -458,8 +454,8 @@ void OutputMap::channelChanged()
{
Attributes::setVisible({addresses, addAddress, removeAddress}, true);
Attributes::setVisible(ecosObject, false);
Attributes::setAliases(ecosObject, tcb::span<const uint16_t>{}, tcb::span<const std::string>{});
Attributes::setValues(ecosObject, tcb::span<const uint16_t>{});
Attributes::setAliases(ecosObject, std::span<const uint16_t>{}, std::span<const std::string>{});
Attributes::setValues(ecosObject, std::span<const uint16_t>{});
if(addresses.empty())
{
@ -507,8 +503,8 @@ void OutputMap::channelChanged()
{
Attributes::setVisible({addresses, addAddress, removeAddress, ecosObject}, false);
Attributes::setMinMax(addresses, std::numeric_limits<uint32_t>::min(), std::numeric_limits<uint32_t>::max());
Attributes::setAliases(ecosObject, tcb::span<const uint16_t>{}, tcb::span<const std::string>{});
Attributes::setValues(ecosObject, tcb::span<const uint16_t>{});
Attributes::setAliases(ecosObject, std::span<const uint16_t>{}, std::span<const std::string>{});
Attributes::setValues(ecosObject, std::span<const uint16_t>{});
}
}

Datei anzeigen

@ -50,8 +50,8 @@ class OutputMap : public SubObject
static constexpr size_t addressesSizeMin = 1;
static constexpr size_t addressesSizeMax = 8;
boost::signals2::connection m_interfaceDestroying;
boost::signals2::connection m_outputECoSObjectsChanged;
boost::signals2::scoped_connection m_interfaceDestroying;
boost::signals2::scoped_connection m_outputECoSObjectsChanged;
void addOutput(OutputChannel ch, uint32_t id);
void addOutput(OutputChannel ch, uint32_t id, OutputController& outputController);

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2024 Reinder Feenstra
* Copyright (C) 2024-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -29,5 +29,5 @@ static const std::array<std::string, 2> keyAliasValues{{"$output_map_item.switch
SwitchOutputMapItem::SwitchOutputMapItem(Object& map, bool value) :
OutputMapItemBase(map, value)
{
Attributes::addAliases(key, tcb::span<const bool>(keyAliasKeys), tcb::span<const std::string>(keyAliasValues));
Attributes::addAliases(key, std::span<const bool>(keyAliasKeys), std::span<const std::string>(keyAliasValues));
}

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021-2022,2024 Reinder Feenstra
* Copyright (C) 2021-2022,2024-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -93,7 +93,7 @@ std::pair<uint32_t, uint32_t> OutputController::outputAddressMinMax(OutputChanne
return {0, 0};
}
std::pair<tcb::span<const uint16_t>, tcb::span<const std::string>> OutputController::getOutputECoSObjects(OutputChannel /*channel*/) const
std::pair<std::span<const uint16_t>, std::span<const std::string>> OutputController::getOutputECoSObjects(OutputChannel /*channel*/) const
{
assert(false);
return {{}, {}};

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021-2022,2024 Reinder Feenstra
* Copyright (C) 2021-2022,2024-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -28,7 +28,7 @@
#include <unordered_map>
#include <memory>
#include <boost/signals2/signal.hpp>
#include <tcb/span.hpp>
#include <span>
#include <traintastic/enum/outputchannel.hpp>
#include <traintastic/enum/outputtype.hpp>
#include "outputvalue.hpp"
@ -97,7 +97,7 @@ class OutputController
/**
*
*/
virtual tcb::span<const OutputChannel> outputChannels() const = 0;
virtual std::span<const OutputChannel> outputChannels() const = 0;
/**
*
@ -122,7 +122,7 @@ class OutputController
/**
*
*/
virtual std::pair<tcb::span<const uint16_t>, tcb::span<const std::string>> getOutputECoSObjects(OutputChannel channel) const;
virtual std::pair<std::span<const uint16_t>, std::span<const std::string>> getOutputECoSObjects(OutputChannel channel) const;
/**
*

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021-2022,2024 Reinder Feenstra
* Copyright (C) 2021-2022,2024-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -24,7 +24,7 @@
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_DCCEX_MESSAGES_HPP
#include <string_view>
#include <tcb/span.hpp>
#include <span>
#include <traintastic/enum/direction.hpp>
#include "../../../utils/tohex.hpp"
@ -307,7 +307,7 @@ namespace Messages {
return "";
}
inline std::string dccPacket(tcb::span<const uint8_t> packet, Track track = Track::Main)
inline std::string dccPacket(std::span<const uint8_t> packet, Track track = Track::Main)
{
assert(packet.size() <= 5);
assert(track == Track::Main || track == Track::Programming);
@ -321,7 +321,7 @@ namespace Messages {
template<class T, std::enable_if_t<std::is_trivially_copyable_v<T> && sizeof(T) <= 5, bool> = true>
inline std::string dccPacket(const T& packet, Track track = Track::Main)
{
return dccPacket(tcb::span<const uint8_t>(reinterpret_cast<const uint8_t*>(&packet), sizeof(T)), track);
return dccPacket(std::span<const uint8_t>(reinterpret_cast<const uint8_t*>(&packet), sizeof(T)), track);
}
}

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2024 Reinder Feenstra
* Copyright (C) 2019-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -940,7 +940,7 @@ void Kernel::resume()
});
}
bool Kernel::send(tcb::span<uint8_t> packet)
bool Kernel::send(std::span<uint8_t> packet)
{
assert(isEventLoopThread());
@ -962,7 +962,7 @@ bool Kernel::send(tcb::span<uint8_t> packet)
return true;
}
bool Kernel::immPacket(tcb::span<const uint8_t> dccPacket, uint8_t repeat)
bool Kernel::immPacket(std::span<const uint8_t> dccPacket, uint8_t repeat)
{
assert(isEventLoopThread());

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2024 Reinder Feenstra
* Copyright (C) 2019-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -29,7 +29,7 @@
#include <filesystem>
#include <boost/asio/steady_timer.hpp>
#include <boost/signals2/connection.hpp>
#include <tcb/span.hpp>
#include <span>
#include <traintastic/enum/direction.hpp>
#include <traintastic/enum/tristate.hpp>
#include <traintastic/enum/outputchannel.hpp>
@ -416,18 +416,18 @@ class Kernel : public ::KernelBase
//! \brief Send LocoNet packet
//! \param[in] packet LocoNet packet bytes, exluding checksum.
//! \return \c true if send, \c false otherwise.
bool send(tcb::span<uint8_t> packet);
bool send(std::span<uint8_t> packet);
//! \brief Send immediate DCC packet
//! \param[in] dccPacket DCC packet byte, exluding checksum. Length is limited to 5.
//! \param[in] repeat DCC packet repeat count 0..7
//! \return \c true if send to command station, \c false otherwise.
bool immPacket(tcb::span<const uint8_t> dccPacket, uint8_t repeat);
bool immPacket(std::span<const uint8_t> dccPacket, uint8_t repeat);
template<class T, std::enable_if_t<std::is_trivially_copyable_v<T> && sizeof(T) <= 5, bool> = true>
bool immPacket(const T& dccPacket, uint8_t repeat)
{
return immPacket(tcb::span<const uint8_t>(reinterpret_cast<const uint8_t*>(&dccPacket), sizeof(T)), repeat);
return immPacket(std::span<const uint8_t>(reinterpret_cast<const uint8_t*>(&dccPacket), sizeof(T)), repeat);
}
/**

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2023-2024 Reinder Feenstra
* Copyright (C) 2023-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -24,7 +24,7 @@
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_LOCONET_MESSAGE_IMMPACKET_HPP
#include "message.hpp"
#include <tcb/span.hpp>
#include <span>
#include "../checksum.hpp"
#include "../signature.hpp"
@ -56,7 +56,7 @@ struct ImmPacket : Message
uint8_t im[5];
uint8_t checksum;
ImmPacket(tcb::span<const uint8_t> dccPacket, uint8_t repeat)
ImmPacket(std::span<const uint8_t> dccPacket, uint8_t repeat)
: Message(OPC_IMM_PACKET)
, len{11}
, header{0x7F}

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2022 Reinder Feenstra
* Copyright (C) 2022,2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -25,7 +25,7 @@
#include <string>
#include <string_view>
#include <tcb/span.hpp>
#include <span>
#include <version.hpp>
#include <traintastic/enum/direction.hpp>
@ -66,7 +66,7 @@ constexpr std::string_view protocolVersion()
return "VN2.0";
}
inline std::string rosterList(tcb::span<const RosterListEntry> list)
inline std::string rosterList(std::span<const RosterListEntry> list)
{
std::string s{"RL"};
s.append(std::to_string(list.size()));

Datei anzeigen

@ -181,84 +181,99 @@ void Kernel::receive(const Message& message)
break;
}
case 0x60:
if(message == NormalOperationResumed())
{
if(message == TrackPowerOff())
{
if(m_trackPowerOn != TriState::True || m_emergencyStop != TriState::False)
{
m_trackPowerOn = TriState::True;
m_emergencyStop = TriState::False;
if(m_onNormalOperationResumed)
EventLoop::call(
[this]()
{
m_onNormalOperationResumed();
});
}
EventLoop::call(
[this]()
{
if(m_trackPowerOn != TriState::False)
{
m_trackPowerOn = TriState::False;
m_emergencyStop = TriState::False;
if(m_onTrackPowerChanged)
m_onTrackPowerChanged(false, false);
}
});
}
else if(message == TrackPowerOff())
else if(message == NormalOperationResumed())
{
if(m_trackPowerOn != TriState::False)
{
m_trackPowerOn = TriState::False;
if(m_onTrackPowerOff)
EventLoop::call(
[this]()
{
m_onTrackPowerOff();
});
EventLoop::call(
[this]()
{
if(m_trackPowerOn != TriState::True || m_emergencyStop != TriState::False)
{
m_trackPowerOn = TriState::True;
m_emergencyStop = TriState::False;
if(m_onTrackPowerChanged)
m_onTrackPowerChanged(true, false);
}
});
}
}
break;
}
case 0x80:
{
if(message == EmergencyStop())
{
if(m_emergencyStop != TriState::True)
{
m_emergencyStop = TriState::True;
EventLoop::call(
[this]()
{
if(m_emergencyStop != TriState::True)
{
m_emergencyStop = TriState::True;
m_trackPowerOn = TriState::True;
if(m_onEmergencyStop)
EventLoop::call(
[this]()
{
m_onEmergencyStop();
});
}
if(m_onTrackPowerChanged)
m_onTrackPowerChanged(true, true);
}
});
}
break;
}
}
}
void Kernel::resumeOperations()
{
m_ioContext.post(
[this]()
{
if(m_trackPowerOn != TriState::True || m_emergencyStop != TriState::False)
assert(isEventLoopThread());
if(m_trackPowerOn != TriState::True || m_emergencyStop != TriState::False)
{
m_ioContext.post(
[this]()
{
send(ResumeOperationsRequest());
});
});
}
}
void Kernel::stopOperations()
{
m_ioContext.post(
[this]()
{
if(m_trackPowerOn != TriState::False)
assert(isEventLoopThread());
if(m_trackPowerOn != TriState::False || m_emergencyStop != TriState::False)
{
m_ioContext.post(
[this]()
{
send(StopOperationsRequest());
});
});
}
}
void Kernel::stopAllLocomotives()
{
m_ioContext.post(
[this]()
{
if(m_emergencyStop != TriState::True)
assert(isEventLoopThread());
if(m_trackPowerOn != TriState::True || m_emergencyStop != TriState::True)
{
m_ioContext.post(
[this]()
{
send(StopAllLocomotivesRequest());
});
});
}
}
void Kernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
@ -388,7 +403,7 @@ bool Kernel::setOutput(uint16_t address, OutputPairValue value)
{
assert(isEventLoopThread());
assert(address >= accessoryOutputAddressMin && address <= accessoryOutputAddressMax);
assert(value == OutputPairValue::First || value == OutputPairValue::First);
assert(value == OutputPairValue::First || value == OutputPairValue::Second);
m_ioContext.post(
[this, address, value]()
{

Datei anzeigen

@ -54,11 +54,37 @@ class Kernel : public ::KernelBase
std::unique_ptr<IOHandler> m_ioHandler;
const bool m_simulation;
TriState m_trackPowerOn;
TriState m_emergencyStop;
std::function<void()> m_onNormalOperationResumed;
std::function<void()> m_onTrackPowerOff;
std::function<void()> m_onEmergencyStop;
/*!
* \brief m_trackPowerOn caches command station track power state.
*
* \note It must be accessed only from event loop thread or from
* XpressNet::Kernel::start().
*
* \sa EventLoop
*/
TriState m_trackPowerOn = TriState::Undefined;
/*!
* \brief m_emergencyStop caches command station emergency stop state.
*
* \note It must be accessed only from event loop thread or from
* XpressNet::Kernel::start().
*
* \sa EventLoop
*/
TriState m_emergencyStop = TriState::Undefined;
/*!
* \brief m_onTrackPowerChanged callback is called when XpressNet power state changes.
*
* \note It is always called from event loop thread
* \note First argument is powerOn, second argument is isStopped
* In XpressNet EmergencyStop is really PowerOn + EmergencyStop and
* PowerOn implicitly means Run so we cannot call \sa trackPowerOn() if world must be stopped
*
* \sa EventLoop
*/
std::function<void(bool, bool)> m_onTrackPowerChanged;
DecoderController* m_decoderController;
@ -135,38 +161,13 @@ class Kernel : public ::KernelBase
/**
* @brief ...
*
* @param[in] callback ...
* @note This function may not be called when the kernel is running.
*/
inline void setOnNormalOperationResumed(std::function<void()> callback)
inline void setOnTrackPowerChanged(std::function<void(bool, bool)> callback)
{
assert(!m_started);
m_onNormalOperationResumed = std::move(callback);
}
/**
* @brief ...
*
* @param[in] callback ...
* @note This function may not be called when the kernel is running.
*/
inline void setOnTrackPowerOff(std::function<void()> callback)
{
assert(!m_started);
m_onTrackPowerOff = std::move(callback);
}
/**
* @brief ...
*
* @param[in] callback ...
* @note This function may not be called when the kernel is running.
*/
inline void setOnEmergencyStop(std::function<void()> callback)
{
assert(!m_started);
m_onEmergencyStop = std::move(callback);
m_onTrackPowerChanged = std::move(callback);
}
/**

Datei anzeigen

@ -44,13 +44,61 @@ bool isChecksumValid(const Message& msg, const int dataSize)
return calcChecksum(msg, dataSize) == *(reinterpret_cast<const uint8_t*>(&msg) + dataSize + 1);
}
std::string toString(const Message& message)
std::string toString(const Message& message, bool raw)
{
std::string s;
std::string s = toHex(message.identification());
// Human readable:
switch(message.header)
{
case 0x21:
{
if(message == ResumeOperationsRequest())
{
s = "RESUME_OPERATIONS_REQUEST";
}
else if(message == StopOperationsRequest())
{
s = "STOP_OPERATIONS_REQUEST";
}
else
raw = true;
break;
}
case 0x61:
{
if(message == NormalOperationResumed())
{
s = "NORMAL_OPERATIONS_RESUMED";
}
else if(message == TrackPowerOff())
{
s = "TRACK_POWER_OFF";
}
else
raw = true;
break;
}
case 0x80:
{
if(message == StopAllLocomotivesRequest())
{
s = "STOP_ALL_LOCO_REQUEST";
}
else
raw = true;
break;
}
case 0x81:
{
if(message == EmergencyStop())
{
s = "EMERGENCY_STOP";
}
else
raw = true;
break;
}
case 0x52:
{
const auto& req = static_cast<const AccessoryDecoderOperationRequest&>(message);
@ -60,13 +108,21 @@ std::string toString(const Message& message)
s.append(req.activate() ? " activate" : " deactivate");
break;
}
default:
{
raw = true;
break;
}
// FIXME: add all messages
}
// Raw data:
s.append(" [");
s.append(toHex(reinterpret_cast<const uint8_t*>(&message), message.size(), true));
s.append("]");
if(raw)
{
// Raw data:
s.append(" [");
s.append(toHex(reinterpret_cast<const uint8_t*>(&message), message.size(), true));
s.append("]");
}
return s;
}

Datei anzeigen

@ -27,7 +27,10 @@
#include <cassert>
#include <cstring>
#include <string>
#include "../../../enum/direction.hpp"
#include <traintastic/enum/direction.hpp>
#include "../../../utils/packed.hpp"
#include "../../../utils/endian.hpp"
#include "../../../utils/byte.hpp"
namespace XpressNet {
@ -37,6 +40,7 @@ constexpr uint16_t longAddressMin = 100;
constexpr uint16_t longAddressMax = 9999;
constexpr uint8_t idFeedbackBroadcast = 0x40;
constexpr uint8_t idLocomotiveBusy = 0xE4;
struct Message;
@ -48,8 +52,10 @@ void updateChecksum(Message& msg);
inline bool isChecksumValid(const Message& msg);
bool isChecksumValid(const Message& msg, const int dataSize);
std::string toString(const Message& message);
std::string toString(const Message& message, bool raw = true);
// Chapters are based on:
// Lenz Dokumentation XpressNet Version 4.0 02/2022
struct Message
{
uint8_t header;
@ -77,9 +83,10 @@ struct Message
{
return 2 + dataSize();
}
};
} ATTRIBUTE_PACKED;
static_assert(sizeof(Message) == 1);
// 2.4.1
struct NormalOperationResumed : Message
{
uint8_t db1 = 0x01;
@ -89,9 +96,10 @@ struct NormalOperationResumed : Message
{
header = 0x61;
}
};
} ATTRIBUTE_PACKED;
static_assert(sizeof(NormalOperationResumed) == 3);
// 2.4.2
struct TrackPowerOff : Message
{
uint8_t db1 = 0x00;
@ -101,9 +109,10 @@ struct TrackPowerOff : Message
{
header = 0x61;
}
};
} ATTRIBUTE_PACKED;
static_assert(sizeof(TrackPowerOff) == 3);
// 2.4.3
struct EmergencyStop : Message
{
uint8_t db1 = 0x00;
@ -113,9 +122,35 @@ struct EmergencyStop : Message
{
header = 0x81;
}
};
} ATTRIBUTE_PACKED;
static_assert(sizeof(EmergencyStop) == 3);
// 2.13
struct CommandStationBusy : Message
{
uint8_t db1 = 0x81;
uint8_t checksum = 0xE0;
CommandStationBusy()
{
header = 0x61;
}
} ATTRIBUTE_PACKED;
static_assert(sizeof(CommandStationBusy) == 3);
// 2.14
struct CommandUnknown : Message
{
uint8_t db1 = 0x82;
uint8_t checksum = 0xE3;
CommandUnknown()
{
header = 0x61;
}
} ATTRIBUTE_PACKED;
static_assert(sizeof(CommandUnknown) == 3);
struct FeedbackBroadcast : Message
{
struct Pair
@ -180,7 +215,7 @@ struct FeedbackBroadcast : Message
else
data &= ~static_cast<uint8_t>(1 << index);
}
};
} ATTRIBUTE_PACKED;
static_assert(sizeof(Pair) == 2);
constexpr uint8_t pairCount() const
@ -205,8 +240,9 @@ struct FeedbackBroadcast : Message
assert(index < pairCount());
return *(reinterpret_cast<Pair*>(&header + sizeof(header)) + index);
}
};
} ATTRIBUTE_PACKED;
// 3.2
struct ResumeOperationsRequest : Message
{
uint8_t db1 = 0x81;
@ -216,9 +252,10 @@ struct ResumeOperationsRequest : Message
{
header = 0x21;
}
};
} ATTRIBUTE_PACKED;
static_assert(sizeof(ResumeOperationsRequest) == 3);
// 3.3
struct StopOperationsRequest : Message
{
uint8_t db1 = 0x80;
@ -228,9 +265,10 @@ struct StopOperationsRequest : Message
{
header = 0x21;
}
};
} ATTRIBUTE_PACKED;
static_assert(sizeof(StopOperationsRequest) == 3);
// 3.4
struct StopAllLocomotivesRequest : Message
{
uint8_t checksum = 0x80;
@ -239,9 +277,10 @@ struct StopAllLocomotivesRequest : Message
{
header = 0x80;
}
};
} ATTRIBUTE_PACKED;
static_assert(sizeof(StopAllLocomotivesRequest) == 2);
// 3.7 Emergency stop locomotive (from Central version 3.0)
struct EmergencyStopLocomotive : Message
{
uint8_t addressHigh;
@ -264,7 +303,7 @@ struct EmergencyStopLocomotive : Message
addressLow = address & 0x7f;
}
}
};
} ATTRIBUTE_PACKED;
static_assert(sizeof(EmergencyStopLocomotive) == 4);
struct LocomotiveInstruction : Message
@ -279,17 +318,27 @@ struct LocomotiveInstruction : Message
if(address >= longAddressMin)
{
assert(address >= longAddressMin && address <= longAddressMax);
addressHigh = 0xc0 | address >> 8;
addressLow = address & 0xff;
addressHigh = 0xC0 | address >> 8;
addressLow = address & 0xFF;
}
else
{
assert(address >= shortAddressMin && address <= shortAddressMax);
addressHigh = 0x00;
addressLow = address & 0x7f;
addressLow = address & 0x7F;
}
}
};
inline uint16_t address() const
{
return (static_cast<uint16_t>(addressHigh & 0x3F) << 8) | addressLow;
}
inline bool isLongAddress() const
{
return (addressHigh & 0xC0) == 0xC0;
}
} ATTRIBUTE_PACKED;
static_assert(sizeof(LocomotiveInstruction) == 4);
struct SpeedAndDirectionInstruction : LocomotiveInstruction
@ -305,7 +354,7 @@ struct SpeedAndDirectionInstruction : LocomotiveInstruction
if(direction == Direction::Forward)
speedAndDirection |= 0x80;
}
};
} ATTRIBUTE_PACKED;
struct SpeedAndDirectionInstruction14 : SpeedAndDirectionInstruction
{
@ -320,7 +369,7 @@ struct SpeedAndDirectionInstruction14 : SpeedAndDirectionInstruction
speedAndDirection |= 0x10;
checksum = calcChecksum(*this);
}
};
} ATTRIBUTE_PACKED;
struct SpeedAndDirectionInstruction27 : SpeedAndDirectionInstruction
{
@ -333,7 +382,7 @@ struct SpeedAndDirectionInstruction27 : SpeedAndDirectionInstruction
speedAndDirection |= (((speedStep + 1) & 0x01) << 4) | ((speedStep + 1) >> 1);
checksum = calcChecksum(*this);
}
};
} ATTRIBUTE_PACKED;
struct SpeedAndDirectionInstruction28 : SpeedAndDirectionInstruction
{
@ -346,7 +395,7 @@ struct SpeedAndDirectionInstruction28 : SpeedAndDirectionInstruction
speedAndDirection |= (((speedStep + 1) & 0x01) << 4) | ((speedStep + 1) >> 1);
checksum = calcChecksum(*this);
}
};
} ATTRIBUTE_PACKED;
struct SpeedAndDirectionInstruction128 : SpeedAndDirectionInstruction
{
@ -359,7 +408,7 @@ struct SpeedAndDirectionInstruction128 : SpeedAndDirectionInstruction
speedAndDirection |= speedStep + 1;
checksum = calcChecksum(*this);
}
};
} ATTRIBUTE_PACKED;
struct FunctionInstructionGroup : LocomotiveInstruction
{
@ -372,7 +421,7 @@ struct FunctionInstructionGroup : LocomotiveInstruction
assert(group >= 1 && group <= 5);
identification = (group == 5) ? 0x28 : (0x1F + group);
}
};
} ATTRIBUTE_PACKED;
struct FunctionInstructionGroup1 : FunctionInstructionGroup
{
@ -392,7 +441,7 @@ struct FunctionInstructionGroup1 : FunctionInstructionGroup
checksum = calcChecksum(*this);
}
};
} ATTRIBUTE_PACKED;
struct FunctionInstructionGroup2 : FunctionInstructionGroup
{
@ -410,7 +459,7 @@ struct FunctionInstructionGroup2 : FunctionInstructionGroup
checksum = calcChecksum(*this);
}
};
} ATTRIBUTE_PACKED;
struct FunctionInstructionGroup3 : FunctionInstructionGroup
{
@ -428,7 +477,7 @@ struct FunctionInstructionGroup3 : FunctionInstructionGroup
checksum = calcChecksum(*this);
}
};
} ATTRIBUTE_PACKED;
struct FunctionInstructionGroup4 : FunctionInstructionGroup
{
@ -454,7 +503,7 @@ struct FunctionInstructionGroup4 : FunctionInstructionGroup
checksum = calcChecksum(*this);
}
};
} ATTRIBUTE_PACKED;
struct FunctionInstructionGroup5 : FunctionInstructionGroup
{
@ -480,7 +529,7 @@ struct FunctionInstructionGroup5 : FunctionInstructionGroup
checksum = calcChecksum(*this);
}
};
} ATTRIBUTE_PACKED;
/*
struct setFunctionStateGroup : LocomotiveInstruction
@ -495,9 +544,23 @@ struct setFunctionStateGroup : LocomotiveInstruction
identification = 0x23 + group;
addressLowHigh(address, addressLow, addressHigh);
}
} __attribute__((packed));
} ATTRIBUTE_PACKED;
*/
// 2.19.7 Locomotive is Occupied (from Central version 3.0)
struct LocomotiveBusy : LocomotiveInstruction
{
uint8_t checksum;
LocomotiveBusy(uint16_t address)
: LocomotiveInstruction(address)
{
identification = idLocomotiveBusy;
checksum = calcChecksum(*this);
}
} ATTRIBUTE_PACKED;
static_assert(sizeof(LocomotiveBusy) == 5);
struct AccessoryDecoderOperationRequest : Message
{
static constexpr uint8_t db2Port = 0x01;
@ -539,7 +602,7 @@ struct AccessoryDecoderOperationRequest : Message
{
return db2 & db2Activate;
}
};
} ATTRIBUTE_PACKED;
static_assert(sizeof(AccessoryDecoderOperationRequest) == 4);
namespace RocoMultiMAUS
@ -573,7 +636,7 @@ namespace RocoMultiMAUS
checksum = calcChecksum(*this);
}
};
} ATTRIBUTE_PACKED;
}
namespace RoSoftS88XpressNetLI
@ -597,7 +660,7 @@ namespace RoSoftS88XpressNetLI
assert((startAddress >= startAddressMin && startAddress <= startAddressMax) || startAddress == startAddressGet);
checksum = calcChecksum(*this);
}
};
} ATTRIBUTE_PACKED;
struct S88ModuleCount : Message
{
@ -618,7 +681,7 @@ namespace RoSoftS88XpressNetLI
assert((moduleCount >= moduleCountMin && moduleCount <= moduleCountMax) || moduleCount == moduleCountGet);
checksum = calcChecksum(*this);
}
};
} ATTRIBUTE_PACKED;
}
inline uint8_t calcChecksum(const Message& msg)

Datei anzeigen

@ -22,7 +22,7 @@
#include "server.hpp"
#include <boost/beast/http/buffer_body.hpp>
#include <tcb/span.hpp>
#include <span>
#include <traintastic/network/message.hpp>
#include <version.hpp>
#include "clientconnection.hpp"
@ -89,7 +89,7 @@ http::message_generator methodNotAllowed(const http::request<http::string_body>&
return response;
}
http::message_generator binary(const http::request<http::string_body>& request, std::string_view contentType, tcb::span<const std::byte> body)
http::message_generator binary(const http::request<http::string_body>& request, std::string_view contentType, std::span<const std::byte> body)
{
if(request.method() != http::verb::get && request.method() != http::verb::head)
{

Datei anzeigen

@ -55,9 +55,6 @@ Session::Session(const std::shared_ptr<ClientConnection>& connection) :
Session::~Session()
{
assert(isEventLoopThread());
m_memoryLoggerChanged.disconnect();
for(const auto& it : m_objectSignals)
it.second.disconnect();
}
bool Session::processMessage(const Message& message)

Datei anzeigen

@ -58,7 +58,7 @@ class Session : public std::enable_shared_from_this<Session>
static void writeAttribute(Message& message, const AbstractAttribute& attribute);
static void writeTypeInfo(Message& message, const TypeInfo& typeInfo);
boost::signals2::connection m_memoryLoggerChanged;
boost::signals2::scoped_connection m_memoryLoggerChanged;
protected:
using Handle = uint32_t;
@ -67,7 +67,7 @@ class Session : public std::enable_shared_from_this<Session>
std::shared_ptr<ClientConnection> m_connection;
boost::uuids::uuid m_uuid;
Handles m_handles;
std::unordered_multimap<Handle, boost::signals2::connection> m_objectSignals;
std::unordered_multimap<Handle, boost::signals2::scoped_connection> m_objectSignals;
bool processMessage(const Message& message);

Datei anzeigen

@ -121,6 +121,8 @@ void TrainVehicleList::propertyChanged(BaseProperty& property)
train().updateWeight();
else if(property.name() == "speed_max")
train().updateSpeedMax();
ObjectList<RailVehicle>::propertyChanged(property);
}
Train& TrainVehicleList::train()

Datei anzeigen

@ -198,7 +198,6 @@ Traintastic::RunStatus Traintastic::run(const std::string& worldUUID, bool simul
Log::log(*this, LogMessage::I1007_X, std::string_view{"nlohmann::json " STR(NLOHMANN_JSON_VERSION_MAJOR) "." STR(NLOHMANN_JSON_VERSION_MINOR) "." STR(NLOHMANN_JSON_VERSION_PATCH)});
Log::log(*this, LogMessage::I1008_X, std::string_view{archive_version_details()});
Log::log(*this, LogMessage::I1009_ZLIB_X, std::string_view{zlibVersion()});
//! \todo Add tcb::span version when available, see https://github.com/tcbrindle/span/issues/33
Log::log(*this, LogMessage::I9002_X, Lua::getVersion());
settings = std::make_shared<Settings>(m_dataDir);

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2022,2024 Reinder Feenstra
* Copyright (C) 2022,2024-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -25,7 +25,7 @@
#include <array>
#include <vector>
#include <tcb/span.hpp>
#include <span>
template<class T, std::size_t N>
inline bool contains(const std::array<T, N>& array, T value)
@ -40,7 +40,7 @@ inline bool contains(const std::vector<T>& vector, T value)
}
template<class T>
inline bool contains(tcb::span<const T> span, T value)
inline bool contains(std::span<const T> span, T value)
{
return std::find(span.begin(), span.end(), value) != span.end();
}

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2023 Reinder Feenstra
* Copyright (C) 2023,2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -24,13 +24,12 @@
#define TRAINTASTIC_SERVER_UTILS_RANDOM_HPP
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <random>
class Random
{
private:
inline static boost::random::mt19937 s_gen{static_cast<unsigned int>(time(nullptr))};
inline static std::mt19937 s_gen{static_cast<unsigned int>(time(nullptr))};
Random() = default;
@ -38,7 +37,7 @@ class Random
template<typename T>
static T value(T min, T max)
{
boost::random::uniform_int_distribution<T> dist(min, max);
std::uniform_int_distribution<T> dist(min, max);
return dist(s_gen);
}

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2020,2023-2024 Reinder Feenstra
* Copyright (C) 2019-2020,2023-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -29,7 +29,7 @@
#include "freightwagon.hpp"
#include "tankwagon.hpp"
tcb::span<const std::string_view> RailVehicles::classList()
std::span<const std::string_view> RailVehicles::classList()
{
static constexpr auto classes = makeArray(
Locomotive::classId,

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2020,2023-2024 Reinder Feenstra
* Copyright (C) 2019-2020,2023-2025 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -29,7 +29,7 @@ struct RailVehicles
{
static constexpr std::string_view classIdPrefix = "vehicle.rail.";
static tcb::span<const std::string_view> classList();
static std::span<const std::string_view> classList();
static std::shared_ptr<RailVehicle> create(World& world, std::string_view classId, std::string_view id);
};

Datei anzeigen

@ -1,88 +0,0 @@
/*
Copyright (c) Marshall Clow 2014.
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Revision history:
2 Dec 2014 mtc First version; power
*/
/// \file algorithm.hpp
/// \brief Misc Algorithms
/// \author Marshall Clow
///
#ifndef BOOST_ALGORITHM_HPP
#define BOOST_ALGORITHM_HPP
#include <functional> // for plus and multiplies
#include <boost/utility/enable_if.hpp> // for boost::disable_if
#include <boost/type_traits/is_integral.hpp>
namespace boost { namespace algorithm {
template <typename T>
BOOST_CXX14_CONSTEXPR T identity_operation ( std::multiplies<T> ) { return T(1); }
template <typename T>
BOOST_CXX14_CONSTEXPR T identity_operation ( std::plus<T> ) { return T(0); }
/// \fn power ( T x, Integer n )
/// \return the value "x" raised to the power "n"
///
/// \param x The value to be exponentiated
/// \param n The exponent (must be >= 0)
///
// \remark Taken from Knuth, The Art of Computer Programming, Volume 2:
// Seminumerical Algorithms, Section 4.6.3
template <typename T, typename Integer>
BOOST_CXX14_CONSTEXPR typename boost::enable_if<boost::is_integral<Integer>, T>::type
power (T x, Integer n) {
T y = 1; // Should be "T y{1};"
if (n == 0) return y;
while (true) {
if (n % 2 == 1) {
y = x * y;
if (n == 1)
return y;
}
n = n / 2;
x = x * x;
}
return y;
}
/// \fn power ( T x, Integer n, Operation op )
/// \return the value "x" raised to the power "n"
/// using the operation "op".
///
/// \param x The value to be exponentiated
/// \param n The exponent (must be >= 0)
/// \param op The operation used
///
// \remark Taken from Knuth, The Art of Computer Programming, Volume 2:
// Seminumerical Algorithms, Section 4.6.3
template <typename T, typename Integer, typename Operation>
BOOST_CXX14_CONSTEXPR typename boost::enable_if<boost::is_integral<Integer>, T>::type
power (T x, Integer n, Operation op) {
T y = identity_operation(op);
if (n == 0) return y;
while (true) {
if (n % 2 == 1) {
y = op(x, y);
if (n == 1)
return y;
}
n = n / 2;
x = op(x, x);
}
return y;
}
}}
#endif // BOOST_ALGORITHM_HPP

Datei anzeigen

@ -1,126 +0,0 @@
/*
Copyright (c) Alexander Zaitsev <zamazan4ik@gmail.com>, 2017
Distributed under the Boost Software License, Version 1.0. (See
accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt)
See http://www.boost.org/ for latest version.
Based on https://blogs.msdn.microsoft.com/oldnewthing/20170104-00/?p=95115
*/
/// \file apply_permutation.hpp
/// \brief Apply permutation to a sequence.
/// \author Alexander Zaitsev
#ifndef BOOST_ALGORITHM_APPLY_PERMUTATION_HPP
#define BOOST_ALGORITHM_APPLY_PERMUTATION_HPP
#include <algorithm>
#include <type_traits>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
namespace boost { namespace algorithm
{
/// \fn apply_permutation ( RandomAccessIterator1 item_begin, RandomAccessIterator1 item_end, RandomAccessIterator2 ind_begin )
/// \brief Reorder item sequence with index sequence order
///
/// \param item_begin The start of the item sequence
/// \param item_end One past the end of the item sequence
/// \param ind_begin The start of the index sequence.
///
/// \note Item sequence size should be equal to index size. Otherwise behavior is undefined.
/// Complexity: O(N).
template<typename RandomAccessIterator1, typename RandomAccessIterator2>
void
apply_permutation(RandomAccessIterator1 item_begin, RandomAccessIterator1 item_end,
RandomAccessIterator2 ind_begin, RandomAccessIterator2 ind_end)
{
typedef typename std::iterator_traits<RandomAccessIterator1>::difference_type Diff;
typedef typename std::iterator_traits<RandomAccessIterator2>::difference_type Index;
using std::swap;
Diff size = std::distance(item_begin, item_end);
for (Diff i = 0; i < size; i++)
{
Diff current = i;
while (i != ind_begin[current])
{
Index next = ind_begin[current];
swap(item_begin[current], item_begin[next]);
ind_begin[current] = current;
current = next;
}
ind_begin[current] = current;
}
}
/// \fn apply_reverse_permutation ( RandomAccessIterator1 item_begin, RandomAccessIterator1 item_end, RandomAccessIterator2 ind_begin )
/// \brief Reorder item sequence with index sequence order
///
/// \param item_begin The start of the item sequence
/// \param item_end One past the end of the item sequence
/// \param ind_begin The start of the index sequence.
///
/// \note Item sequence size should be equal to index size. Otherwise behavior is undefined.
/// Complexity: O(N).
template<typename RandomAccessIterator1, typename RandomAccessIterator2>
void
apply_reverse_permutation(
RandomAccessIterator1 item_begin,
RandomAccessIterator1 item_end,
RandomAccessIterator2 ind_begin,
RandomAccessIterator2 ind_end)
{
typedef typename std::iterator_traits<RandomAccessIterator2>::difference_type Diff;
using std::swap;
Diff length = std::distance(item_begin, item_end);
for (Diff i = 0; i < length; i++)
{
while (i != ind_begin[i])
{
Diff next = ind_begin[i];
swap(item_begin[i], item_begin[next]);
swap(ind_begin[i], ind_begin[next]);
}
}
}
/// \fn apply_permutation ( Range1 item_range, Range2 ind_range )
/// \brief Reorder item sequence with index sequence order
///
/// \param item_range The item sequence
/// \param ind_range The index sequence
///
/// \note Item sequence size should be equal to index size. Otherwise behavior is undefined.
/// Complexity: O(N).
template<typename Range1, typename Range2>
void
apply_permutation(Range1& item_range, Range2& ind_range)
{
apply_permutation(boost::begin(item_range), boost::end(item_range),
boost::begin(ind_range), boost::end(ind_range));
}
/// \fn apply_reverse_permutation ( Range1 item_range, Range2 ind_range )
/// \brief Reorder item sequence with index sequence order
///
/// \param item_range The item sequence
/// \param ind_range The index sequence
///
/// \note Item sequence size should be equal to index size. Otherwise behavior is undefined.
/// Complexity: O(N).
template<typename Range1, typename Range2>
void
apply_reverse_permutation(Range1& item_range, Range2& ind_range)
{
apply_reverse_permutation(boost::begin(item_range), boost::end(item_range),
boost::begin(ind_range), boost::end(ind_range));
}
}}
#endif //BOOST_ALGORITHM_APPLY_PERMUTATION_HPP

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden Mehr anzeigen