current state, just to have some history

Dieser Commit ist enthalten in:
Reinder Feenstra 2019-11-01 21:35:54 +01:00
Commit 3fd0aa18be
4300 geänderte Dateien mit 871754 neuen und 0 gelöschten Zeilen

41
.gitignore vendored Normale Datei
Datei anzeigen

@ -0,0 +1,41 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
*.pro.user*
*.pro*.user*
*.hpp~*
*.cpp~*
.directory
.build*
build*

339
LICENSE Normale Datei
Datei anzeigen

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

20
README.md Normale Datei
Datei anzeigen

@ -0,0 +1,20 @@
Third party
server:
boost 1.71
nlohmann's json for modern C++
client:
Qt 5
https://github.com/snowwlex/QtWaitingSpinner

6
client/dark.qrc Normale Datei
Datei anzeigen

@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/dark">
<file alias="run.svg">gfx/dark/run.svg</file>
<file alias="stop.svg">gfx/dark/stop.svg</file>
</qresource>
</RCC>

86
client/gfx/dark/run.svg Normale Datei

Dateidiff unterdrückt, weil mindestens eine Zeile zu lang ist

Nachher

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

86
client/gfx/dark/stop.svg Normale Datei
Datei anzeigen

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="96"
height="96"
viewBox="0 0 25.399999 25.400001"
version="1.1"
id="svg8"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="stop.svg">
<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="87.582824"
inkscape:cy="31.95405"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1015"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1">
<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" />
<dc:title></dc:title>
<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)">
<path
style="stroke-width:0.99999994;fill:#cd5c5c;fill-opacity:0.94117647"
d="M 18 8 A 9.9999997 9.9999997 0 0 0 8 18 L 8 78 A 9.9999997 9.9999997 0 0 0 18 88 L 78 88 A 9.9999997 9.9999997 0 0 0 88 78 L 88 18 A 9.9999997 9.9999997 0 0 0 78 8 L 18 8 z "
transform="matrix(0.26458333,0,0,0.26458333,0,271.59998)"
id="path17" />
</g>
</svg>

Nachher

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

Datei anzeigen

@ -0,0 +1,208 @@
/**
* client/src/dialog/connectdialog.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "connectdialog.hpp"
#include <QUdpSocket>
#include <QNetworkInterface>
#include <QFormLayout>
#include <QComboBox>
#include <QLineEdit>
#include <QLabel>
#include <QPushButton>
#include <QUrl>
#include <QTimer>
#include "../network/client.hpp"
ConnectDialog::ConnectDialog(QWidget* parent) :
QDialog(parent, Qt::Dialog | Qt::WindowTitleHint | Qt::WindowCloseButtonHint),
m_udpSocket{new QUdpSocket(this)},
m_server{new QComboBox()},
m_username{new QLineEdit()},
m_password{new QLineEdit()},
m_status{new QLabel()},
m_connect{new QPushButton(tr("Connect"))}
{
setWindowTitle(tr("Connect to server"));
//m_udpSocket->bind(QHostAddress::Any);
m_server->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow);
m_server->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_server->setEditable(true);
//m_server->addItem("localhost");
m_password->setEchoMode(QLineEdit::Password);
m_status->setAlignment(Qt::AlignCenter);
m_status->setMinimumWidth(400);
QFormLayout* formLayout = new QFormLayout();
formLayout->setMargin(0);
formLayout->addRow(tr("Server"), m_server);
formLayout->addRow(tr("Username"), m_username);
formLayout->addRow(tr("Password"), m_password);
QVBoxLayout* layout = new QVBoxLayout();
layout->setMargin(0);
layout->addLayout(formLayout);
layout->addWidget(m_status);
layout->addWidget(m_connect, 0, Qt::AlignCenter);
layout->setSizeConstraint(QLayout::SetFixedSize);
setLayout(layout);
//setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
connect(m_udpSocket, &QUdpSocket::readyRead, this, &ConnectDialog::socketReadyRead);
connect(&m_broadcastTimer, &QTimer::timeout, this, &ConnectDialog::broadcast);
connect(Client::instance, &Client::stateChanged, this, &ConnectDialog::stateChanged);
connect(m_server, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ConnectDialog::serverIndexChanged);
connect(m_server, &QComboBox::currentTextChanged, this, &ConnectDialog::serverTextChanged);
connect(m_connect, &QPushButton::clicked, this, &ConnectDialog::connectClick);
m_broadcastTimer.start(1000);
broadcast();
}
void ConnectDialog::closeEvent(QCloseEvent*)
{
deleteLater();
}
void ConnectDialog::setControlsEnabled(bool value)
{
m_server->setEnabled(value);
m_username->setEnabled(value);
m_password->setEnabled(value);
m_connect->setEnabled(value);
}
void ConnectDialog::broadcast()
{
// decrement TTL of all discovered servers:
for(Servers::iterator it = m_servers.begin(); it != m_servers.end(); )
{
if(--it.value().second == 0)
{
for(int i = 0; i < m_server->count(); i++)
if(m_server->itemData(i) == it.key())
{
m_server->removeItem(i);
break;
}
it = m_servers.erase(it);
}
else
it++;
}
// send discovery request on all networks:
for(auto& interface : QNetworkInterface::allInterfaces())
for(auto& addressEntry : interface.addressEntries())
if(!addressEntry.broadcast().isNull())
{
auto message = Message::newRequest(Message::Command::Discover);
m_udpSocket->writeDatagram(reinterpret_cast<const char*>(**message), message->size(), addressEntry.broadcast(), Client::defaultPort);
}
}
void ConnectDialog::socketReadyRead()
{
while(m_udpSocket->hasPendingDatagrams())
{
Message message(m_udpSocket->pendingDatagramSize());
QHostAddress host;
quint16 port;
m_udpSocket->readDatagram(static_cast<char*>(*message), message.size(), &host, &port);
if(message.command() == Message::Command::Discover && message.isResponse() && !message.isError())
{
QString name = QString::fromUtf8(message.read<QByteArray>());
QUrl url;
url.setHost(host.toString());
url.setPort(port);
auto it = m_servers.find(url);
if(it == m_servers.end())
{
m_servers[url] = {name, defaultTTL};
m_server->addItem(url.host() + (url.port() != Client::defaultPort ? ":" + QString::number(url.port()) : "") + " (" + name + ")", url);
}
else
it->second = defaultTTL;
}
}
}
void ConnectDialog::stateChanged()
{
switch(Client::instance->state())
{
case Client::State::Disconnected:
m_status->setText("");
break;
case Client::State::Disconnecting:
m_status->setText(tr("Disconnecting"));
break;
case Client::State::Connecting:
m_status->setText(tr("Connecting"));
break;
case Client::State::Connected:
m_status->setText(tr("Connected"));
QTimer::singleShot(300, this, &ConnectDialog::close);
break;
case Client::State::SocketError:
m_status->setText(Client::instance->errorString());
setControlsEnabled(true);
break;
case Client::State::ErrorAuthenticationFailed:
m_status->setText(tr("Authentication failed"));
break;
case Client::State::ErrorNewSessionFailed:
m_status->setText(tr("Create session failed"));
break;
}
}
void ConnectDialog::serverIndexChanged(int index)
{
m_url = m_server->itemData(index).toUrl();
m_connect->setEnabled(m_url.isValid());
}
void ConnectDialog::serverTextChanged(const QString& text)
{
QString url{text};
m_url = QUrl::fromUserInput(url.remove(QRegularExpression("\\s*\\(.*\\)$")));
m_connect->setEnabled(m_url.isValid());
}
void ConnectDialog::connectClick()
{
setControlsEnabled(false);
Client::instance->connectToHost(m_url, m_username->text(), m_password->text());
}

Datei anzeigen

@ -0,0 +1,71 @@
/**
* client/src/dialog/connectdialog.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_DIALOG_CONNECTDIALOG_HPP
#define CLIENT_DIALOG_CONNECTDIALOG_HPP
#include <QDialog>
#include <QTimer>
#include <QMap>
#include <QPair>
#include <QUrl>
class QUdpSocket;
class QComboBox;
class QLineEdit;
class QLabel;
class QPushButton;
class ConnectDialog : public QDialog
{
protected:
using Servers = QMap<QUrl, QPair<QString, int>>;
static constexpr int defaultTTL = 3;
QUdpSocket* m_udpSocket;
Servers m_servers;
QTimer m_broadcastTimer;
QComboBox* m_server;
QLineEdit* m_username;
QLineEdit* m_password;
QLabel* m_status;
QPushButton* m_connect;
QUrl m_url;
void closeEvent(QCloseEvent*) final;
void setControlsEnabled(bool value);
protected slots:
void broadcast();
void socketReadyRead();
void stateChanged();
void serverIndexChanged(int index);
void serverTextChanged(const QString& text);
void connectClick();
public:
ConnectDialog(QWidget* parent = nullptr);
};
#endif

53
client/src/main.cpp Normale Datei
Datei anzeigen

@ -0,0 +1,53 @@
/**
* client/src/main.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "mainwindow.hpp"
#include <QApplication>
#ifdef Q_OS_WINDOWS
#include <QSettings>
#endif
#include "network/client.hpp"
int main(int argc, char* argv[])
{
QApplication::setOrganizationName("Traintastic");
QApplication::setOrganizationDomain("traintastic.org");
QApplication::setApplicationName("traintastic-client");
QApplication::setApplicationVersion("0.1.0");
#ifdef Q_OS_WINDOWS
QSettings::setDefaultFormat(QSettings::IniFormat);
#endif
QApplication app(argc, argv);
Client client;
Client::instance = &client;
MainWindow mw;
mw.show();
if(client.state() != Client::State::Connected)
mw.connectToServer();
return app.exec();
}

268
client/src/mainwindow.cpp Normale Datei
Datei anzeigen

@ -0,0 +1,268 @@
/**
* client/src/mainwindow.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "mainwindow.hpp"
#include <QMenuBar>
#include <QStatusBar>
#include <QMdiArea>
#include <QMdiSubWindow>
#include <QVBoxLayout>
#include <QToolBar>
#include <QApplication>
#include <QMessageBox>
#include <QSettings>
#include <QApplication>
#include "dialog/connectdialog.hpp"
#include "network/client.hpp"
#include "subwindow/hardwarelistsubwindow.hpp"
#include "subwindow/serversettingssubwindow.hpp"
#include "subwindow/serverconsolesubwindow.hpp"
#define SETTING_PREFIX "mainwindow/"
#define SETTING_GEOMETRY SETTING_PREFIX "geometry"
#define SETTING_WINDOWSTATE SETTING_PREFIX "windowstate"
MainWindow::MainWindow(QWidget* parent) :
QMainWindow(parent),
m_mdiArea{new QMdiArea()}
{
setWindowTitle("Traintastic");
QAction* actFullScreen;
m_mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
m_mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
{
setMenuBar(new QMenuBar());
QMenu* menu;
//QMenu* subMenu;
//QAction* act;
menu = menuBar()->addMenu(tr("File"));
m_actionConnectToServer = menu->addAction(tr("Connect to server") + "...", this, &MainWindow::connectToServer);
m_actionDisconnectFromServer = menu->addAction(tr("Disconnect from server"), this, &MainWindow::disconnectFromServer);
menu->addSeparator();
m_actionNewWorld = menu->addAction(tr("New world"), this, &MainWindow::newWorld);
m_actionLoadWorld = menu->addAction(tr("Load world") + "...", this, &MainWindow::loadWorld);
m_actionSaveWorld = menu->addAction(tr("Save world"), this, &MainWindow::saveWorld);
menu->addSeparator();
m_actionImportWorld = menu->addAction(tr("Import world") + "...", this, &MainWindow::importWorld);
m_actionExportWorld = menu->addAction(tr("Export world") + "...", this, &MainWindow::exportWorld);
menu->addSeparator();
menu->addAction(tr("Quit"), this, &MainWindow::close);
menu = menuBar()->addMenu(tr("View"));
actFullScreen = menu->addAction(tr("Fullscreen"), this, &MainWindow::toggleFullScreen);
actFullScreen->setCheckable(true);
actFullScreen->setShortcut(Qt::Key_F11);
menu = menuBar()->addMenu(tr("Objects"));
m_actionHardware = menu->addAction(tr("Hardware") + "...", this, &MainWindow::showHardware);
menu = menuBar()->addMenu(tr("Tools"));
menu->addAction(tr("Client settings") + "...");
menu->addSeparator();
m_actionServerSettings = menu->addAction(tr("Server settings") + "...", this, &MainWindow::showServerSettings);
m_actionServerConsole = menu->addAction(tr("Server console") + "...", this, &MainWindow::showServerConsole);
menu = menuBar()->addMenu(tr("Help"));
menu->addAction(tr("Help"));
menu->addSeparator();
menu->addAction(tr("About Qt") + "...", qApp, &QApplication::aboutQt);
menu->addAction(tr("About") + "...", this, &MainWindow::showAbout);
}
QToolBar* toolbar = new QToolBar();
m_actionModeRun = toolbar->addAction(QIcon(":/dark/run.svg"), tr("Run"));
m_actionModeStop = toolbar->addAction(QIcon(":/dark/stop.svg"), tr("Stop"));
m_actionModeEdit = toolbar->addAction(tr("Edit"));
toolbar->addSeparator();
QVBoxLayout* l = new QVBoxLayout();
l->setMargin(0);
l->addWidget(toolbar);
l->addWidget(m_mdiArea);
QWidget* w = new QWidget();
w->setLayout(l);
setCentralWidget(w);
setStatusBar(new QStatusBar());
QSettings settings;
if(settings.contains(SETTING_GEOMETRY))
restoreGeometry(settings.value(SETTING_GEOMETRY).toByteArray());
if(settings.contains(SETTING_WINDOWSTATE))
setWindowState(static_cast<Qt::WindowState>(settings.value(SETTING_WINDOWSTATE).toInt()));
actFullScreen->setChecked(isFullScreen());
connect(Client::instance, &Client::stateChanged, this, &MainWindow::updateActionEnabled);
updateActionEnabled();
}
MainWindow::~MainWindow()
{
}
void MainWindow::connectToServer()
{
ConnectDialog* d = new ConnectDialog(this);
d->setModal(true);
d->show();
}
void MainWindow::disconnectFromServer()
{
Client::instance->disconnectFromHost();
}
void MainWindow::closeEvent(QCloseEvent* event)
{
QSettings settings;
settings.setValue(SETTING_GEOMETRY, saveGeometry());
settings.setValue(SETTING_WINDOWSTATE, static_cast<int>(windowState()));
QMainWindow::closeEvent(event);
}
void MainWindow::newWorld()
{
}
void MainWindow::loadWorld()
{
}
void MainWindow::saveWorld()
{
}
void MainWindow::importWorld()
{
}
void MainWindow::exportWorld()
{
}
void MainWindow::toggleFullScreen()
{
const bool fullScreen = qobject_cast<QAction*>(sender())->isChecked();
if(fullScreen == isFullScreen())
return;
if(fullScreen)
{
m_beforeFullScreenGeometry = saveGeometry();
showFullScreen();
}
else
{
showNormal(); // required to exit fullscreen mode
if(!m_beforeFullScreenGeometry.isEmpty())
restoreGeometry(m_beforeFullScreenGeometry);
else
showMaximized();
}
}
void MainWindow::showHardware()
{
if(!m_mdiSubWindow.hardwareList)
{
m_mdiSubWindow.hardwareList = new HardwareListSubWindow();
m_mdiArea->addSubWindow(m_mdiSubWindow.hardwareList);
m_mdiSubWindow.hardwareList->setAttribute(Qt::WA_DeleteOnClose);
connect(m_mdiSubWindow.hardwareList, &QMdiSubWindow::destroyed, [=](QObject*){ m_mdiSubWindow.hardwareList = nullptr; });
m_mdiSubWindow.hardwareList->show();
}
else
m_mdiArea->setActiveSubWindow(m_mdiSubWindow.hardwareList);
}
void MainWindow::showServerSettings()
{
if(!m_mdiSubWindow.serverSettings)
{
m_mdiSubWindow.serverSettings = new ServerSettingsSubWindow();
m_mdiArea->addSubWindow(m_mdiSubWindow.serverSettings);
m_mdiSubWindow.serverSettings->setAttribute(Qt::WA_DeleteOnClose);
connect(m_mdiSubWindow.serverSettings, &QMdiSubWindow::destroyed, [=](QObject*){ m_mdiSubWindow.serverSettings = nullptr; });
m_mdiSubWindow.serverSettings->show();
}
else
m_mdiArea->setActiveSubWindow(m_mdiSubWindow.serverSettings);
}
void MainWindow::showServerConsole()
{
if(!m_mdiSubWindow.serverConsole)
{
m_mdiSubWindow.serverConsole = new ServerConsoleSubWindow();
m_mdiArea->addSubWindow(m_mdiSubWindow.serverConsole);
m_mdiSubWindow.serverConsole->setAttribute(Qt::WA_DeleteOnClose);
connect(m_mdiSubWindow.serverConsole, &QMdiSubWindow::destroyed, [=](QObject*){ m_mdiSubWindow.serverConsole = nullptr; });
m_mdiSubWindow.serverConsole->show();
}
else
m_mdiArea->setActiveSubWindow(m_mdiSubWindow.serverConsole);
}
void MainWindow::showAbout()
{
QMessageBox::about(this, tr("About Traintastic"),
"<h2>Traintastic <small>v" + QApplication::applicationVersion() + "</small></h2>"
"<p>Copyright &copy; 2019 Reinder Feenstra</p>"
"<p>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.</p>"
"<p>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.</p>");
}
void MainWindow::updateActionEnabled()
{
Client& client = *Client::instance;
const bool connected = client.state() == Client::State::Connected;
m_actionConnectToServer->setEnabled(client.isDisconnected());
m_actionConnectToServer->setVisible(!connected);
m_actionDisconnectFromServer->setVisible(connected);
m_actionNewWorld->setEnabled(connected);
m_actionLoadWorld->setEnabled(connected);
m_actionSaveWorld->setEnabled(connected && false);
m_actionImportWorld->setEnabled(connected);
m_actionExportWorld->setEnabled(connected);
m_actionHardware->setEnabled(connected);
m_actionServerSettings->setEnabled(connected);
m_actionServerConsole->setEnabled(connected);
m_actionModeRun->setEnabled(false);
m_actionModeStop->setEnabled(false);
m_actionModeEdit->setEnabled(false);
//if(client.state() == Client::State::SocketError)
// statusBar()->showMessage(client.errorString());
}

85
client/src/mainwindow.hpp Normale Datei
Datei anzeigen

@ -0,0 +1,85 @@
/**
* client/src/mainwindow.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_MAINWINDOW_HPP
#define CLIENT_MAINWINDOW_HPP
#define MAINWINDOW_HPP
#include <QMainWindow>
class QMdiArea;
class HardwareListSubWindow;
class ServerSettingsSubWindow;
class ServerConsoleSubWindow;
class MainWindow : public QMainWindow
{
Q_OBJECT
protected:
QMdiArea* m_mdiArea;
struct
{
HardwareListSubWindow* hardwareList = nullptr;
ServerSettingsSubWindow* serverSettings = nullptr;
ServerConsoleSubWindow* serverConsole = nullptr;
} m_mdiSubWindow;
QAction* m_actionConnectToServer;
QAction* m_actionDisconnectFromServer;
QAction* m_actionNewWorld;
QAction* m_actionLoadWorld;
QAction* m_actionSaveWorld;
QAction* m_actionImportWorld;
QAction* m_actionExportWorld;
QAction* m_actionHardware;
QAction* m_actionServerSettings;
QAction* m_actionServerConsole;
QAction* m_actionModeRun;
QAction* m_actionModeStop;
QAction* m_actionModeEdit;
QByteArray m_beforeFullScreenGeometry;
void closeEvent(QCloseEvent* event) final;
void updateActionEnabled();
protected slots:
void disconnectFromServer();
void newWorld();
void loadWorld();
void saveWorld();
void importWorld();
void exportWorld();
void toggleFullScreen();
void showHardware();
void showServerSettings();
void showServerConsole();
void showAbout();
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow() override;
public slots:
void connectToServer();
};
#endif

462
client/src/network/client.cpp Normale Datei
Datei anzeigen

@ -0,0 +1,462 @@
/**
* client/src/network/client.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "client.hpp"
#include <QTcpSocket>
#include <QUrl>
#include <QCryptographicHash>
#include <message.hpp>
#include "object.hpp"
#include "property.hpp"
#include "tablemodel.hpp"
#include <enum/interfaceitemtype.hpp>
#include <enum/propertytype.hpp>
Client* Client::instance = nullptr;
Client::Client() :
QObject(),
m_socket{new QTcpSocket(this)},
m_state{State::Disconnected}
{
connect(m_socket, &QTcpSocket::connected, this, &Client::socketConnected);
connect(m_socket, &QTcpSocket::disconnected, this, &Client::socketDisconnected);
connect(m_socket, static_cast<void(QTcpSocket::*)(QAbstractSocket::SocketError)>(&QTcpSocket::error), this, &Client::socketError);
m_socket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
connect(m_socket, &QTcpSocket::readyRead, this, &Client::socketReadyRead);
}
Client::~Client()
{
}
bool Client::isDisconnected() const
{
return m_state != State::Connected && m_state != State::Connecting && m_state != State::Disconnecting;
}
Client::SocketError Client::error() const
{
return m_socket->error();
}
QString Client::errorString() const
{
return m_socket->errorString();
}
void Client::connectToHost(const QUrl& url, const QString& username, const QString& password)
{
m_username = username;
if(password.isEmpty())
m_password.clear();
else
m_password = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha256);
setState(State::Connecting);
m_socket->connectToHost(url.host(), static_cast<quint16>(url.port(defaultPort)));
}
void Client::disconnectFromHost()
{
m_socket->disconnectFromHost();
}
void Client::cancelRequest(int requestId)
{
if(requestId < std::numeric_limits<uint16_t>::min() || requestId > std::numeric_limits<uint16_t>::max())
return;
auto it = m_requestCallback.find(static_cast<uint16_t>(requestId));
if(it != m_requestCallback.end())
m_requestCallback.erase(it);
}
int Client::getObject(const QString& id, std::function<void(const ObjectPtr&, Message::ErrorCode)> callback)
{
std::unique_ptr<Message> request{Message::newRequest(Message::Command::GetObject)};
request->write(id.toLatin1());
send(request,
[this, callback](const std::shared_ptr<Message> message)
{
ObjectPtr object;
if(!message->isError())
object = readObject(*message);
m_objects[object->handle()] = object.data();
callback(object, message->errorCode());
});
return request->requestId();
}
void Client::releaseObject(Object* object)
{
Q_ASSERT(object);
m_objects.remove(object->handle());
auto event = Message::newEvent(Message::Command::ReleaseObject, sizeof(object->m_handle));
event->write(object->m_handle);
send(event);
object->m_handle = invalidHandle;
}
void Client::setPropertyBool(Property& property, bool value)
{
auto event = Message::newEvent(Message::Command::ObjectSetProperty);
event->write(static_cast<Object*>(property.parent())->m_handle);
event->write(property.name().toLatin1());
event->write(PropertyType::Boolean);
event->write(value);
send(event);
}
void Client::setPropertyInt64(Property& property, int64_t value)
{
auto event = Message::newEvent(Message::Command::ObjectSetProperty);
event->write(static_cast<Object*>(property.parent())->m_handle);
event->write(property.name().toLatin1());
event->write(PropertyType::Integer);
event->write(value);
send(event);
}
void Client::setPropertyDouble(Property& property, double value)
{
auto event = Message::newEvent(Message::Command::ObjectSetProperty);
event->write(static_cast<Object*>(property.parent())->m_handle);
event->write(property.name().toLatin1());
event->write(PropertyType::Float);
event->write(value);
send(event);
}
void Client::setPropertyString(Property& property, const QString& value)
{
auto event = Message::newEvent(Message::Command::ObjectSetProperty);
event->write(static_cast<Object*>(property.parent())->m_handle);
event->write(property.name().toLatin1());
event->write(PropertyType::String);
event->write(value.toUtf8());
send(event);
}
int Client::getTableModel(const QString& id, std::function<void(const TableModelPtr&, Message::ErrorCode)> callback)
{
std::unique_ptr<Message> request{Message::newRequest(Message::Command::GetTableModel)};
request->write(id.toLatin1());
send(request,
[this, callback](const std::shared_ptr<Message> message)
{
TableModelPtr tableModel;
if(!message->isError())
tableModel = readTableModel(*message);
m_tableModels[tableModel->handle()] = tableModel.data();
callback(tableModel, message->errorCode());
});
return request->requestId();
}
void Client::releaseTableModel(TableModel* tableModel)
{
Q_ASSERT(tableModel);
m_tableModels.remove(tableModel->handle());
auto event = Message::newEvent(Message::Command::ReleaseTableModel, sizeof(tableModel->m_handle));
event->write(tableModel->m_handle);
send(event);
tableModel->m_handle = invalidHandle;
}
void Client::setTableModelRegion(TableModel* tableModel, int columnMin, int columnMax, int rowMin, int rowMax)
{
auto event = Message::newEvent(Message::Command::TableModelSetRegion);
event->write(tableModel->handle());
event->write(columnMin);
event->write(columnMax);
event->write(rowMin);
event->write(rowMax);
send(event);
}
void Client::send(std::unique_ptr<Message>& message)
{
Q_ASSERT(!message->isRequest());
m_socket->write(static_cast<const char*>(**message), message->size());
}
void Client::send(std::unique_ptr<Message>& message, std::function<void(const std::shared_ptr<Message>&)> callback)
{
Q_ASSERT(message->isRequest());
Q_ASSERT(!m_requestCallback.contains(message->requestId()));
m_requestCallback[message->requestId()] = callback;
m_socket->write(static_cast<const char*>(**message), message->size());
}
ObjectPtr Client::readObject(const Message& message)
{
message.readBlock(); // object
const Handle handle = message.read<Handle>();
ObjectPtr obj; // get object by handle
if(!obj)
{
const QString classId = QString::fromLatin1(message.read<QByteArray>());
obj = QSharedPointer<Object>::create(handle, classId);
message.readBlock(); // items
while(!message.endOfBlock())
{
message.readBlock(); // item
const QString name = QString::fromLatin1(message.read<QByteArray>());
const InterfaceItemType type = message.read<InterfaceItemType>();
switch(type)
{
case InterfaceItemType::Property:
{
const PropertyType propertyType = message.read<PropertyType>();
QVariant value;
switch(propertyType)
{
case PropertyType::Boolean:
value = message.read<bool>();
break;
case PropertyType::Integer:
value = message.read<qint64>();
break;
case PropertyType::Float:
value = message.read<double>();
break;
case PropertyType::String:
value = QString::fromUtf8(message.read<QByteArray>());
break;
case PropertyType::Object:
// TODO
break;
case PropertyType::Invalid:
break;
}
Q_ASSERT(value.isValid());
if(Q_LIKELY(value.isValid()))
obj->m_interfaceItems.add(*new Property(*obj, name, propertyType, value));
break;
}
}
message.readBlockEnd(); // end item
}
message.readBlockEnd(); // end items
}
message.readBlockEnd(); // end object
return obj;
}
TableModelPtr Client::readTableModel(const Message& message)
{
message.readBlock(); // model
const Handle handle = message.read<Handle>();
const QString classId = QString::fromLatin1(message.read<QByteArray>());
TableModelPtr tableModel = TableModelPtr::create(handle, classId);
const int columnCount = message.read<int>();
for(int i = 0; i < columnCount; i++)
tableModel->m_columnHeaders.push_back(QString::fromUtf8(message.read<QByteArray>()));
Q_ASSERT(tableModel->m_columnHeaders.size() == columnCount);
message.read(tableModel->m_rowCount);
message.readBlockEnd(); // end model
return tableModel;
}
void Client::setState(State state)
{
if(m_state != state)
{
m_state = state;
emit stateChanged();
}
}
void Client::processMessage(const std::shared_ptr<Message> message)
{
if(message->isResponse())
{
auto it = m_requestCallback.find(message->requestId());
if(it != m_requestCallback.end())
{
it.value()(message);
m_requestCallback.erase(it);
}
}
else if(message->isEvent())
{
switch(message->command())
{
case Message::Command::ObjectPropertyChanged:
if(Object* object = m_objects.value(message->read<Handle>(), nullptr))
{
if(Property* property = object->getProperty(QString::fromLatin1(message->read<QByteArray>())))
{
switch(message->read<PropertyType>())
{
case PropertyType::Boolean:
{
const bool value = message->read<bool>();
property->m_value = value;
emit property->valueChanged();
emit property->valueChangedBool(value);
break;
}
case PropertyType::Integer:
break;
case PropertyType::Float:
break;
case PropertyType::String:
break;
}
}
}
break;
case Message::Command::TableModelColumnHeadersChanged:
if(TableModel* model = m_tableModels.value(message->read<Handle>(), nullptr))
{
const int columnCount = message->read<int>();
model->m_columnHeaders.clear();
for(int i = 0; i < columnCount; i++)
model->m_columnHeaders.push_back(QString::fromUtf8(message->read<QByteArray>()));
Q_ASSERT(model->m_columnHeaders.size() == columnCount);
}
break;
case Message::Command::TableModelRowCountChanged:
if(TableModel* model = m_tableModels.value(message->read<Handle>(), nullptr))
model->setRowCount(message->read<int>());
break;
case Message::Command::TableModelUpdateRegion:
if(TableModel* model = m_tableModels.value(message->read<Handle>(), nullptr))
{
const uint32_t columnMin = message->read<uint32_t>();
const uint32_t columnMax = message->read<uint32_t>();
const uint32_t rowMin = message->read<uint32_t>();
const uint32_t rowMax = message->read<uint32_t>();
model->beginResetModel();
TableModel::ColumnRow index;
QByteArray data;
for(index.second = rowMin; index.second <= rowMax; index.second++)
for(index.first = columnMin; index.first <= columnMax; index.first++)
{
message->read(data);
model->m_texts[index] = QString::fromUtf8(data);
}
model->endResetModel();
}
break;
default:
Q_ASSERT(false);
break;
}
}
}
void Client::socketConnected()
{
std::unique_ptr<Message> request{Message::newRequest(Message::Command::Login)};
request->write(m_username.toUtf8());
request->write(m_password);
send(request,
[this](const std::shared_ptr<Message> message)
{
if(message && message->isResponse() && !message->isError())
{
std::unique_ptr<Message> request{Message::newRequest(Message::Command::NewSession)};
send(request,
[this](const std::shared_ptr<Message> message)
{
if(message && message->isResponse() && !message->isError())
{
message->read(m_sessionUUID);
setState(State::Connected);
}
else
{
setState(State::ErrorNewSessionFailed);
m_socket->disconnectFromHost();
}
});
}
else
{
setState(State::ErrorAuthenticationFailed);
m_socket->disconnectFromHost();
}
});
}
void Client::socketDisconnected()
{
setState(State::Disconnected);
}
void Client::socketError(QAbstractSocket::SocketError)
{
setState(State::SocketError);
}
void Client::socketReadyRead()
{
while(m_socket->bytesAvailable() != 0)
{
if(!m_readBuffer.message) // read header
{
m_readBuffer.offset += m_socket->read(reinterpret_cast<char*>(&m_readBuffer.header) + m_readBuffer.offset, sizeof(m_readBuffer.header) - m_readBuffer.offset);
if(m_readBuffer.offset == sizeof(m_readBuffer.header))
{
if(m_readBuffer.header.dataSize != 0)
m_readBuffer.message = std::make_shared<Message>(m_readBuffer.header);
else
processMessage(std::make_shared<Message>(m_readBuffer.header));
m_readBuffer.offset = 0;
}
}
else // read data
{
m_readBuffer.offset += m_socket->read(reinterpret_cast<char*>(m_readBuffer.message->data()) + m_readBuffer.offset, m_readBuffer.message->dataSize() - m_readBuffer.offset);
if(m_readBuffer.offset == m_readBuffer.message->dataSize())
{
processMessage(m_readBuffer.message);
m_readBuffer.message.reset();
m_readBuffer.offset = 0;
}
}
}
}

122
client/src/network/client.hpp Normale Datei
Datei anzeigen

@ -0,0 +1,122 @@
/**
* client/src/network/client.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_NETWORK_CLIENT_HPP
#define CLIENT_NETWORK_CLIENT_HPP
#include <QObject>
#include <QAbstractSocket>
#include <QMap>
#include <QUuid>
#include <message.hpp>
#include "handle.hpp"
#include "objectptr.hpp"
#include "tablemodelptr.hpp"
class QTcpSocket;
class Property;
class Client : public QObject
{
Q_OBJECT
public:
enum class State
{
Disconnected = 0,
Disconnecting = 1,
Connected = 2,
Connecting = 3,
SocketError = 4,
ErrorAuthenticationFailed = 5,
ErrorNewSessionFailed = 6,
};
using SocketError = QAbstractSocket::SocketError;
protected:
QTcpSocket* m_socket;
State m_state;
QString m_username;
QByteArray m_password;
struct
{
qint64 offset = 0;
Message::Header header;
std::shared_ptr<Message> message;
} m_readBuffer;
QMap<uint16_t, std::function<void(const std::shared_ptr<Message>&)>> m_requestCallback;
QUuid m_sessionUUID;
QMap<Handle, Object*> m_objects;
QMap<Handle, TableModel*> m_tableModels;
void setState(State state);
void processMessage(const std::shared_ptr<Message> message);
void send(std::unique_ptr<Message>& message);
void send(std::unique_ptr<Message>& message, std::function<void(const std::shared_ptr<Message>&)> callback);
ObjectPtr readObject(const Message &message);
TableModelPtr readTableModel(const Message& message);
protected slots:
void socketConnected();
void socketDisconnected();
void socketError(QAbstractSocket::SocketError);
void socketReadyRead();
public:
static const quint16 defaultPort = 5740;
static constexpr int invalidRequestId = -1;
static Client* instance;
Client();
~Client();
inline bool isConnected() const { return m_state == State::Connected; }
bool isDisconnected() const;
State state() const { return m_state; }
SocketError error() const;
QString errorString() const;
void connectToHost(const QUrl& url, const QString& username, const QString& password);
void disconnectFromHost();
void cancelRequest(int requestId);
int getObject(const QString& id, std::function<void(const ObjectPtr&, Message::ErrorCode)> callback);
void releaseObject(Object* object);
void setPropertyBool(Property& property, bool value);
void setPropertyInt64(Property& property, int64_t value);
void setPropertyDouble(Property& property, double value);
void setPropertyString(Property& property, const QString& value);
int getTableModel(const QString& id, std::function<void(const TableModelPtr&, Message::ErrorCode)> callback);
void releaseTableModel(TableModel* tableModel);
void setTableModelRegion(TableModel* tableModel, int columnMin, int columnMax, int rowMin, int rowMax);
signals:
void stateChanged();
};
#endif

Datei anzeigen

@ -0,0 +1,32 @@
/**
* client/src/network/handle.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_NETWORK_HANDLE_HPP
#define CLIENT_NETWORK_HANDLE_HPP
#include <cstdint>
using Handle = uint32_t;
constexpr Handle invalidHandle = 0;
#endif

Datei anzeigen

@ -0,0 +1,30 @@
/**
* client/src/network/interfaceitem.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "interfaceitem.hpp"
#include "object.hpp"
InterfaceItem::InterfaceItem(Object& object, const QString& name) :
QObject(&object),
m_name{name}
{
}

Datei anzeigen

@ -0,0 +1,44 @@
/**
* client/src/network/interfaceitem.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_NETWORK_INTERFACEITEM_HPP
#define CLIENT_NETWORK_INTERFACEITEM_HPP
#include <QObject>
class Object;
class InterfaceItem : public QObject
{
Q_OBJECT
protected:
const QString m_name;
public:
explicit InterfaceItem(Object& object, const QString& name);
const QString& name() const { return m_name; }
const QString& displayName() const { return m_name; }
};
#endif

Datei anzeigen

@ -0,0 +1,29 @@
/**
* client/src/network/interfaceitems.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "interfaceitems.hpp"
#include "interfaceitem.hpp"
void InterfaceItems::add(InterfaceItem& item)
{
insert(item.name(), &item);
}

Datei anzeigen

@ -0,0 +1,36 @@
/**
* client/src/network/interfaceitems.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_NETWORK_INTERFACEITEMS_HPP
#define CLIENT_NETWORK_INTERFACEITEMS_HPP
#include <QMap>
class InterfaceItem;
class InterfaceItems : public QMap<QString, InterfaceItem*>
{
public:
void add(InterfaceItem& item);
};
#endif

Datei anzeigen

@ -0,0 +1,47 @@
/**
* client/src/network/object.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "object.hpp"
#include "client.hpp"
#include "property.hpp"
Object::Object(Handle handle, const QString& classId) :
QObject(nullptr),
m_handle{handle},
m_classId{classId}
{
}
Object::~Object()
{
Client::instance->releaseObject(this);
}
const Property* Object::getProperty(const QString& name) const
{
return dynamic_cast<Property*>(m_interfaceItems.value(name, nullptr));
}
Property* Object::getProperty(const QString& name)
{
return dynamic_cast<Property*>(m_interfaceItems.value(name, nullptr));
}

Datei anzeigen

@ -0,0 +1,59 @@
/**
* client/src/network/object.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_NETWORK_OBJECT_HPP
#define CLIENT_NETWORK_OBJECT_HPP
#include <QObject>
#include "handle.hpp"
#include "interfaceitems.hpp"
class Property;
class Object : public QObject
{
Q_OBJECT
friend class Client;
protected:
Handle m_handle;
const QString m_classId;
InterfaceItems m_interfaceItems;
public:
explicit Object(Handle handle, const QString& classId);
Object(const Object& copy) = delete;
~Object() final;
Handle handle() const { return m_handle; }
const QString& classId() const { return m_classId; }
const InterfaceItems& interfaceItems() const { return m_interfaceItems; }
const Property* getProperty(const QString& name) const;
Property* getProperty(const QString& name);
};
#endif

Datei anzeigen

@ -0,0 +1,32 @@
/**
* client/src/network/objectptr.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_NETWORK_OBJECTPTR_HPP
#define CLIENT_NETWORK_OBJECTPTR_HPP
#include <QSharedPointer>
class Object;
using ObjectPtr = QSharedPointer<Object>;
#endif

Datei anzeigen

@ -0,0 +1,51 @@
/**
* client/src/network/property.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "property.hpp"
#include "client.hpp"
Property::Property(Object& object, const QString& name, PropertyType type, const QVariant& value) :
InterfaceItem(object, name),
m_type{type},
m_value{value}
{
}
void Property::setValueBool(bool value)
{
Client::instance->setPropertyBool(*this, value);
}
void Property::setValueInt64(int64_t value)
{
Client::instance->setPropertyInt64(*this, value);
}
void Property::setValueDouble(double value)
{
Client::instance->setPropertyDouble(*this, value);
}
void Property::setValueString(const QString& value)
{
Client::instance->setPropertyString(*this, value);
}

Datei anzeigen

@ -0,0 +1,68 @@
/**
* client/src/network/property.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_NETWORK_PROPERTY_HPP
#define CLIENT_NETWORK_PROPERTY_HPP
#include "interfaceitem.hpp"
#include <QVariant>
#include <enum/propertytype.hpp>
//#include "objectptr.hpp"
class Object;
class Property : public InterfaceItem
{
Q_OBJECT
friend class Client;
protected:
const PropertyType m_type;
QVariant m_value;
public:
explicit Property(Object& object, const QString& name, PropertyType type, const QVariant& value);
PropertyType type() const { return m_type; }
bool toBool() const { return m_value.toBool(); }
int64_t toInt64() const { return m_value.toLongLong(); }
double toDouble() const { return m_value.toDouble(); }
QString toString() const { return m_value.toString(); }
QVariant toVariant() const { return m_value; }
signals:
void valueChanged();
void valueChangedBool(bool newValue);
void valueChangedInt64(int64_t newValue);
void valueChangedDouble(double newValue);
void valueChangedString(const QString& newValue);
public slots:
void setValueBool(bool value);
void setValueInt64(int64_t value);
void setValueDouble(double value);
void setValueString(const QString& value);
};
#endif

Datei anzeigen

@ -0,0 +1,94 @@
/**
* client/src/network/tablemodel.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "tablemodel.hpp"
#include "client.hpp"
TableModel::TableModel(Handle handle, const QString& classId, QObject* parent) :
QAbstractTableModel(parent),
m_handle{handle},
m_classId{classId},
m_rowCount{0}
{
}
TableModel::~TableModel()
{
Client::instance->releaseTableModel(this);
}
QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(role == Qt::DisplayRole)
{
if(orientation == Qt::Vertical)
return QString::number(1 + section);
else if(orientation == Qt::Horizontal)
return m_columnHeaders[section];
}
return QAbstractTableModel::headerData(section, orientation, role);
}
QVariant TableModel::data(const QModelIndex& index, int role) const
{
if(role == Qt::DisplayRole)
return m_texts.value(ColumnRow(index.column(), index.row()));
return QVariant{};
}
void TableModel::setRegion(int columnMin, int columnMax, int rowMin, int rowMax)
{
if(m_region.columnMin != columnMin ||
m_region.columnMax != columnMax ||
m_region.rowMin != rowMin ||
m_region.rowMax != rowMax)
{
m_region.columnMin = columnMin;
m_region.columnMax = columnMax;
m_region.rowMin = rowMin;
m_region.rowMax = rowMax;
Client::instance->setTableModelRegion(this, m_region.columnMin, m_region.columnMax, m_region.rowMin, m_region.rowMax);
}
}
void TableModel::setColumnHeaders(const QVector<QString>& values)
{
if(m_columnHeaders != values)
{
beginResetModel();
m_columnHeaders = values;
endResetModel();
}
}
void TableModel::setRowCount(int value)
{
if(m_rowCount != value)
{
beginResetModel();
m_rowCount = value;
endResetModel();
}
}

Datei anzeigen

@ -0,0 +1,69 @@
/**
* client/src/network/tablemodel.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_NETWORK_TABLEMODEL_HPP
#define CLIENT_NETWORK_TABLEMODEL_HPP
#include <QAbstractTableModel>
#include "handle.hpp"
class TableModel : public QAbstractTableModel
{
Q_OBJECT
friend class Client;
protected:
using ColumnRow = std::pair<uint32_t, uint32_t>;
Handle m_handle;
const QString m_classId;
QVector<QString> m_columnHeaders;
int m_rowCount;
struct Region
{
int rowMin = 0;
int rowMax = -1;
int columnMin = 0;
int columnMax = -1;
} m_region;
QMap<ColumnRow, QString> m_texts;
void setColumnHeaders(const QVector<QString>& values);
void setRowCount(int value);
public:
explicit TableModel(Handle handle, const QString& classId, QObject* parent = nullptr);
~TableModel() final;
Handle handle() const { return m_handle; }
int columnCount(const QModelIndex& parent = QModelIndex()) const final { Q_UNUSED(parent); return m_columnHeaders.size(); }
int rowCount(const QModelIndex& parent = QModelIndex()) const final { Q_UNUSED(parent); return m_rowCount; }
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const final;
QVariant data(const QModelIndex& index, int role) const final;
void setRegion(int columnMin, int columnMax, int rowMin, int rowMax);
};
#endif

Datei anzeigen

@ -0,0 +1,32 @@
/**
* client/src/network/tablemodelptr.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_NETWORK_TABLEMODELPTR_HPP
#define CLIENT_NETWORK_TABLEMODELPTR_HPP
#include <QSharedPointer>
class TableModel;
using TableModelPtr = QSharedPointer<TableModel>;
#endif

42
client/src/network/utils.cpp Normale Datei
Datei anzeigen

@ -0,0 +1,42 @@
/**
* client/src/network/utils.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "utils.hpp"
QString errorCodeToText(Message::ErrorCode ec)
{
using ErrorCode = Message::ErrorCode;
Q_ASSERT(ec != ErrorCode::None);
QString text;
switch(ec)
{
case ErrorCode::UnknownObject:
text = "Unknown object";
break;
case ErrorCode::None:
break; // silence warning
}
return QString("Error %1: %2").arg(static_cast<int>(ec)).arg(text);
}

30
client/src/network/utils.hpp Normale Datei
Datei anzeigen

@ -0,0 +1,30 @@
/**
* client/src/network/utils.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_NETWORK_UTILS_HPP
#define CLIENT_NETWORK_UTILS_HPP
#include <message.hpp>
QString errorCodeToText(Message::ErrorCode ec);
#endif // UTILS_HPP

Datei anzeigen

@ -0,0 +1,50 @@
/**
* client/src/subwindow/hardwarelistsubwindow.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "hardwarelistsubwindow.hpp"
#include <QSettings>
#include "../widget/hardwarewidget.hpp"
#define SETTING_PREFIX "hardwarelistsubwindow/"
#define SETTING_GEOMETRY SETTING_PREFIX "geometry"
#define SETTING_WINDOWSTATE SETTING_PREFIX "windowstate"
HardwareListSubWindow::HardwareListSubWindow(QWidget* parent) :
QMdiSubWindow(parent)
{
setWindowTitle(tr("Hardware"));
setWidget(new HardwareWidget(this));
QSettings settings;
if(settings.contains(SETTING_GEOMETRY))
restoreGeometry(settings.value(SETTING_GEOMETRY).toByteArray());
if(settings.contains(SETTING_WINDOWSTATE))
setWindowState(static_cast<Qt::WindowState>(settings.value(SETTING_WINDOWSTATE).toInt()));
}
void HardwareListSubWindow::closeEvent(QCloseEvent *event)
{
QSettings settings;
settings.setValue(SETTING_GEOMETRY, saveGeometry());
settings.setValue(SETTING_WINDOWSTATE, static_cast<int>(windowState()));
QMdiSubWindow::closeEvent(event);
}

Datei anzeigen

@ -0,0 +1,39 @@
/**
* client/src/subwindow/hardwarelistsubwindow.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_SUBWINDOW_HARDWARELISTSUBWINDOW_HPP
#define CLIENT_SUBWINDOW_HARDWARELISTSUBWINDOW_HPP
#include <QMdiSubWindow>
class HardwareListSubWindow : public QMdiSubWindow
{
Q_OBJECT
protected:
void closeEvent(QCloseEvent* event) final;
public:
HardwareListSubWindow(QWidget* parent = nullptr);
};
#endif

Datei anzeigen

@ -0,0 +1,31 @@
/**
* client/src/subwindow/serversettingssubwindow.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "serversettingssubwindow.hpp"
#include "../widget/serversettingswidget.hpp"
ServerSettingsSubWindow::ServerSettingsSubWindow(QWidget* parent) :
QMdiSubWindow(parent)
{
setWindowTitle(tr("Server settings"));
setWidget(new ServerSettingsWidget(this));
}

Datei anzeigen

@ -0,0 +1,40 @@
/**
* client/src/subwindow/serverconsolesubwindow.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_SUBWINDOW_SERVERCONSOLESUBWINDOW_HPP
#define CLIENT_SUBWINDOW_SERVERCONSOLESUBWINDOW_HPP
#include <QMdiSubWindow>
class ServerConsoleSubWindow : public QMdiSubWindow
{
Q_OBJECT
protected:
void closeEvent(QCloseEvent*) final;
public:
ServerConsoleSubWindow(QWidget* parent = nullptr);
//~ServerConsoleSubWindow() final;
};
#endif

Datei anzeigen

@ -0,0 +1,35 @@
/**
* client/src/subwindow/serverconsolesubwindow.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "serverconsolesubwindow.hpp"
#include "../widget/serverconsolewidget.hpp"
ServerConsoleSubWindow::ServerConsoleSubWindow(QWidget* parent) :
QMdiSubWindow(parent)
{
setWindowTitle(tr("Server console"));
setWidget(new ServerConsoleWidget(this));
}
void ServerConsoleSubWindow::closeEvent(QCloseEvent*)
{
}

Datei anzeigen

@ -0,0 +1,36 @@
/**
* client/src/subwindow/serverconsolesubwindow.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_SUBWINDOW_SERVERSETTINGSSUBWINDOW_HPP
#define CLIENT_SUBWINDOW_SERVERSETTINGSSUBWINDOW_HPP
#include <QMdiSubWindow>
class ServerSettingsSubWindow : public QMdiSubWindow
{
Q_OBJECT
public:
ServerSettingsSubWindow(QWidget* parent = nullptr);
};
#endif

Datei anzeigen

@ -0,0 +1,91 @@
/**
* client/src/widget/alertwidget.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "alertwidget.hpp"
#include <QPainter>
AlertWidget* AlertWidget::error(const QString& text, QWidget* parent)
{
AlertWidget* w = new AlertWidget(parent);
w->setType(Type::Error);
w->setText(text);
return w;
}
AlertWidget::AlertWidget(QWidget* parent) :
QWidget(parent),
m_type{Type::Error}
{
setMinimumWidth(100);
setMinimumHeight(qRound(fontMetrics().height() * 1.25));
updateColors();
}
void AlertWidget::setType(Type value)
{
if(m_type != value)
{
m_type = value;
updateColors();
repaint();
}
}
void AlertWidget::setText(const QString& value)
{
if(m_text != value)
{
m_text = value;
repaint();
}
}
void AlertWidget::paintEvent(QPaintEvent*)
{
const qreal radius = 7;
const int textMargin = 5;
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
QPainterPath path;
path.addRoundedRect(rect(), radius, radius);
painter.fillPath(path, m_backgroundColor);
painter.setPen(m_borderColor);
painter.drawPath(path);
painter.setPen(m_textColor);
painter.drawText(rect().adjusted(textMargin, 0, -textMargin, 0), Qt::AlignLeft | Qt::AlignVCenter, m_text);
}
void AlertWidget::updateColors()
{
switch(m_type)
{
case Type::Error:
m_borderColor = Qt::red;
m_backgroundColor = Qt::gray;
m_textColor = Qt::red;
break;
}
}

Datei anzeigen

@ -0,0 +1,60 @@
/**
* client/src/widget/alertwidget.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_WIDGET_ALERTWIDGET_HPP
#define CLIENT_WIDGET_ALERTWIDGET_HPP
#include <QWidget>
class AlertWidget : public QWidget
{
Q_OBJECT
public:
enum class Type
{
Error,
};
protected:
Type m_type;
QString m_text;
QColor m_backgroundColor;
QColor m_borderColor;
QColor m_textColor;
void paintEvent(QPaintEvent* event) final;
void updateColors();
public:
static AlertWidget* error(const QString& text, QWidget* parent = nullptr);
AlertWidget(QWidget* parent = nullptr);
Type type() const { return m_type; }
void setType(Type value);
const QString& text() const { return m_text; }
void setText(const QString& value);
};
#endif

Datei anzeigen

@ -0,0 +1,31 @@
/**
* client/src/widget/commandstationlistwidget.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "commandstationlistwidget.hpp"
CommandStationListWidget::CommandStationListWidget(const QString& id, QWidget* parent) :
ObjectListWidget(id, parent)
{
addActionAdd();
addActionEdit();
addActionDelete();
}

Datei anzeigen

@ -0,0 +1,36 @@
/**
* client/src/widget/commandstationlistwidget.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_WIDGET_COMMANDSTATIONLISTWIDGET_HPP
#define CLIENT_WIDGET_COMMANDSTATIONLISTWIDGET_HPP
#include "objectlistwidget.hpp"
class CommandStationListWidget : public ObjectListWidget
{
Q_OBJECT
public:
CommandStationListWidget(const QString& id, QWidget* parent = nullptr);
};
#endif

Datei anzeigen

@ -0,0 +1,28 @@
/**
* client/src/widget/decoderlistwidget.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "decoderlistwidget.hpp"
DecoderListWidget::DecoderListWidget(const QString& id, QWidget* parent) :
ObjectListWidget(id, parent)
{
}

Datei anzeigen

@ -0,0 +1,36 @@
/**
* client/src/widget/decoderlistwidget.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_WIDGET_DECODERLISTWIDGET_HPP
#define CLIENT_WIDGET_DECODERLISTWIDGET_HPP
#include "objectlistwidget.hpp"
class DecoderListWidget : public ObjectListWidget
{
Q_OBJECT
public:
DecoderListWidget(const QString& id, QWidget* parent = nullptr);
};
#endif

Datei anzeigen

@ -0,0 +1,32 @@
/**
* client/src/widget/hardwarewidget.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "hardwarewidget.hpp"
#include "commandstationlistwidget.hpp"
#include "decoderlistwidget.hpp"
HardwareWidget::HardwareWidget(QWidget* parent) :
QTabWidget(parent)
{
addTab(new CommandStationListWidget("command_station_list", this), tr("Command stations"));
addTab(new DecoderListWidget("decoder_list", this), tr("Decoders"));
}

Datei anzeigen

@ -0,0 +1,36 @@
/**
* client/src/widget/hardwarewidget.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_WIDGET_HARDWAREWIDGET_HPP
#define CLIENT_WIDGET_HARDWAREWIDGET_HPP
#include <QTabWidget>
class HardwareWidget : public QTabWidget
{
Q_OBJECT
public:
HardwareWidget(QWidget* parent = nullptr);
};
#endif

Datei anzeigen

@ -0,0 +1,76 @@
/**
* client/src/widget/objecteditwidget.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "objecteditwidget.hpp"
#include <QFormLayout>
#include <QtWaitingSpinner/waitingspinnerwidget.h>
#include "../network/client.hpp"
#include "../network/object.hpp"
#include "../network/property.hpp"
#include "../network/utils.hpp"
#include "../widget/alertwidget.hpp"
#include "../widget/propertycheckbox.hpp"
ObjectEditWidget::ObjectEditWidget(const QString& id, QWidget* parent) :
QWidget(parent),
m_id{id}
{
setLayout(new QFormLayout());
auto* spinner = new WaitingSpinnerWidget(this, true, false);
spinner->start();
m_requestId = Client::instance->getObject(m_id,
[this, spinner](const ObjectPtr& object, Message::ErrorCode ec)
{
m_requestId = Client::invalidRequestId;
if(object)
{
m_object = object;
buildForm();
}
else
static_cast<QFormLayout*>(this->layout())->addRow(AlertWidget::error(errorCodeToText(ec)));
delete spinner;
});
}
ObjectEditWidget::~ObjectEditWidget()
{
Client::instance->cancelRequest(m_requestId);
}
void ObjectEditWidget::buildForm()
{
QFormLayout* l = static_cast<QFormLayout*>(this->layout());
for(auto item : m_object->interfaceItems())
if(Property* property = dynamic_cast<Property*>(item))
{
QWidget* w = nullptr;
if(property->type() == PropertyType::Boolean)
w = new PropertyCheckBox(*property);
l->addRow(property->displayName(), w);
}
}

Datei anzeigen

@ -0,0 +1,45 @@
/**
* client/src/widget/objecteditwidget.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_WIDGET_OBJECTEDITWIDGET_HPP
#define CLIENT_WIDGET_OBJECTEDITWIDGET_HPP
#include <QWidget>
#include "../network/objectptr.hpp"
class ObjectEditWidget : public QWidget
{
Q_OBJECT
protected:
const QString m_id;
int m_requestId;
ObjectPtr m_object;
virtual void buildForm();
public:
explicit ObjectEditWidget(const QString& id, QWidget* parent = nullptr);
~ObjectEditWidget() override;
};
#endif

Datei anzeigen

@ -0,0 +1,109 @@
/**
* client/src/widget/objectlistwidget.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "objectlistwidget.hpp"
#include <QVBoxLayout>
#include <QToolBar>
#include <QTableView>
#include <QtWaitingSpinner/waitingspinnerwidget.h>
#include "tablewidget.hpp"
#include "../network/client.hpp"
//#include "../network/object.hpp"
#include "../network/tablemodel.hpp"
#include "../network/utils.hpp"
#include "../widget/alertwidget.hpp"
ObjectListWidget::ObjectListWidget(const QString& id, QWidget* parent) :
QWidget(parent),
m_id{id},
m_toolbar{new QToolBar()},
m_actionAdd{nullptr},
m_actionEdit{nullptr},
m_actionDelete{nullptr},
m_tableWidget{new TableWidget()}
{
QVBoxLayout* layout = new QVBoxLayout();
layout->setMargin(0);
layout->addWidget(m_toolbar);
layout->addWidget(m_tableWidget);
setLayout(layout);
auto* spinner = new WaitingSpinnerWidget(this, true, false);
spinner->start();
m_requestId = Client::instance->getObject(m_id,
[this, spinner](const ObjectPtr& object, Message::ErrorCode ec)
{
m_requestId = Client::invalidRequestId;
if(object)
{
m_object = object;
m_requestId = Client::instance->getTableModel(m_id,
[this, spinner](const TableModelPtr& tableModel, Message::ErrorCode ec)
{
if(tableModel)
{
m_requestId = Client::invalidRequestId;
m_tableWidget->setTableModel(tableModel);
delete spinner;
}
else
static_cast<QVBoxLayout*>(this->layout())->insertWidget(0, AlertWidget::error(errorCodeToText(ec)));
});
}
else
static_cast<QVBoxLayout*>(this->layout())->insertWidget(0, AlertWidget::error(errorCodeToText(ec)));
});
}
ObjectListWidget::~ObjectListWidget()
{
Client::instance->cancelRequest(m_requestId);
}
void ObjectListWidget::addActionAdd()
{
Q_ASSERT(!m_actionAdd);
m_actionAdd = m_toolbar->addAction(tr("Add"));
}
void ObjectListWidget::addActionEdit()
{
Q_ASSERT(!m_actionEdit);
m_actionEdit = m_toolbar->addAction(tr("Edit"));
m_actionEdit->setEnabled(false);
}
void ObjectListWidget::addActionDelete()
{
Q_ASSERT(!m_actionDelete);
m_actionDelete = m_toolbar->addAction(tr("Delete"));
m_actionDelete->setEnabled(false);
}

Datei anzeigen

@ -0,0 +1,55 @@
/**
* client/src/widget/objectlistwidget.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_WIDGET_OBJECTLISTWIDGET_HPP
#define CLIENT_WIDGET_OBJECTLISTWIDGET_HPP
#include <QWidget>
#include "../network/objectptr.hpp"
class QToolBar;
class TableWidget;
class ObjectListWidget : public QWidget
{
Q_OBJECT
protected:
const QString m_id;
int m_requestId;
ObjectPtr m_object;
QToolBar* m_toolbar;
QAction* m_actionAdd;
QAction* m_actionEdit;
QAction* m_actionDelete;
TableWidget* m_tableWidget;
void addActionAdd();
void addActionEdit();
void addActionDelete();
public:
explicit ObjectListWidget(const QString& id, QWidget* parent = nullptr);
~ObjectListWidget() override;
};
#endif

Datei anzeigen

@ -0,0 +1,34 @@
/**
* client/src/widget/propertycheckbox.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "propertycheckbox.hpp"
#include "../network/property.hpp"
PropertyCheckBox::PropertyCheckBox(Property& property, QWidget* parent) :
QCheckBox(parent),
m_property{property}
{
Q_ASSERT(m_property.type() == PropertyType::Boolean);
setChecked(m_property.toBool());
connect(&m_property, &Property::valueChangedBool, this, &PropertyCheckBox::setChecked);
connect(this, &PropertyCheckBox::toggled, &m_property, &Property::setValueBool);
}

Datei anzeigen

@ -0,0 +1,39 @@
/**
* client/src/widget/propertycheckbox.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_WIDGET_PROPERTYCHECKBOX_HPP
#define CLIENT_WIDGET_PROPERTYCHECKBOX_HPP
#include <QCheckBox>
class Property;
class PropertyCheckBox : public QCheckBox
{
protected:
Property& m_property;
public:
PropertyCheckBox(Property& property, QWidget* parent = nullptr);
};
#endif

Datei anzeigen

@ -0,0 +1,79 @@
/**
* client/src/widget/serverconsolewidget.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "serverconsolewidget.hpp"
#include <QVBoxLayout>
//#include <QToolBar>
#include <QTableView>
#include <QtWaitingSpinner/waitingspinnerwidget.h>
#include "tablewidget.hpp"
#include "../network/client.hpp"
//#include "../network/object.hpp"
#include "../network/tablemodel.hpp"
#include "../network/utils.hpp"
#include "../widget/alertwidget.hpp"
ServerConsoleWidget::ServerConsoleWidget(QWidget* parent) :
QWidget(parent),
m_tableWidget{new TableWidget()}
{
QVBoxLayout* layout = new QVBoxLayout();
layout->setMargin(0);
//layout->addWidget(new QToolBar());
layout->addWidget(m_tableWidget);
setLayout(layout);
auto* spinner = new WaitingSpinnerWidget(this, true, false);
spinner->start();
m_requestId = Client::instance->getObject(objectId,
[this, spinner](const ObjectPtr& object, Message::ErrorCode ec)
{
m_requestId = Client::invalidRequestId;
if(object)
{
m_object = object;
m_requestId = Client::instance->getTableModel(objectId,
[this, spinner](const TableModelPtr& tableModel, Message::ErrorCode ec)
{
if(tableModel)
{
m_requestId = Client::invalidRequestId;
m_tableWidget->setTableModel(tableModel);
delete spinner;
}
else
static_cast<QVBoxLayout*>(this->layout())->insertWidget(0, AlertWidget::error(errorCodeToText(ec)));
});
}
else
static_cast<QVBoxLayout*>(this->layout())->insertWidget(0, AlertWidget::error(errorCodeToText(ec)));
});
}
ServerConsoleWidget::~ServerConsoleWidget()
{
Client::instance->cancelRequest(m_requestId);
}

Datei anzeigen

@ -0,0 +1,46 @@
/**
* client/src/widget/serverconsolewidget.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_WIDGET_SERVERCONSOLEWIDGET_HPP
#define CLIENT_WIDGET_SERVERCONSOLEWIDGET_HPP
#include <QWidget>
#include "../network/objectptr.hpp"
#include "../network/tablemodelptr.hpp"
class TableWidget;
class ServerConsoleWidget : public QWidget
{
protected:
static constexpr char const* objectId = "console";
int m_requestId;
ObjectPtr m_object;
TableWidget* m_tableWidget;
public:
ServerConsoleWidget(QWidget* parent = nullptr);
~ServerConsoleWidget() final;
};
#endif

Datei anzeigen

@ -0,0 +1,28 @@
/**
* client/src/widget/serversettingswidget.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "serversettingswidget.hpp"
ServerSettingsWidget::ServerSettingsWidget(QWidget* parent) :
ObjectEditWidget("settings", parent)
{
}

Datei anzeigen

@ -0,0 +1,36 @@
/**
* client/src/widget/serversettingswidget.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_WIDGET_SERVERSETTINGSWIDGET_HPP
#define CLIENT_WIDGET_SERVERSETTINGSWIDGET_HPP
#include "objecteditwidget.hpp"
class ServerSettingsWidget : public ObjectEditWidget
{
Q_OBJECT
public:
ServerSettingsWidget(QWidget* parent = nullptr);
};
#endif

Datei anzeigen

@ -0,0 +1,79 @@
/**
* client/src/widget/tablewidget.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "tablewidget.hpp"
#include <QHeaderView>
#include <QScrollBar>
#include "../network/tablemodel.hpp"
TableWidget::TableWidget(QWidget* parent) :
QTableView(parent)
{
setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
setWordWrap(false);
verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
verticalHeader()->setDefaultSectionSize(fontMetrics().height());
horizontalHeader()->setDefaultSectionSize(fontMetrics().height());
}
void TableWidget::setTableModel(const TableModelPtr& model)
{
Q_ASSERT(!m_model);
m_model = model;
setModel(m_model.data());
connect(m_model.data(), &TableModel::modelReset, this, &TableWidget::updateRegion);
connect(horizontalScrollBar(), &QScrollBar::rangeChanged, this, &TableWidget::updateRegion);
connect(horizontalScrollBar(), &QScrollBar::valueChanged, this, &TableWidget::updateRegion);
connect(verticalScrollBar(), &QScrollBar::rangeChanged, this, &TableWidget::updateRegion);
connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &TableWidget::updateRegion);
updateRegion();
}
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)
rowMax = rowCount - 1;
else
rowMax = qMin(rowMax + 1, rowCount - 1);
int columnMin = qMax(topLeft.column(), 0);
int columnMax = indexAt(r.topRight()).column();
if(columnMax == -1)
columnMax = columnCount - 1;
else
columnMax = qMin(columnMax + 1, columnCount - 1);
m_model->setRegion(columnMin, columnMax, rowMin, rowMax);
}

Datei anzeigen

@ -0,0 +1,45 @@
/**
* client/src/widget/tablewidget.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 CLIENT_WIDGET_TABLEWIDGET_HPP
#define CLIENT_WIDGET_TABLEWIDGET_HPP
#include <QTableView>
#include "../network/tablemodelptr.hpp"
class TableWidget : public QTableView
{
Q_OBJECT
protected:
TableModelPtr m_model;
protected slots:
void updateRegion();
public:
TableWidget(QWidget* parent = nullptr);
void setTableModel(const TableModelPtr& model);
};
#endif

Datei anzeigen

@ -0,0 +1,277 @@
/* Original Work Copyright (c) 2012-2014 Alexander Turkin
Modified 2014 by William Hallatt
Modified 2015 by Jacob Dawid
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// Own includes
#include "waitingspinnerwidget.h"
// Standard includes
#include <cmath>
#include <algorithm>
// Qt includes
#include <QPainter>
#include <QTimer>
WaitingSpinnerWidget::WaitingSpinnerWidget(QWidget *parent,
bool centerOnParent,
bool disableParentWhenSpinning)
: QWidget(parent),
_centerOnParent(centerOnParent),
_disableParentWhenSpinning(disableParentWhenSpinning) {
initialize();
}
WaitingSpinnerWidget::WaitingSpinnerWidget(Qt::WindowModality modality,
QWidget *parent,
bool centerOnParent,
bool disableParentWhenSpinning)
: QWidget(parent, Qt::Dialog | Qt::FramelessWindowHint),
_centerOnParent(centerOnParent),
_disableParentWhenSpinning(disableParentWhenSpinning){
initialize();
// We need to set the window modality AFTER we've hidden the
// widget for the first time since changing this property while
// the widget is visible has no effect.
setWindowModality(modality);
setAttribute(Qt::WA_TranslucentBackground);
}
void WaitingSpinnerWidget::initialize() {
_color = Qt::black;
_roundness = 100.0;
_minimumTrailOpacity = 3.14159265358979323846;
_trailFadePercentage = 80.0;
_revolutionsPerSecond = 1.57079632679489661923;
_numberOfLines = 20;
_lineLength = 10;
_lineWidth = 2;
_innerRadius = 10;
_currentCounter = 0;
_isSpinning = false;
_timer = new QTimer(this);
connect(_timer, SIGNAL(timeout()), this, SLOT(rotate()));
updateSize();
updateTimer();
hide();
}
void WaitingSpinnerWidget::paintEvent(QPaintEvent *) {
updatePosition();
QPainter painter(this);
painter.fillRect(this->rect(), Qt::transparent);
painter.setRenderHint(QPainter::Antialiasing, true);
if (_currentCounter >= _numberOfLines) {
_currentCounter = 0;
}
painter.setPen(Qt::NoPen);
for (int i = 0; i < _numberOfLines; ++i) {
painter.save();
painter.translate(_innerRadius + _lineLength,
_innerRadius + _lineLength);
qreal rotateAngle =
static_cast<qreal>(360 * i) / static_cast<qreal>(_numberOfLines);
painter.rotate(rotateAngle);
painter.translate(_innerRadius, 0);
int distance =
lineCountDistanceFromPrimary(i, _currentCounter, _numberOfLines);
QColor color =
currentLineColor(distance, _numberOfLines, _trailFadePercentage,
_minimumTrailOpacity, _color);
painter.setBrush(color);
// TODO improve the way rounded rect is painted
painter.drawRoundedRect(
QRect(0, -_lineWidth / 2, _lineLength, _lineWidth), _roundness,
_roundness, Qt::RelativeSize);
painter.restore();
}
}
void WaitingSpinnerWidget::start() {
updatePosition();
_isSpinning = true;
show();
if(parentWidget() && _disableParentWhenSpinning) {
parentWidget()->setEnabled(false);
}
if (!_timer->isActive()) {
_timer->start();
_currentCounter = 0;
}
}
void WaitingSpinnerWidget::stop() {
_isSpinning = false;
hide();
if(parentWidget() && _disableParentWhenSpinning) {
parentWidget()->setEnabled(true);
}
if (_timer->isActive()) {
_timer->stop();
_currentCounter = 0;
}
}
void WaitingSpinnerWidget::setNumberOfLines(int lines) {
_numberOfLines = lines;
_currentCounter = 0;
updateTimer();
}
void WaitingSpinnerWidget::setLineLength(int length) {
_lineLength = length;
updateSize();
}
void WaitingSpinnerWidget::setLineWidth(int width) {
_lineWidth = width;
updateSize();
}
void WaitingSpinnerWidget::setInnerRadius(int radius) {
_innerRadius = radius;
updateSize();
}
QColor WaitingSpinnerWidget::color() {
return _color;
}
qreal WaitingSpinnerWidget::roundness() {
return _roundness;
}
qreal WaitingSpinnerWidget::minimumTrailOpacity() {
return _minimumTrailOpacity;
}
qreal WaitingSpinnerWidget::trailFadePercentage() {
return _trailFadePercentage;
}
qreal WaitingSpinnerWidget::revolutionsPersSecond() {
return _revolutionsPerSecond;
}
int WaitingSpinnerWidget::numberOfLines() {
return _numberOfLines;
}
int WaitingSpinnerWidget::lineLength() {
return _lineLength;
}
int WaitingSpinnerWidget::lineWidth() {
return _lineWidth;
}
int WaitingSpinnerWidget::innerRadius() {
return _innerRadius;
}
bool WaitingSpinnerWidget::isSpinning() const {
return _isSpinning;
}
void WaitingSpinnerWidget::setRoundness(qreal roundness) {
_roundness = std::max(0.0, std::min(100.0, roundness));
}
void WaitingSpinnerWidget::setColor(QColor color) {
_color = color;
}
void WaitingSpinnerWidget::setRevolutionsPerSecond(qreal revolutionsPerSecond) {
_revolutionsPerSecond = revolutionsPerSecond;
updateTimer();
}
void WaitingSpinnerWidget::setTrailFadePercentage(qreal trail) {
_trailFadePercentage = trail;
}
void WaitingSpinnerWidget::setMinimumTrailOpacity(qreal minimumTrailOpacity) {
_minimumTrailOpacity = minimumTrailOpacity;
}
void WaitingSpinnerWidget::rotate() {
++_currentCounter;
if (_currentCounter >= _numberOfLines) {
_currentCounter = 0;
}
update();
}
void WaitingSpinnerWidget::updateSize() {
int size = (_innerRadius + _lineLength) * 2;
setFixedSize(size, size);
}
void WaitingSpinnerWidget::updateTimer() {
_timer->setInterval(1000 / (_numberOfLines * _revolutionsPerSecond));
}
void WaitingSpinnerWidget::updatePosition() {
if (parentWidget() && _centerOnParent) {
move(parentWidget()->width() / 2 - width() / 2,
parentWidget()->height() / 2 - height() / 2);
}
}
int WaitingSpinnerWidget::lineCountDistanceFromPrimary(int current, int primary,
int totalNrOfLines) {
int distance = primary - current;
if (distance < 0) {
distance += totalNrOfLines;
}
return distance;
}
QColor WaitingSpinnerWidget::currentLineColor(int countDistance, int totalNrOfLines,
qreal trailFadePerc, qreal minOpacity,
QColor color) {
if (countDistance == 0) {
return color;
}
const qreal minAlphaF = minOpacity / 100.0;
int distanceThreshold =
static_cast<int>(ceil((totalNrOfLines - 1) * trailFadePerc / 100.0));
if (countDistance > distanceThreshold) {
color.setAlphaF(minAlphaF);
} else {
qreal alphaDiff = color.alphaF() - minAlphaF;
qreal gradient = alphaDiff / static_cast<qreal>(distanceThreshold + 1);
qreal resultAlpha = color.alphaF() - gradient * countDistance;
// If alpha is out of bounds, clip it.
resultAlpha = std::min(1.0, std::max(0.0, resultAlpha));
color.setAlphaF(resultAlpha);
}
return color;
}

Datei anzeigen

@ -0,0 +1,114 @@
/* Original Work Copyright (c) 2012-2014 Alexander Turkin
Modified 2014 by William Hallatt
Modified 2015 by Jacob Dawid
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
// Qt includes
#include <QWidget>
#include <QTimer>
#include <QColor>
class WaitingSpinnerWidget : public QWidget {
Q_OBJECT
public:
/*! Constructor for "standard" widget behaviour - use this
* constructor if you wish to, e.g. embed your widget in another. */
WaitingSpinnerWidget(QWidget *parent = nullptr,
bool centerOnParent = true,
bool disableParentWhenSpinning = true);
/*! Constructor - use this constructor to automatically create a modal
* ("blocking") spinner on top of the calling widget/window. If a valid
* parent widget is provided, "centreOnParent" will ensure that
* QtWaitingSpinner automatically centres itself on it, if not,
* "centreOnParent" is ignored. */
WaitingSpinnerWidget(Qt::WindowModality modality,
QWidget *parent = nullptr,
bool centerOnParent = true,
bool disableParentWhenSpinning = true);
public slots:
void start();
void stop();
public:
void setColor(QColor color);
void setRoundness(qreal roundness);
void setMinimumTrailOpacity(qreal minimumTrailOpacity);
void setTrailFadePercentage(qreal trail);
void setRevolutionsPerSecond(qreal revolutionsPerSecond);
void setNumberOfLines(int lines);
void setLineLength(int length);
void setLineWidth(int width);
void setInnerRadius(int radius);
void setText(QString text);
QColor color();
qreal roundness();
qreal minimumTrailOpacity();
qreal trailFadePercentage();
qreal revolutionsPersSecond();
int numberOfLines();
int lineLength();
int lineWidth();
int innerRadius();
bool isSpinning() const;
private slots:
void rotate();
protected:
void paintEvent(QPaintEvent *paintEvent);
private:
static int lineCountDistanceFromPrimary(int current, int primary,
int totalNrOfLines);
static QColor currentLineColor(int distance, int totalNrOfLines,
qreal trailFadePerc, qreal minOpacity,
QColor color);
void initialize();
void updateSize();
void updateTimer();
void updatePosition();
private:
QColor _color;
qreal _roundness; // 0..100
qreal _minimumTrailOpacity;
qreal _trailFadePercentage;
qreal _revolutionsPerSecond;
int _numberOfLines;
int _lineLength;
int _lineWidth;
int _innerRadius;
private:
WaitingSpinnerWidget(const WaitingSpinnerWidget&);
WaitingSpinnerWidget& operator=(const WaitingSpinnerWidget&);
QTimer *_timer;
bool _centerOnParent;
bool _disableParentWhenSpinning;
int _currentCounter;
bool _isSpinning;
};

Datei anzeigen

@ -0,0 +1,74 @@
QT += core gui widgets network
TARGET = traintastic-client
TEMPLATE = app
DEFINES += QT_DEPRECATED_WARNINGS
greaterThan(QT_MINOR_VERSION, 11): { # >= 5.12
CONFIG += c++17
} else {
QMAKE_CXXFLAGS += -std=c++17
}
INCLUDEPATH += \
../shared/src \
thirdparty
SOURCES += \
src/main.cpp \
src/mainwindow.cpp \
src/dialog/connectdialog.cpp \
src/subwindow/hardwarelistsubwindow.cpp \
src/network/client.cpp \
src/network/object.cpp \
src/subwindow/serverconsolesubwindow.cpp \
thirdparty/QtWaitingSpinner/waitingspinnerwidget.cpp \
src/widget/alertwidget.cpp \
src/network/utils.cpp \
src/network/tablemodel.cpp \
src/widget/serverconsolewidget.cpp \
src/widget/tablewidget.cpp \
src/widget/objectlistwidget.cpp \
src/widget/hardwarewidget.cpp \
src/widget/decoderlistwidget.cpp \
src/widget/commandstationlistwidget.cpp \
src/widget/objecteditwidget.cpp \
src/widget/serversettingswidget.cpp \
src/subwindow/serversettingssubwindow.cpp \
src/network/property.cpp \
src/network/interfaceitems.cpp \
src/network/interfaceitem.cpp \
src/widget/propertycheckbox.cpp
HEADERS += \
src/mainwindow.hpp \
src/dialog/connectdialog.hpp \
src/subwindow/hardwarelistsubwindow.hpp \
src/network/client.hpp \
src/network/object.hpp \
../shared/src/message.hpp \
src/subwindow/serverconsolesubwindow.hpp \
thirdparty/QtWaitingSpinner/waitingspinnerwidget.h \
src/widget/alertwidget.hpp \
src/network/utils.hpp \
src/network/tablemodel.hpp \
src/network/tablemodelptr.hpp \
src/network/handle.hpp \
src/network/objectptr.hpp \
src/widget/serverconsolewidget.hpp \
src/widget/tablewidget.hpp \
src/widget/objectlistwidget.hpp \
src/widget/commandstationlistwidget.hpp \
src/widget/hardwarewidget.hpp \
src/widget/decoderlistwidget.hpp \
src/widget/objecteditwidget.hpp \
src/widget/serversettingswidget.hpp \
src/subwindow/serversettingssubwindow.hpp \
src/network/property.hpp \
src/network/interfaceitems.hpp \
src/network/interfaceitem.hpp \
src/widget/propertycheckbox.hpp
RESOURCES += \
dark.qrc

8
lang/en-us.txt Normale Datei
Datei anzeigen

@ -0,0 +1,8 @@
## Traintastic language file: English (US)
language:en-us=English
languaga:nl-nl=Dutch
settings:localhost_only=Localhost only
settings:port=Port
#settings:default_world=

7
lang/nl-nl.txt Normale Datei
Datei anzeigen

@ -0,0 +1,7 @@
## Traintastic language file: Dutch (NL)
language:en-us=Engels
language:nl-nl=Nederlands
settings:localhost_only=Alleen localhost
settings:port=Poort

50
server/CMakeLists.txt Normale Datei
Datei anzeigen

@ -0,0 +1,50 @@
cmake_minimum_required(VERSION 3.9)
project(traintastic-server VERSION 0.1.0 DESCRIPTION "Traintastic server")
include(GNUInstallDirs)
#find_package(PkgConfig REQUIRED)
find_package(Lua 5.3 REQUIRED)
#pkg_check_modules(USBXPRESSNET REQUIRED IMPORTED_TARGET usbxpressnet)
find_path(USBXPRESSNET_INCLUDE_DIR usbxpressnet.h)
#message(${USBXPRESSNET_INCLUDE_DIR})
find_library(USBXPRESSNET_LIB usbxpressnet)
#message(${USBXPRESSNET_LIB})
#if(NOT USBXPRESSNET_LIB)
# message(FATAL_ERROR "usbxpressnet library not found")
#endif()
file(GLOB SOURCES
"src/*.cpp"
"src/core/*.cpp"
"src/hardware/commandstation/*.cpp"
"src/hardware/commandstation/protocol/*.cpp"
"src/hardware/decoder/*.cpp"
"src/lua/*.cpp"
"thirdparty/boost/libs/program_options/src/*.cpp")
add_executable(traintastic-server ${SOURCES})
add_definitions(
-DVERSION=${PROJECT_VERSION}
-DBOOST_ALL_NO_LIB
-DBOOST_ERROR_CODE_HEADER_ONLY
-DBOOST_CHRONO_HEADER_ONLY
-DBOOST_ASIO_HEADER_ONLY
-DBOOST_SYSTEM_NO_DEPRECATED)
target_include_directories(traintastic-server PRIVATE
../shared/src/
thirdparty
thirdparty/boost
${LUA_INCLUDE_DIR}
${USBXPRESSNET_INCLUDE_DIR})
target_link_libraries(traintastic-server
pthread
stdc++fs
${LUA_LIBRARIES}
${USBXPRESSNET_LIB})
# PkgConfig::USBXPRESSNET)
set_target_properties(traintastic-server PROPERTIES CXX_STANDARD 17)

119
server/FindLua.cmake Normale Datei
Datei anzeigen

@ -0,0 +1,119 @@
# 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
#
# 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 "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/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/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

@ -0,0 +1,33 @@
/**
* server/src/core/abstractproperty.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "abstractproperty.hpp"
#include "object.hpp"
#include <iostream>
void AbstractProperty::changed()
{
std::cout << "AbstractProperty::changed" << std::endl;
m_object.propertyChanged(m_object, *this);
}

Datei anzeigen

@ -0,0 +1,139 @@
/**
* server/src/core/abstractproperty.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_ABSTRACTPROPERTY_HPP
#define SERVER_CORE_ABSTRACTPROPERTY_HPP
#include "interfaceitem.hpp"
#include <enum/propertytype.hpp>
#include "propertyflags.hpp"
#include "objectptr.hpp"
#include <cassert>
#include <nlohmann/json.hpp>
class AbstractProperty : public InterfaceItem
{
protected:
const PropertyType m_type;
PropertyFlags m_flags;
void changed();
public:
AbstractProperty(Object& object, const std::string& name, PropertyType type, PropertyFlags flags) :
InterfaceItem{object, name},
m_type{type},
m_flags{flags}
{
assert(type != PropertyType::Invalid);
assert(is_access_valid(flags) && is_store_valid(flags));
}
bool isConstant() const
{
return false;//!is_empty(m_flags & PropertyFlags::Constant);
}
bool isReadable() const
{
return true;//!is_empty(m_flags & (PropertyFlags::Constant | PropertyFlags::ReadOnly));
}
bool isWriteable() const
{
return true;//!is_empty(m_flags & PropertyFlags::WriteOnly);
}
PropertyType type() const
{
return m_type;
}
virtual bool toBool() const = 0;
virtual int64_t toInt64() const = 0;
virtual double toDouble() const = 0;
virtual std::string toString() const = 0;
virtual ObjectPtr toObject() const = 0;
virtual void fromBool(bool value) = 0;
virtual void fromInt64(int64_t value) = 0;
virtual void fromDouble(double value) = 0;
virtual void fromString(const std::string& value) = 0;
virtual void fromObject(const ObjectPtr& value) = 0;
nlohmann::json toJSON() const
{
switch(type())
{
case PropertyType::Boolean:
return toBool();
case PropertyType::Integer:
return toInt64();
case PropertyType::Float:
return toDouble();
case PropertyType::String:
return toString();
// TODO: case nlohmann::json::value_t::object:
// TODO: case nlohmann::json::value_t::array:
default:
throw std::runtime_error("unsupported type");
}
}
void fromJSON(const nlohmann::json& value)
{
switch(value.type())
{
// TODO: case nlohmann::json::value_t::null:
case nlohmann::json::value_t::boolean:
fromBool(value);
break;
case nlohmann::json::value_t::number_integer:
case nlohmann::json::value_t::number_unsigned:
fromInt64(value);
break;
case nlohmann::json::value_t::number_float:
fromDouble(value);
break;
case nlohmann::json::value_t::string:
fromString(value);
break;
// TODO: case nlohmann::json::value_t::object:
// TODO: case nlohmann::json::value_t::array:
default:
throw std::runtime_error("unsupported JSON type");
}
}
};
#endif

281
server/src/core/client.cpp Normale Datei
Datei anzeigen

@ -0,0 +1,281 @@
/**
* server/src/core/client.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "client.hpp"
//#include "console.hpp"
//#include "objectregistry.hpp"
#include "traintastic.hpp"
#include "eventloop.hpp"
#include "session.hpp"
Client::Client(Traintastic& server, const std::string& id, boost::asio::ip::tcp::socket socket) :
m_server{server},
m_socket(std::move(socket)),
m_id{id},
m_authenticated{false}//,
//m_lastObjectHandle{0}
{
m_socket.set_option(boost::asio::socket_base::linger(true, 0));
m_socket.set_option(boost::asio::ip::tcp::no_delay(true));
Traintastic::instance->console->info(id, "Connected");
}
Client::~Client()
{
try
{
m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
}
catch(...)
{
}
m_socket.close();
}
void Client::start()
{
doReadHeader();
}
void Client::doReadHeader()
{
auto self(shared_from_this());
boost::asio::async_read(m_socket,
boost::asio::buffer(&m_readBuffer.header, sizeof(m_readBuffer.header)),
[this, self](const boost::system::error_code& e, std::size_t)
{
if(!e)
{
m_readBuffer.message.reset(new Message(m_readBuffer.header));
if(m_readBuffer.message->dataSize() == 0)
{
if(m_readBuffer.message->command() != Message::Command::Ping)
EventLoop::call(&Client::processMessage, this, m_readBuffer.message);
else
; // TODO: ping hier replyen
m_readBuffer.message.reset();
doReadHeader();
}
else
doReadData();
}
else if(e == boost::asio::error::eof || e == boost::asio::error::connection_aborted || e == boost::asio::error::connection_reset)
connectionLost();
else
connectionError("readheader", e.message());
});
}
void Client::doReadData()
{
auto self(shared_from_this());
boost::asio::async_read(m_socket,
boost::asio::buffer(m_readBuffer.message->data(), m_readBuffer.message->dataSize()),
[this, self](const boost::system::error_code& ec, std::size_t)
{
if(!ec)
{
if(m_readBuffer.message->command() != Message::Command::Ping)
EventLoop::call(&Client::processMessage, this, m_readBuffer.message);
else
; // TODO: ping hier replyen
m_readBuffer.message.reset();
doReadHeader();
}
else if(ec == boost::asio::error::eof || ec == boost::asio::error::connection_aborted || ec == boost::asio::error::connection_reset)
connectionLost();
else
connectionError("readdata", ec.message());
});
}
void Client::doWrite()
{
auto self(shared_from_this());
boost::asio::async_write(m_socket, boost::asio::buffer(**m_writeQueue.front(), m_writeQueue.front()->size()),
[this, self](const boost::system::error_code& ec, std::size_t)
{
if(!ec)
{
m_writeQueue.pop();
if(!m_writeQueue.empty())
doWrite();
}
else
connectionError("write", ec.message());
});
}
void Client::processMessage(const std::shared_ptr<Message> message)
{
if(m_authenticated && m_session)
{
if(m_session->processMessage(*message))
return;
}
else if(m_authenticated && !m_session)
{
if(message->command() == Message::Command::NewSession && message->type() == Message::Type::Request)
{
m_session = std::make_shared<Session>(shared_from_this());
Traintastic::instance->console->notice(m_id, "Created new session");
auto response = Message::newResponse(message->command(), message->requestId());
response->write(m_session->uuid());
sendMessage(std::move(response));
return;
}
}
else
{
if(message->command() == Message::Command::Login && message->type() == Message::Type::Request)
{
m_authenticated = true; // oke for now, login can be added later :)
Traintastic::instance->console->notice(m_id, "Logged in anonymously");
sendMessage(Message::newResponse(message->command(), message->requestId()));
return;
}
}
if(message->type() == Message::Type::Request)
{
Traintastic::instance->console->debug(m_id, "Received invalid command: " + std::to_string(static_cast<uint8_t>(message->command())));
usleep(10*1000*1000);
sendMessage(Message::newErrorResponse(message->command(), message->requestId(), Message::ErrorCode::InvalidCommand));
}
/*
switch(message.command())
{
case Message::Command::None:
break;
case Message::Command::CreateObject:
if(message.isRequest())
{
const std::string id = message.read();
m_server.console->debug(m_id, std::string("CreateObject: id=") + id);
//std::unique_ptr<Message> response = Message::newResponse(message.command(), message.requestId());
//response->add(ObjectRegistry::instance->exists(id));
//sendMessage(std::move(response));
}
break;
case Message::Command::IsObject:
if(message.isRequest())
{
const std::string id = message.read();
m_server.console->debug(m_id, std::string("IsObject: id=") + id);
std::unique_ptr<Message> response = Message::newResponse(message.command(), message.requestId());
response->add(ObjectRegistry::instance->exists(id));
sendMessage(std::move(response));
}
break;
case Message::Command::GetObject:
if(message.isRequest())
{
std::string id = message.read();
m_server.console->debug(m_id, std::string("GetObject: id=") + id);
ObjectPtr p = ObjectRegistry::instance->get(id);
if(p)
{
ObjectHandle handle = 0;//subscribeObject(p);
std::unique_ptr<Message> response = Message::newResponse(message.command(), message.requestId());
response->add(handle);
sendMessage(std::move(response));
}
else
sendMessage(Message::newErrorResponse(message.command(), message.requestId(), Message::ErrorCode::UnknownObjectId, std::string("No object exists with id `") + id + "`"));
}
break;
case Message::Command::ReleaseObject:
if(message.isRequest())
{
ObjectHandle handle = message.read<ObjectHandle>();
m_server.console->debug(m_id, std::string("ReleaseObject: handle=") + std::to_string(handle));
if(false)//unsubscribeObject(handle))
sendMessage(std::move(Message::newResponse(message.command(), message.requestId())));
else
sendMessage(std::move(Message::newErrorResponse(message.command(), message.requestId(), Message::ErrorCode::InvalidHandle)));
}
break;
case Message::Command::SetProperty:
if(message.isRequest())
{
ObjectHandle handle = message.read<ObjectHandle>();
std::string name = message.read();
m_server.console->debug(m_id, std::string("SetProperty: handle=") + std::to_string(handle) + ", name=" + name);
ObjectPtr object;// = getObject(handle);
AbstractProperty* property;
//if(!object)
// sendMessage(std::move(Message::newErrorResponse(message.command(), message.requestId(), Message::ErrorCode::InvalidHandle)));
//else if(!(property = object->getProperty(name)))
// sendMessage(std::move(Message::newErrorResponse(message.command(), message.requestId(), Message::ErrorCode::InvalidProperty)));
//else if(!readPropertyValue(message, property))
sendMessage(std::move(Message::newErrorResponse(message.command(), message.requestId(), Message::ErrorCode::InvalidValue)));
}
break;
case Message::Command::PropertyChanged:
assert(false);
break;
}
*/
}
void Client::sendMessage(std::unique_ptr<Message> message)
{
bool wasEmpty = m_writeQueue.empty();
m_writeQueue.emplace(std::move(message));
if(wasEmpty)
doWrite();
}
void Client::connectionLost()
{
m_server.console->info(m_id, "Connection lost");
disconnect();
}
void Client::connectionError(const std::string& where, const std::string& what)
{
m_server.console->error(m_id, what + " [" + where + "]");
disconnect();
}
void Client::disconnect()
{
m_server.clientGone(shared_from_this());
}

84
server/src/core/client.hpp Normale Datei
Datei anzeigen

@ -0,0 +1,84 @@
/**
* server/src/core/client.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_CLIENT_HPP
#define SERVER_CORE_CLIENT_HPP
//#include "Message.hpp"
//#include "MessageQueue.hpp"
//#include <thread>
#include <memory>
#include <queue>
//#include <deque>
//#include <list>
//#include <condition_variable>
#include <boost/asio.hpp>
//#include "connection-callback.hpp"
//#include "status.hpp"
//#include <chrono>
//#include <cmath>
#include "objectptr.hpp"
#include <message.hpp>
class Traintastic;
class Session;
class Client : public std::enable_shared_from_this<Client>
{
friend class Session;
protected:
using ObjectHandle = uint32_t;
Traintastic& m_server;
boost::asio::ip::tcp::socket m_socket;
const std::string m_id;
struct
{
Message::Header header;
std::shared_ptr<Message> message;
} m_readBuffer;
std::queue<std::unique_ptr<Message>> m_writeQueue;
bool m_authenticated;
std::shared_ptr<Session> m_session;
//ObjectHandle m_lastObjectHandle;
//std::map<ObjectHandle,ObjectPtr> m_objects;
void doReadHeader();
void doReadData();
void doWrite();
void processMessage(const std::shared_ptr<Message> message);
void sendMessage(std::unique_ptr<Message> message);
void connectionLost();
void connectionError(const std::string& where, const std::string& what);
void disconnect();
public:
Client(Traintastic& server, const std::string& id, boost::asio::ip::tcp::socket socket);
virtual ~Client();
void start();
};
#endif

123
server/src/core/console.cpp Normale Datei
Datei anzeigen

@ -0,0 +1,123 @@
/**
* server/src/core/console.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "console.hpp"
#include <iostream>
#include <iomanip>
#include <chrono>
#include "consoletablemodel.hpp"
std::ostream& operator<<(std::ostream& out, const Console::Level& value)
{
switch(value)
{
case Console::Level::Debug:
out << "[debug] ";
break;
case Console::Level::Info:
out << "[info] ";
break;
case Console::Level::Notice:
out << "[notice] ";
break;
case Console::Level::Warning:
out << "[warning] ";
break;
case Console::Level::Error:
out << "[error] ";
break;
case Console::Level::Critical:
out << "[critical]";
break;
case Console::Level::Fatal:
out << "[fatal] ";
break;
}
return out;
}
std::string to_string(const Console::Level& value)
{
switch(value)
{
case Console::Level::Debug:
return "Debug";
case Console::Level::Info:
return "Info";
case Console::Level::Notice:
return "Notice";
case Console::Level::Warning:
return "Warning";
case Console::Level::Error:
return "Error";
case Console::Level::Critical:
return "Critical";
case Console::Level::Fatal:
return "Fatal";
}
assert(false);
return "unknown";
}
Console::Console() :
IdObject("console"),
count{this, "count", 1000, PropertyFlags::AccessWWW}
{
m_interfaceItems.add(count);
}
void Console::log(Level level, const std::string& id, const std::string& message)
{
const auto now = std::chrono::system_clock::now();
const auto tm = std::chrono::system_clock::to_time_t(now);
const auto us = std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()) % 1000000;
std::ostream& ss = (level >= Level::Error) ? std::cerr : std::cout;
ss << std::put_time(std::localtime(&tm), "%F %T") << '.' << std::setfill('0') << std::setw(6) << us.count() << ' ' << level << ' ' << id << ": " << message << std::endl;
m_logs.push_back({
.time_s = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count(),
.time_us = static_cast<uint32_t>(us.count()),
.level = level,
.id = id,
.message = message});
for(auto& model : m_models)
model->logAdded();
}
TableModelPtr Console::getModel()
{
return std::make_shared<ConsoleTableModel>(*this);
}

84
server/src/core/console.hpp Normale Datei
Datei anzeigen

@ -0,0 +1,84 @@
/**
* server/src/core/console.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_CONSOLE_HPP
#define SERVER_CORE_CONSOLE_HPP
#include "idobject.hpp"
#include "table.hpp"
#include <memory>
#include <vector>
class ConsoleTableModel;
class Console : public IdObject, public Table
{
friend class ConsoleTableModel;
public:
enum class Level
{
Debug = 0,
Info = 1,
Notice = 2,
Warning = 3,
Error = 4,
Critical = 5,
Fatal = 6,
};
protected:
struct Log
{
int64_t time_s;
uint32_t time_us;
Level level;
std::string id;
std::string message;
};
std::vector<Log> m_logs;
std::vector<ConsoleTableModel*> m_models;
public:
CLASS_ID("console");
Property<uint32_t> count;
Console();
void log(Level level, const std::string& id, const std::string& message);
inline void debug(const std::string& id, const std::string& message) { log(Level::Debug, id, message); }
inline void info(const std::string& id, const std::string& message) { log(Level::Info, id, message); }
inline void notice(const std::string& id, const std::string& message) { log(Level::Notice, id, message); }
inline void warning(const std::string& id, const std::string& message) { log(Level::Warning, id, message); }
inline void error(const std::string& id, const std::string& message) { log(Level::Error, id, message); }
inline void critical(const std::string& id, const std::string& message) { log(Level::Critical, id, message); }
inline void fatal(const std::string& id, const std::string& message) { log(Level::Fatal, id, message); }
TableModelPtr getModel() final;
};
std::string to_string(const Console::Level& value);
#endif

Datei anzeigen

@ -0,0 +1,88 @@
/**
* server/src/core/consoletablemodel.cpp - Console table model
*
* This file is part of the traintastic source code
*
* Copyright (C) 2019 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 "consoletablemodel.hpp"
#include "console.hpp"
#include <chrono>
#include <iomanip>
constexpr uint32_t columnTime = 0;
constexpr uint32_t columnLevel = 1;
constexpr uint32_t columnId = 2;
constexpr uint32_t columnMessage = 3;
ConsoleTableModel::ConsoleTableModel(Console& console) :
TableModel(),
m_console{console}
{
m_console.m_models.push_back(this);
setColumnHeaders({"Time", "Level", "Object", "Message"});
setRowCount(m_console.m_logs.size());
}
ConsoleTableModel::~ConsoleTableModel()
{
auto it = std::find(m_console.m_models.begin(), m_console.m_models.end(), this);
assert(it != m_console.m_models.end());
m_console.m_models.erase(it);
}
std::string ConsoleTableModel::getText(uint32_t column, uint32_t row) const
{
if(row < rowCount())
{
const auto& log = m_console.m_logs[row];
switch(column)
{
case columnTime:
{
const std::chrono::time_point<std::chrono::system_clock> time(std::chrono::seconds(log.time_s));
const auto tm = std::chrono::system_clock::to_time_t(time);
std::string s;
s.resize(32);
size_t n = strftime(s.data(), s.size(), "%F %T", std::localtime(&tm));
n += snprintf(s.data() + n, s.size() - n, ".%06u", log.time_us);
s.resize(n);
return s;
}
case columnLevel:
return to_string(log.level);
case columnId:
return log.id;
case columnMessage:
return log.message;
default:
assert(false);
break;
}
}
return "";
}
void ConsoleTableModel::logAdded()
{
setRowCount(m_console.m_logs.size());
}

Datei anzeigen

@ -0,0 +1,48 @@
/**
* server/src/core/consoletablemodel.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_CONSOLETABLEMODEL_HPP
#define SERVER_CORE_CONSOLETABLEMODEL_HPP
#include "tablemodel.hpp"
class Console;
class ConsoleTableModel : public TableModel
{
friend class Console;
protected:
Console& m_console;
void logAdded();
public:
CLASS_ID("console_table_model")
ConsoleTableModel(Console& console);
~ConsoleTableModel() final;
std::string getText(uint32_t column, uint32_t row) const final;
};
#endif

120
server/src/core/eventloop.hpp Normale Datei
Datei anzeigen

@ -0,0 +1,120 @@
/**
* server/src/core/eventloop.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_EVENTLOOP_HPP
#define SERVER_CORE_EVENTLOOP_HPP
#include <queue>
#include <mutex>
#include <thread>
#include <condition_variable>
#include <functional>
#include <iostream>
class EventLoop
{
private:
inline static EventLoop* s_instance;
std::queue<std::function<void()>> m_queue;
std::mutex m_queueMutex;
std::condition_variable m_condition;
bool m_run;
std::thread m_thread;
EventLoop() :
m_run{true},
m_thread(&EventLoop::run, this)
{
}
EventLoop(const EventLoop&) = delete;
~EventLoop()
{
}
void add(std::function<void()>&& f)
{
std::lock_guard<std::mutex> lock(m_queueMutex);
m_queue.emplace(f);
m_condition.notify_one();
}
void run()
{
std::unique_lock<std::mutex> lock(m_queueMutex);
while(m_run)
{
if(m_queue.empty())
m_condition.wait(lock, [this]{ return !m_queue.empty(); });
if(m_queue.empty())
continue; // a suspisius wakeup may occur
std::function<void()>& f{m_queue.front()};
lock.unlock();
try
{
f();
}
catch(const std::exception& e)
{
std::cerr << e.what() << std::endl;
}
lock.lock();
m_queue.pop();
}
}
void exit()
{
add([this](){ m_run = false; });
m_thread.join();
}
public:
static void start()
{
s_instance = new EventLoop();
}
static void stop()
{
s_instance->exit();
delete s_instance;
s_instance = nullptr;
}
template<typename _Callable, typename... _Args>
inline static void call(_Callable&& __f, _Args&&... __args)
{
s_instance->add(std::bind(__f, __args...));
}
};
#endif

116
server/src/core/handlelist.hpp Normale Datei
Datei anzeigen

@ -0,0 +1,116 @@
/**
* server/src/core/handlelist.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_HANDLELIST_HPP
#define SERVER_CORE_HANDLELIST_HPP
#include <cstdint>
#include <cassert>
#include <unordered_map>
template<typename Thandle, typename Titem>
class HandleList
{
public:
using Handle = Thandle;
protected:
Handle m_nextHandle;
std::unordered_map<Handle, Titem> m_handleToItem;
std::unordered_map<Titem, Handle> m_itemToHandle;
public:
static const Handle invalidHandle = 0;
HandleList() :
m_nextHandle{invalidHandle + 1}
{
}
Titem getItem(Handle handle) const
{
auto it = m_handleToItem.find(handle);
return it != m_handleToItem.end() ? it->second : nullptr;
}
Handle addItem(const Titem& item)
{
if(!item)
return invalidHandle;
auto it = m_itemToHandle.find(item);
if(it == m_itemToHandle.end())
{
Handle handle = m_nextHandle;
m_handleToItem.emplace(handle, item);
m_itemToHandle.emplace(item, handle);
assert(m_handleToItem.size() == m_itemToHandle.size());
if(++m_nextHandle == invalidHandle)
m_nextHandle++;
return handle;
}
else
return it->second;
}
Handle getHandle(const Titem& item)
{
if(item)
{
auto it = m_itemToHandle.find(item);
if(it != m_itemToHandle.end())
return it->second;
}
return invalidHandle;
}
void removeHandle(const Thandle& handle)
{
auto it = m_handleToItem.find(handle);
if(it != m_handleToItem.end())
{
m_itemToHandle.erase(it->second);
m_handleToItem.erase(it);
assert(m_handleToItem.size() == m_itemToHandle.size());
}
}
void removeItem(const Titem& item)
{
auto it = m_itemToHandle.find(item);
if(it != m_itemToHandle.end())
{
m_handleToItem.erase(it->second);
m_itemToHandle.erase(it);
assert(m_handleToItem.size() == m_itemToHandle.size());
}
}
void clear()
{
m_handleToItem.clear();
m_itemToHandle.clear();
assert(m_handleToItem.size() == m_itemToHandle.size());
}
};
#endif

28
server/src/core/idobject.cpp Normale Datei
Datei anzeigen

@ -0,0 +1,28 @@
/**
* Traintastic
*
* Copyright (C) 2019 Reinder Feenstra <reinderfeenstra@gmail.com>
*
* 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 "idobject.hpp"
IdObject::IdObject(const std::string& _id) :
Object{},
id{this, "id", _id, PropertyFlags::AccessWCC}
{
m_interfaceItems.add(id);
}

37
server/src/core/idobject.hpp Normale Datei
Datei anzeigen

@ -0,0 +1,37 @@
/**
* server/src/core/idobject.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_IDOBJECT_HPP
#define SERVER_CORE_IDOBJECT_HPP
#include "object.hpp"
#include "property.hpp"
class IdObject : public Object
{
public:
Property<std::string> id;
IdObject(const std::string& _id);
};
#endif

Datei anzeigen

@ -0,0 +1,58 @@
/**
* server/src/core/interfaceitem.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_INTERFACEITEM_HPP
#define SERVER_CORE_INTERFACEITEM_HPP
#include <string>
class Object;
class InterfaceItem
{
protected:
Object& m_object;
const std::string m_name;
public:
InterfaceItem(Object& object, const std::string& name) :
m_object{object},
m_name{name}
{
}
virtual ~InterfaceItem()
{
}
Object& object() const
{
return m_object;
}
const std::string& name() const
{
return m_name;
}
};
#endif

Datei anzeigen

@ -0,0 +1,29 @@
/**
* server/src/core/interfaceitems.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "interfaceitems.hpp"
#include "interfaceitem.hpp"
void InterfaceItems::add(InterfaceItem& item)
{
emplace(item.name(), item);
}

Datei anzeigen

@ -0,0 +1,37 @@
/**
* server/src/core/interfaceitems.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_INTERFACEITEMS_HPP
#define SERVER_CORE_INTERFACEITEMS_HPP
#include <map>
#include <string>
class InterfaceItem;
class InterfaceItems : public std::map<std::string, InterfaceItem&>
{
public:
void add(InterfaceItem& item);
};
#endif

56
server/src/core/object.cpp Normale Datei
Datei anzeigen

@ -0,0 +1,56 @@
/**
* server/src/core/object.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "object.hpp"
//#include "abstractmethod.hpp"
#include "abstractproperty.hpp"
Object::Object()
{
}
Object::~Object()
{
}
InterfaceItem* Object::getItem(const std::string& name)
{
auto it = m_interfaceItems.find(name);
return (it != m_interfaceItems.end()) ? &it->second : nullptr;
}
//AbstractMethod* Object::getMethod(const std::string& name)
//{
// return dynamic_cast<AbstractMethod*>(getItem(name));
//}
AbstractProperty* Object::getProperty(const std::string& name)
{
return dynamic_cast<AbstractProperty*>(getItem(name));
}
//void Object::log(LogLevel level, const std::string& message) const
//{
// auto p = m_instance.lock();
// if(p)
// p->consoleLog(level, message);
//}

67
server/src/core/object.hpp Normale Datei
Datei anzeigen

@ -0,0 +1,67 @@
/**
* server/src/core/object.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_OBJECT_HPP
#define SERVER_CORE_OBJECT_HPP
#include "objectptr.hpp"
#include <boost/signals2/signal.hpp>
#include "interfaceitems.hpp"
#define CLASS_ID(id) \
public: \
static constexpr std::string_view classId = id; \
const std::string_view& getClassId() const override { return classId; }
class AbstractMethod;
class AbstractProperty;
class Object : public std::enable_shared_from_this<Object>
{
protected:
InterfaceItems m_interfaceItems;
//void log(LogLevel level, const std::string& message) const;
//inline void logError(const std::string& message) const { log(LogLevel::Error, message); }
public:
boost::signals2::signal<void (Object& object, AbstractProperty&)> propertyChanged;
Object();
virtual ~Object();
template <typename Derived>
std::shared_ptr<Derived> shared_ptr()
{
return std::static_pointer_cast<Derived>(shared_from_this());
}
virtual const std::string_view& getClassId() const = 0;
const InterfaceItems& interfaceItems() const { return m_interfaceItems; }
InterfaceItem* getItem(const std::string& name);
//AbstractMethod* getMethod(const std::string& name);
AbstractProperty* getProperty(const std::string& name);
};
#endif

Datei anzeigen

@ -0,0 +1,68 @@
/**
* server/src/core/objectlist.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_OBJECTLIST_HPP
#define SERVER_CORE_OBJECTLIST_HPP
#include "idobject.hpp"
#include "table.hpp"
template<typename T>
class ObjectListTableModel;
template<typename T>
class ObjectList : public IdObject, public Table
{
friend class ObjectListTableModel<T>;
static_assert(std::is_base_of<IdObject, T>::value);
protected:
std::vector<std::shared_ptr<T>> m_items;
std::vector<ObjectListTableModel<T>*> m_models;
public:
Property<uint32_t> length;
ObjectList(const std::string& _id) :
IdObject{_id},
length{this, "length", 0, PropertyFlags::AccessRRR}
{
}
const std::shared_ptr<T>& operator[](uint32_t index)
{
assert(index < m_items.size());
return m_items[index];
}
void add(const std::shared_ptr<T>& object)
{
m_items.push_back(object);
const auto size = m_items.size();
length = size;
for(auto& model : m_models)
model->setRowCount(size);
}
};
#endif

Datei anzeigen

@ -0,0 +1,55 @@
/**
* server/src/core/objectlisttablemodel.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_OBJECTLISTTABLEMODEL_HPP
#define SERVER_CORE_OBJECTLISTTABLEMODEL_HPP
#include "tablemodel.hpp"
#include "objectlist.hpp"
template<typename T>
class ObjectListTableModel : public TableModel
{
protected:
ObjectList<T>& m_list;
const T& getItem(uint32_t row) const { return *m_list.m_items[row]; }
//T& getItem(uint32_t row) { return *m_list.m_items[row]; }
public:
ObjectListTableModel(ObjectList<T>& list) :
TableModel(),
m_list{list}
{
m_list.m_models.push_back(this);
setRowCount(m_list.m_items.size());
}
~ObjectListTableModel() override
{
auto it = std::find(m_list.m_models.begin(), m_list.m_models.end(), this);
assert(it != m_list.m_models.end());
m_list.m_models.erase(it);
}
};
#endif

Datei anzeigen

@ -0,0 +1,164 @@
/**
* server/src/core/objectproperty.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_OBJECTPROPERTY_HPP
#define SERVER_CORE_OBJECTPROPERTY_HPP
#include "abstractproperty.hpp"
#include "to.hpp"
#include <functional>
template<class T>
class ObjectProperty : public AbstractProperty
{
public:
using OnSet = std::function<bool(const std::shared_ptr<T>& value)>;
protected:
std::shared_ptr<T> m_value;
OnSet m_onSet;
public:
ObjectProperty(Object* object, const std::string& name, const std::shared_ptr<T>& value, PropertyFlags flags) :
AbstractProperty(*object, name, PropertyType::Object, flags),
m_value{value}
{
}
ObjectProperty(Object* object, const std::string& name, nullptr_t, PropertyFlags flags) :
ObjectProperty(object, name, std::shared_ptr<T>(), flags)
{
}
ObjectProperty(Object* object, const std::string& name, const std::shared_ptr<T>& value, PropertyFlags flags, OnSet onSet) :
ObjectProperty(object, name, value, flags)
{
m_onSet = onSet;
}
ObjectProperty(Object* object, const std::string& name, nullptr_t, PropertyFlags flags, OnSet onSet) :
ObjectProperty(object, name, std::shared_ptr<T>(), flags, onSet)
{
}
const std::shared_ptr<T>& value() const
{
return m_value;
}
void setValue(const std::shared_ptr<T>& value)
{
assert(isWriteable());
if(isWriteable() && m_value != value)
{
m_value = value;
changed();
}
}
inline const T* operator ->() const
{
return m_value.get();
}
inline T* operator ->()
{
return m_value.get();
}
inline const T& operator *() const
{
return *m_value;
}
inline T& operator *()
{
return *m_value;
}
ObjectProperty<T>& operator =(const std::shared_ptr<T>& value)
{
setValue(value);
return *this;
}
bool toBool() const final
{
throw conversion_error();
}
int64_t toInt64() const final
{
throw conversion_error();
}
double toDouble() const final
{
throw conversion_error();
}
std::string toString() const final
{
throw conversion_error();
}
ObjectPtr toObject() const final
{
return std::dynamic_pointer_cast<Object>(m_value);
}
void fromBool(bool value) final
{
throw conversion_error();
}
void fromInt64(int64_t value) final
{
throw conversion_error();
}
void fromDouble(double value) final
{
throw conversion_error();
}
void fromString(const std::string& value) final
{
throw conversion_error();
}
void fromObject(const ObjectPtr& value) final
{
if(value)
{
std::shared_ptr<T> v = std::dynamic_pointer_cast<T>(value);
if(v)
setValue(v);
else
throw conversion_error();
}
else
setValue(nullptr);
}
};
#endif

Datei anzeigen

@ -0,0 +1,33 @@
/**
* server/src/core/objectptr.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_OBJECTPTR_HPP
#define SERVER_CORE_OBJECTPTR_HPP
#include <memory>
class Object;
using ObjectPtr = std::shared_ptr<Object>;
using ObjectPtrWeak = std::weak_ptr<Object>;
#endif

28
server/src/core/output.cpp Normale Datei
Datei anzeigen

@ -0,0 +1,28 @@
/**
* Traintastic
*
* Copyright (C) 2019 Reinder Feenstra <reinderfeenstra@gmail.com>
*
* 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 "output.hpp"
Output::Output(const std::string& _id) :
IdObject{_id},
value{this, "value", false, PropertyFlags::AccessWWW}
{
m_interfaceItems.add(value);
}

36
server/src/core/output.hpp Normale Datei
Datei anzeigen

@ -0,0 +1,36 @@
/**
* server/src/core/output.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_OUTPUT_HPP
#define SERVER_CORE_OUTPUT_HPP
#include "idobject.hpp"
class Output : public IdObject
{
public:
Property<bool> value;
Output(const std::string& _id);
};
#endif

140
server/src/core/property.hpp Normale Datei
Datei anzeigen

@ -0,0 +1,140 @@
/**
* server/src/core/property.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_PROPERTY_HPP
#define SERVER_CORE_PROPERTY_HPP
#include "abstractproperty.hpp"
#include "propertytypetraits.hpp"
#include "to.hpp"
#include <functional>
template<typename T>
class Property : public AbstractProperty
{
public:
using OnChanged = std::function<void(const T& value)>;
using OnSet = std::function<bool(T& value)>;
protected:
T m_value;
OnChanged m_onChanged;
OnSet m_onSet;
public:
Property(Object* object, const std::string& name, const T& value, PropertyFlags flags) :
AbstractProperty(*object, name, property_type<T>::value, flags),
m_value{value}
{
//static_assert(property_type<T>::value != PropertyType::Invalid);
}
Property(Object* object, const std::string& name, const T& value, PropertyFlags flags, OnChanged onChanged) :
Property(object, name, value, flags)
{
m_onChanged = onChanged;
}
Property(Object* object, const std::string& name, const T& value, PropertyFlags flags, OnSet onSet) :
Property(object, name, value, flags)
{
m_onSet = onSet;
}
T value() const
{
return m_value;
}
void setValue(T value)
{
assert(isWriteable());
if(isWriteable() && (!m_onSet || m_onSet(value)) && m_value != value)
{
m_value = value;
changed();
}
}
operator T() const
{
return m_value;
}
Property<T>& operator =(const T& value)
{
setValue(value);
return *this;
}
bool toBool() const final
{
return to<bool>(m_value);
}
int64_t toInt64() const final
{
return to<int64_t>(m_value);
}
double toDouble() const final
{
return to<double>(m_value);
}
std::string toString() const final
{
return to<std::string>(m_value);
}
ObjectPtr toObject() const final
{
throw conversion_error();
}
void fromBool(bool value) final
{
setValue(to<T>(value));
}
void fromInt64(int64_t value) final
{
setValue(to<T>(value));
}
void fromDouble(double value) final
{
setValue(to<T>(value));
}
void fromString(const std::string& value) final
{
setValue(to<T>(value));
}
void fromObject(const ObjectPtr& value) final
{
throw conversion_error();
}
};
#endif

Datei anzeigen

@ -0,0 +1,94 @@
/**
* server/src/core/propertyflags.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_PROPERTYFLAGS_HPP
#define SERVER_CORE_PROPERTYFLAGS_HPP
#include <cstdint>
enum class PropertyFlags : uint32_t
{
// Access:
EditConstant = 1 << 0,
EditReadOnly = 2 << 0,
EditReadWrite = 3 << 0,
StopConstant = 1 << 2,
StopReadOnly = 2 << 2,
StopReadWrite = 3 << 2,
RunConstant = 1 << 4,
RunReadOnly = 2 << 4,
RunReadWrite = 3 << 4,
AccessRRR = PropertyFlags::EditReadOnly | PropertyFlags::StopReadOnly | PropertyFlags::RunReadOnly,
AccessRRW = PropertyFlags::EditReadOnly | PropertyFlags::StopReadOnly | PropertyFlags::RunReadWrite,
AccessRWW = PropertyFlags::EditReadOnly | PropertyFlags::StopReadWrite | PropertyFlags::RunReadWrite,
AccessWCC = PropertyFlags::EditReadWrite | PropertyFlags::StopConstant | PropertyFlags::RunConstant,
AccessWWW = PropertyFlags::EditReadWrite | PropertyFlags::StopReadWrite | PropertyFlags::RunReadWrite,
// Store:
NoStore = 1 << 6,
Store = 2 << 6,
StoreState = 3 << 6,
};
constexpr PropertyFlags operator| (const PropertyFlags& lhs, const PropertyFlags& rhs)
{
return static_cast<PropertyFlags>(static_cast<uint32_t>(lhs) | static_cast<uint32_t>(rhs));
}
constexpr PropertyFlags& operator|= (PropertyFlags& lhs, const PropertyFlags& rhs)
{
return lhs = lhs | rhs;
}
constexpr PropertyFlags operator& (const PropertyFlags& lhs, const PropertyFlags& rhs)
{
return static_cast<PropertyFlags>(static_cast<uint32_t>(lhs) & static_cast<uint32_t>(rhs));
}
constexpr PropertyFlags& operator&= (PropertyFlags& lhs, const PropertyFlags& rhs)
{
return lhs = lhs & rhs;
}
constexpr bool is_empty(const PropertyFlags value)
{
return value == static_cast<PropertyFlags>(0);
}
//constexpr PropertyFlags PropertyFlagsAccessMask = PropertyFlags::Constant | PropertyFlags::ReadOnly | PropertyFlags::WriteOnly;
//constexpr PropertyFlags PropertyFlagsStoreMask = PropertyFlags::NoStore | PropertyFlags::Store | PropertyFlags::StoreState;
constexpr bool is_access_valid(const PropertyFlags value)
{
return true;
//const PropertyFlags access = value & PropertyFlagsAccessMask;
//return (access == PropertyFlags::Constant) || !is_empty(access & PropertyFlags::ReadWrite);
}
constexpr bool is_store_valid(const PropertyFlags value)
{
return true;
//const PropertyFlags store = value & PropertyFlagsStoreMask;
//return (store == PropertyFlags::NoStore) || (store == PropertyFlags::Store) || (store == PropertyFlags::StoreState);
}
#endif

Datei anzeigen

@ -0,0 +1,40 @@
/**
* server/src/core/propertytypetraits.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_PROPERTYTYPETRAITS_HPP
#define SERVER_CORE_PROPERTYTYPETRAITS_HPP
#include <enum/propertytype.hpp>
#include "objectptr.hpp"
template<typename T>
struct property_type
{
static constexpr PropertyType value =
std::is_same<T, bool>::value ? PropertyType::Boolean : (
std::is_integral<T>::value ? PropertyType::Integer : (
std::is_floating_point<T>::value ? PropertyType::Float : (
std::is_same<T, std::string>::value ? PropertyType::String : (
PropertyType::Invalid))));
};
#endif

318
server/src/core/session.cpp Normale Datei
Datei anzeigen

@ -0,0 +1,318 @@
/**
* server/src/core/session.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "session.hpp"
#include <boost/uuid/random_generator.hpp>
#include "traintastic.hpp"
#include "client.hpp"
#include "abstractproperty.hpp"
#include <enum/interfaceitemtype.hpp>
#include "tablemodel.hpp"
#include "world.hpp"
#include "settings.hpp"
#include "../hardware/commandstation/commandstationlist.hpp"
#include <iostream>
Session::Session(const std::shared_ptr<Client>& client) :
m_client{client},
m_uuid{boost::uuids::random_generator()()}
{
}
bool Session::processMessage(const Message& message)
{
switch(message.command())
{
case Message::Command::GetObject:
case Message::Command::GetTableModel:
{
std::string id;
message.read(id);
ObjectPtr obj;
if(id == "console")
obj = Traintastic::instance->console.toObject();
else if(id == "settings")
obj = Traintastic::instance->settings.toObject();
else if(id == "command_station_list")
obj = Traintastic::instance->world->commandStationList;
if(obj)
{
switch(message.command())
{
case Message::Command::GetObject:
{
Traintastic::instance->console->debug(m_client->m_id, "GetObject: " + id);
auto response = Message::newResponse(message.command(), message.requestId());
writeObject(*response, obj);
m_client->sendMessage(std::move(response));
break;
}
case Message::Command::GetTableModel:
{
if(Table* table = dynamic_cast<Table*>(obj.get()))
{
TableModelPtr model = table->getModel();
assert(model);
Traintastic::instance->console->debug(m_client->m_id, "GetTableModel: " + id);
auto response = Message::newResponse(message.command(), message.requestId());
writeTableModel(*response, model);
m_client->sendMessage(std::move(response));
model->columnHeadersChanged = [this](const TableModelPtr& model)
{
auto event = Message::newEvent(Message::Command::TableModelColumnHeadersChanged);
event->write(m_handles.getHandle(std::dynamic_pointer_cast<Object>(model)));
event->write(model->columnCount());
for(const std::string& text : model->columnHeaders())
event->write(text);
m_client->sendMessage(std::move(event));
};
model->rowCountChanged = [this](const TableModelPtr& model)
{
auto event = Message::newEvent(Message::Command::TableModelRowCountChanged);
event->write(m_handles.getHandle(std::dynamic_pointer_cast<Object>(model)));
event->write(model->rowCount());
m_client->sendMessage(std::move(event));
};
model->updateRegion = [this](const TableModelPtr& model, const TableModel::Region& region)
{
std::cout << "updateRegion " << region.columnMin << " " << region.columnMax << " " << region.rowMin << " " << region.rowMax << std::endl;
auto event = Message::newEvent(Message::Command::TableModelUpdateRegion);
event->write(m_handles.getHandle(std::dynamic_pointer_cast<Object>(model)));
event->write(region.columnMin);
event->write(region.columnMax);
event->write(region.rowMin);
event->write(region.rowMax);
for(uint32_t row = region.rowMin; row <= region.rowMax; row++)
for(uint32_t column = region.columnMin; column <= region.columnMax; column++)
event->write(model->getText(column, row));
m_client->sendMessage(std::move(event));
};
/*
void TableModel::sendRegion(uint32_t columnMin, uint32_t columnMax, uint32_t rowMin, uint32_t rowMax)
{
auto event = Message::newEvent(Message::Command::TableModelUpdateRegion);
event->write(m_handle);
for(uint32_t row = rowMin; row <= rowMax; row++)
for(uint32_t column = columnMin; column <= columnMax; column++)
{
}
}
*/
}
else
m_client->sendMessage(Message::newErrorResponse(message.command(), message.requestId(), Message::ErrorCode::ObjectNotTable));
break;
}
default:
assert(false);
return false;
}
}
else
{
Traintastic::instance->console->debug(m_client->m_id, "UnknownObject: " + id);
m_client->sendMessage(Message::newErrorResponse(message.command(), message.requestId(), Message::ErrorCode::UnknownObject));
}
return true;
}
case Message::Command::ReleaseObject:
{
Handle handle = message.read<Handle>();
Traintastic::instance->console->debug(m_client->m_id, "ReleaseObject: " + std::to_string(handle));
m_handles.removeHandle(handle);
break;
}
case Message::Command::ObjectSetProperty:
{
if(ObjectPtr object = m_handles.getItem(message.read<Handle>()))
{
if(AbstractProperty* property = object->getProperty(message.read<std::string>()))
{
switch(message.read<PropertyType>())
{
case PropertyType::Boolean:
property->fromBool(message.read<bool>());
break;
case PropertyType::Integer:
property->fromInt64(message.read<int64_t>());
break;
case PropertyType::Float:
property->fromDouble(message.read<double>());
break;
case PropertyType::String:
property->fromString(message.read<std::string>());
break;
}
}
}
break;
}
case Message::Command::ReleaseTableModel:
{
Handle handle = message.read<Handle>();
Traintastic::instance->console->debug(m_client->m_id, "ReleaseTableModel: " + std::to_string(handle));
m_handles.removeHandle(handle);
break;
}
case Message::Command::TableModelSetRegion:
{
TableModelPtr model = std::dynamic_pointer_cast<TableModel>(m_handles.getItem(message.read<Handle>()));
if(model)
{
TableModel::Region region;
message.read(region.columnMin);
message.read(region.columnMax);
message.read(region.rowMin);
message.read(region.rowMax);
//std::cout << "TableModelSetRegion " << region.columnMin << " " << region.columnMax << " " << region.rowMin << " " << region.rowMax << std::endl;
model->setRegion(region);
}
break;
}
default:
break;
}
return false;
}
void Session::writeObject(Message& message, const ObjectPtr& object)
{
message.writeBlock(); // object
auto handle = m_handles.getHandle(object);
if(handle == Handles::invalidHandle)
{
using namespace std::placeholders;
handle = m_handles.addItem(object);
m_propertyChanged.emplace(handle, object->propertyChanged.connect(std::bind(&Session::objectPropertyChanged, this, std::placeholders::_1, std::placeholders::_2)));
message.write(handle);
message.write(object->getClassId());
message.writeBlock(); // items
for(auto& it : object->interfaceItems())
{
InterfaceItem& item = it.second;
message.writeBlock(); // item
message.write(item.name());
if(AbstractProperty* property = dynamic_cast<AbstractProperty*>(&item))
{
message.write(InterfaceItemType::Property);
message.write(property->type());
switch(property->type())
{
case PropertyType::Boolean:
message.write(property->toBool());
break;
case PropertyType::Integer:
message.write(property->toInt64());
break;
case PropertyType::Float:
message.write(property->toDouble());
break;
case PropertyType::String:
message.write(property->toString());
break;
default:
assert(false);
break;
}
}
message.writeBlockEnd(); // end item
}
message.writeBlockEnd(); // end items
}
else
message.write(handle);
message.writeBlockEnd(); // end object
}
void Session::writeTableModel(Message& message, const TableModelPtr& model)
{
message.writeBlock(); // model
assert(m_handles.getHandle(std::dynamic_pointer_cast<Object>(model)) == Handles::invalidHandle);
auto handle = m_handles.addItem(std::dynamic_pointer_cast<Object>(model));
message.write(handle);
message.write(model->getClassId());
message.write(model->columnCount());
for(const std::string& text : model->columnHeaders())
message.write(text);
message.write(model->rowCount());
message.writeBlockEnd(); // end model
}
void Session::objectPropertyChanged(Object& object, AbstractProperty& property)
{
std::cout << "objectPropertyChanged " << property.name() << std::endl;
auto event = Message::newEvent(Message::Command::ObjectPropertyChanged);
event->write(m_handles.getHandle(object.shared_from_this()));
event->write(property.name());
event->write(property.type());
switch(property.type())
{
case PropertyType::Boolean:
event->write(property.toBool());
break;
case PropertyType::Integer:
event->write(property.toInt64());
break;
case PropertyType::Float:
event->write(property.toDouble());
break;
case PropertyType::String:
event->write(property.toString());
break;
}
m_client->sendMessage(std::move(event));
}

63
server/src/core/session.hpp Normale Datei
Datei anzeigen

@ -0,0 +1,63 @@
/**
* server/src/core/session.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_SESSION_HPP
#define SERVER_CORE_SESSION_HPP
#include <memory>
#include <boost/uuid/uuid.hpp>
#include <boost/signals2/connection.hpp>
#include <message.hpp>
#include "handlelist.hpp"
#include "objectptr.hpp"
#include "tablemodelptr.hpp"
class Client;
class AbstractProperty;
class Session : public std::enable_shared_from_this<Session>
{
friend class Client;
protected:
using Handle = uint32_t;
using Handles = HandleList<Handle, ObjectPtr>;
std::shared_ptr<Client> m_client;
boost::uuids::uuid m_uuid;
Handles m_handles;
std::unordered_map<Handle, boost::signals2::connection> m_propertyChanged;
bool processMessage(const Message& message);
void writeObject(Message& message, const ObjectPtr& object);
void writeTableModel(Message& message, const TableModelPtr& model);
void objectPropertyChanged(Object& object, AbstractProperty& property);
public:
Session(const std::shared_ptr<Client>& client);
const boost::uuids::uuid& uuid() const { return m_uuid; }
};
#endif

84
server/src/core/settings.cpp Normale Datei
Datei anzeigen

@ -0,0 +1,84 @@
/**
* server/src/core/settings.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "settings.hpp"
#include <fstream>
#include <nlohmann/json.hpp>
#include "traintastic.hpp"
using nlohmann::json;
const std::string Settings::id{Settings::classId};
Settings::Settings(const std::filesystem::path& filename) :
Object{},
m_filename{filename},
localhostOnly{this, "localhost_only", true, PropertyFlags::AccessWCC},
port{this, "port", defaultPort, PropertyFlags::AccessWCC},
discoverable{this, "discoverable", true, PropertyFlags::AccessWWW},
defaultWorld{this, "default_world", "", PropertyFlags::AccessWWW}
{
m_interfaceItems.add(localhostOnly);
m_interfaceItems.add(port);
m_interfaceItems.add(discoverable);
m_interfaceItems.add(defaultWorld);
load();
}
void Settings::load()
{
std::ifstream file(m_filename);
if(file.is_open())
{
Traintastic::instance->console->debug(id, "Settings file: " + m_filename.string());
json settings = json::parse(file);
for(auto& [name, value] : settings.items())
{
AbstractProperty* property = getProperty(name);
if(property)
property->fromJSON(value);
else
Traintastic::instance->console->warning(id, "Setting `" + name + "` doesn't exist");
}
Traintastic::instance->console->info(id, "Loaded settings");
}
else
Traintastic::instance->console->info(id, "Settings file not found, using defaults");
}
void Settings::save()
{
json settings = json::object();
for(const auto& it : m_interfaceItems)
if(AbstractProperty* property = dynamic_cast<AbstractProperty*>(&it.second))
settings[property->name()] = property->toJSON();
std::ofstream file(m_filename);
if(file.is_open())
{
file << settings.dump(2);
Traintastic::instance->console->notice(id, "Saved settings");
}
else
Traintastic::instance->console->critical(id, "Can't write to settings file");
}

53
server/src/core/settings.hpp Normale Datei
Datei anzeigen

@ -0,0 +1,53 @@
/**
* server/src/core/settings.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_SETTINGS_HPP
#define SERVER_CORE_SETTINGS_HPP
#include "object.hpp"
#include "stdfilesystem.hpp"
#include "property.hpp"
class Settings : public Object
{
protected:
static const std::string id;
const std::filesystem::path m_filename;
void load();
void save();
public:
CLASS_ID("settings");
static constexpr uint16_t defaultPort = 5740; //!< unoffical, not (yet) assigned by IANA
Property<bool> localhostOnly;
Property<uint16_t> port;
Property<bool> discoverable;
Property<std::string> defaultWorld;
Settings(const std::filesystem::path& filename);
};
#endif

Datei anzeigen

@ -0,0 +1,35 @@
/**
* server/src/core/stdfilesystem.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_STDFILESYSTEM_HPP
#define SERVER_CORE_STDFILESYSTEM_HPP
#if __GNUC__ < 8
#include <experimental/filesystem>
namespace std {
namespace filesystem = experimental::filesystem::v1;
}
#else
#include <filesystem>
#endif
#endif

34
server/src/core/table.hpp Normale Datei
Datei anzeigen

@ -0,0 +1,34 @@
/**
* server/src/core/table.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_TABLE_HPP
#define SERVER_CORE_TABLE_HPP
#include "tablemodelptr.hpp"
class Table
{
public:
virtual TableModelPtr getModel() = 0;
};
#endif

Datei anzeigen

@ -0,0 +1,75 @@
/**
* server/src/core/tablemodel.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "tablemodel.hpp"
TableModel::TableModel() :
Object(),
m_rowCount{0}
{
}
void TableModel::setRegion(const Region& value)
{
if(m_region != value)
{
if(!(value <= m_region))
{
Region update = value;
if(update.columnMin == m_region.columnMin && update.columnMax == m_region.columnMax)
{
if(update.rowMin < m_region.rowMin && update.rowMax <= m_region.rowMax) // extend/move top
update.rowMax = m_region.rowMin - 1;
else if(update.rowMin >= m_region.rowMin && update.rowMax > m_region.rowMax) // extend/move bottom
update.rowMin = m_region.rowMax + 1;
}
m_region = value;
if(updateRegion)
updateRegion(shared_ptr<TableModel>(), update);
}
else
m_region = value;
}
}
void TableModel::setColumnHeaders(const std::vector<std::string>& values)
{
if(m_columnHeaders != values)
{
m_columnHeaders = values;
if(columnHeadersChanged)
columnHeadersChanged(shared_ptr<TableModel>());
}
}
void TableModel::setRowCount(uint32_t value)
{
if(m_rowCount != value)
{
m_rowCount = value;
if(rowCountChanged)
rowCountChanged(shared_ptr<TableModel>());
}
}

107
server/src/core/tablemodel.hpp Normale Datei
Datei anzeigen

@ -0,0 +1,107 @@
/**
* server/src/core/tablemodel.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_TABLEMODEL_HPP
#define SERVER_CORE_TABLEMODEL_HPP
#include "object.hpp"
//#include <vector>
#include <functional>
#include "tablemodelptr.hpp"
class TableModel : public Object
{
public:
struct Region
{
uint32_t columnMin;
uint32_t columnMax;
uint32_t rowMin;
uint32_t rowMax;
Region() :
columnMin{0},
columnMax{0},
rowMin{0},
rowMax{0}
{
}
Region(uint32_t _columnMin, uint32_t _columnMax, uint32_t _rowMin, uint32_t _rowMax) :
columnMin{_columnMin},
columnMax{_columnMax},
rowMin{_rowMin},
rowMax{_rowMax}
{
}
bool operator==(const Region& rhs) const
{
return
this->columnMin == rhs.columnMin &&
this->columnMax == rhs.columnMax &&
this->rowMin == rhs.rowMin &&
this->rowMax == rhs.rowMax;
}
bool operator!=(const Region& rhs) const
{
return !(*this == rhs);
}
bool operator<=(const Region& rhs) const
{
return
this->columnMin >= rhs.columnMin &&
this->columnMax <= rhs.columnMax &&
this->rowMin >= rhs.rowMin &&
this->rowMax <= rhs.rowMax;
}
};
private:
std::vector<std::string> m_columnHeaders;
uint32_t m_rowCount;
Region m_region;
protected:
void setColumnHeaders(const std::vector<std::string>& values);
void setRowCount(uint32_t value);
public:
std::function<void(const TableModelPtr&)> columnHeadersChanged;
std::function<void(const TableModelPtr&)> rowCountChanged;
std::function<void(const TableModelPtr&, const Region& region)> updateRegion;
TableModel();
const std::vector<std::string>& columnHeaders() const { return m_columnHeaders; }
uint32_t columnCount() const { return m_columnHeaders.size(); }
uint32_t rowCount() const { return m_rowCount; }
virtual std::string getText(uint32_t column, uint32_t row) const = 0;
void setRegion(const Region& value);
};
#endif

Datei anzeigen

@ -0,0 +1,32 @@
/**
* server/src/core/tablemodelptr.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_TABLEMODELPTR_HPP
#define SERVER_CORE_TABLEMODELPTR_HPP
#include <memory>
class TableModel;
using TableModelPtr = std::shared_ptr<TableModel>;
#endif

76
server/src/core/to.hpp Normale Datei
Datei anzeigen

@ -0,0 +1,76 @@
/**
* server/src/core/to.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_TO_HPP
#define SERVER_CORE_TO_HPP
#include <type_traits>
#include <limits>
#include <stdexcept>
#include <cmath>
class conversion_error : public std::runtime_error
{
public:
conversion_error() :
std::runtime_error("conversion error")
{
}
};
class out_of_range_error : public std::runtime_error
{
public:
out_of_range_error() :
std::runtime_error("out of range error")
{
}
};
template<typename To, typename From>
To to(const From& value)
{
if constexpr(std::is_same<To, From>::value)
return value;
else if constexpr(!std::is_same<To, bool>::value && std::is_integral<To>::value && !std::is_same<From, bool>::value && std::is_integral<From>::value)
{
if constexpr(std::numeric_limits<To>::min() <= std::numeric_limits<From>::min() && std::numeric_limits<To>::max() >= std::numeric_limits<From>::max())
return value;
else if(value >= std::numeric_limits<To>::min() && value <= std::numeric_limits<To>::max())
return value;
else
throw out_of_range_error();
}
else if constexpr(std::is_floating_point<To>::value && (std::is_integral<From>::value || std::is_floating_point<From>::value))
return value;
else if constexpr(std::is_integral<To>::value && std::is_floating_point<From>::value)
{
if(value >= std::numeric_limits<To>::min() && value <= std::numeric_limits<To>::max())
return static_cast<To>(std::round(value));
else
throw out_of_range_error();
}
throw conversion_error();
}
#endif

Datei anzeigen

@ -0,0 +1,275 @@
/**
* server/src/core/traintastic.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "traintastic.hpp"
#include <fstream>
#include <nlohmann/json.hpp>
#include <boost/uuid/nil_generator.hpp>
#include <boost/uuid/string_generator.hpp>
#include <boost/uuid/uuid_io.hpp>
#include "eventloop.hpp"
#include "settings.hpp"
#include "client.hpp"
#include "world.hpp"
#include "worldlist.hpp"
using nlohmann::json;
const std::string Traintastic::id{Traintastic::classId};
std::unique_ptr<Traintastic> Traintastic::instance;
Traintastic::Traintastic(const std::filesystem::path& dataDir) :
Object{},
m_dataDir{std::filesystem::absolute(dataDir)},
m_ioContext{},
m_acceptor{m_ioContext},
m_socketTCP{m_ioContext},
m_socketUDP{m_ioContext},
console{this, "console", std::make_shared<Console>(), PropertyFlags::AccessRRR},
settings{this, "settings", nullptr, PropertyFlags::AccessRRR},
world{this, "world", nullptr, PropertyFlags::AccessRRR},
worldList{this, "world_list", nullptr, PropertyFlags::AccessRRR}
{
m_interfaceItems.add(console);
m_interfaceItems.add(settings);
m_interfaceItems.add(world);
m_interfaceItems.add(worldList);
}
Traintastic::~Traintastic()
{
assert(m_ioContext.stopped());
}
bool Traintastic::run()
{
settings = std::make_shared<Settings>(m_dataDir / "settings.json");
worldList = std::make_shared<WorldList>(m_dataDir / "world");
if(!settings->defaultWorld.value().empty())
{
boost::uuids::uuid uuid;
try
{
uuid = boost::uuids::string_generator()(settings->defaultWorld.value());
}
catch(const std::exception& e)
{
uuid = boost::uuids::nil_generator()();
console->error(id, "Invalid default world uuid");
}
if(!uuid.is_nil())
loadWorld(uuid);
}
else
newWorld();
if(!start())
return false;
auto work = std::make_shared<boost::asio::io_service::work>(m_ioContext);
m_ioContext.run();
return true;
}
void Traintastic::shutdown()
{
console->notice(id, "Shutting down");
m_ioContext.stop();
}
bool Traintastic::start()
{
boost::system::error_code ec;
m_acceptor.open(boost::asio::ip::tcp::v4(), ec);
if(ec)
{
console->fatal(id, ec.message());
return false;
}
m_acceptor.bind(boost::asio::ip::tcp::endpoint(settings->localhostOnly ? boost::asio::ip::address_v4::loopback() : boost::asio::ip::address_v4::any(), settings->port), ec);
if(ec)
{
console->fatal(id, ec.message());
return false;
}
m_acceptor.listen(5, ec);
if(ec)
{
console->fatal(id, ec.message());
return false;
}
if(settings->discoverable)
{
if(settings->port == Settings::defaultPort)
{
m_socketUDP.open(boost::asio::ip::udp::v4(), ec);
if(ec)
{
console->fatal(id, ec.message());
return false;
}
m_socketUDP.bind(boost::asio::ip::udp::endpoint(boost::asio::ip::address_v4::any(), Settings::defaultPort), ec);
if(ec)
{
console->fatal(id, "bind: " + ec.message());
return false;
}
console->notice(id, "Discovery enabled");
doReceive();
}
else
console->warning(id, std::string("Discovery disabled, only allowed on port ") + std::to_string(Settings::defaultPort));
}
else
console->notice(id, "Discovery disabled");
console->notice(id, std::string("Listening at ") + m_acceptor.local_endpoint().address().to_string() + ":" + std::to_string(m_acceptor.local_endpoint().port()));
doAccept();
return true;
}
bool Traintastic::stop()
{
m_acceptor.close();
return true;
}
void Traintastic::newWorld()
{
world = std::make_shared<World>();
console->notice(id, "Created new world");
}
void Traintastic::loadWorld(const boost::uuids::uuid& uuid)
{
if(const WorldList::WorldInfo* info = worldList->find(uuid))
loadWorld(info->path);
else
console->error(id, "World " + to_string(uuid) + " doesn't exist");
}
void Traintastic::loadWorld(const std::filesystem::path& path)
{
try
{
world = std::make_shared<World>(path / "traintastic.json");
}
catch(const std::exception& e)
{
console->critical(id, std::string("Loading world failed: ") + e.what());
}
}
void Traintastic::saveWorld()
{
}
void Traintastic::clientGone(const std::shared_ptr<Client>& client)
{
}
void Traintastic::doReceive()
{
m_socketUDP.async_receive_from(boost::asio::buffer(m_udpBuffer), m_remoteEndpoint,
[this](const boost::system::error_code& ec, std::size_t bytesReceived)
{
if(!ec)
{
if(bytesReceived == sizeof(Message::Header))
{
Message message(*reinterpret_cast<Message::Header*>(m_udpBuffer.data()));
if(!settings->localhostOnly || m_remoteEndpoint.address().is_loopback())
{
if(message.dataSize() == 0)
{
std::unique_ptr<Message> response = processMessage(message);
if(response)
{
m_socketUDP.async_send_to(boost::asio::buffer(**response, response->size()), m_remoteEndpoint,
[this](const boost::system::error_code&, std::size_t)
{
doReceive();
});
return;
}
}
}
}
doReceive();
}
else
console->error(id, ec.message());
});
}
std::unique_ptr<Message> Traintastic::processMessage(const Message& message)
{
if(message.command() == Message::Command::Discover && message.isRequest())
{
std::unique_ptr<Message> response = Message::newResponse(message.command(), message.requestId());
response->write(boost::asio::ip::host_name());
return std::move(response);
}
return nullptr;
}
void Traintastic::doAccept()
{
m_acceptor.async_accept(m_socketTCP,
[this](boost::system::error_code ec)
{
EventLoop::call(
[this, ec]()
{
if(!ec)
{
try
{
std::shared_ptr<Client> client = std::make_shared<Client>(*this, "client[" + m_socketTCP.remote_endpoint().address().to_string() + ":" + std::to_string(m_socketTCP.remote_endpoint().port()) + "]", std::move(m_socketTCP));
client->start();
m_clients.push_back(client);
doAccept();
}
catch(const std::exception& e)
{
console->critical(id, e.what());
}
}
else
console->error(id, ec.message());
});
});
}

Datei anzeigen

@ -0,0 +1,89 @@
/**
* server/src/core/traintastic.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 SERVER_CORE_TRAINTASTIC_HPP
#define SERVER_CORE_TRAINTASTIC_HPP
#include <memory>
#include <list>
#include "stdfilesystem.hpp"
#include <boost/asio.hpp>
#include <boost/uuid/uuid.hpp>
#include "object.hpp"
#include "objectproperty.hpp"
#include "console.hpp"
class Client;
class Message;
class Settings;
class World;
class WorldList;
class Traintastic : public Object
{
friend class Client;
private:
const std::filesystem::path m_dataDir;
boost::asio::io_context m_ioContext;
boost::asio::ip::tcp::acceptor m_acceptor;
boost::asio::ip::tcp::socket m_socketTCP;
boost::asio::ip::udp::socket m_socketUDP;
std::array<char, 8> m_udpBuffer;
boost::asio::ip::udp::endpoint m_remoteEndpoint;
std::list<std::shared_ptr<Client>> m_clients;
bool start();
bool stop();
void newWorld();
void loadWorld(const boost::uuids::uuid& uuid);
void loadWorld(const std::filesystem::path& path);
void saveWorld();
void doReceive();
std::unique_ptr<Message> processMessage(const Message& message);
void doAccept();
protected:
void clientGone(const std::shared_ptr<Client>& client);
public:
CLASS_ID("traintastic");
static const std::string id;
static std::unique_ptr<Traintastic> instance;
ObjectProperty<Console> console;
ObjectProperty<Settings> settings;
ObjectProperty<World> world;
ObjectProperty<WorldList> worldList;
Traintastic(const std::filesystem::path& dataDir);
~Traintastic() final;
bool run();
void shutdown();
};
#endif

81
server/src/core/world.cpp Normale Datei
Datei anzeigen

@ -0,0 +1,81 @@
/**
* server/src/core/world.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 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 "world.hpp"
#include <fstream>
#include <nlohmann/json.hpp>
#include <boost/uuid/random_generator.hpp>
#include <boost/uuid/string_generator.hpp>
#include <boost/uuid/uuid_io.hpp>
#include "traintastic.hpp"
#include "../hardware/commandstation/commandstationlist.hpp"
#include "../hardware/decoder/decoderlist.hpp"
using nlohmann::json;
const std::string World::id{World::classId};
World::World() :
Object(),
m_uuid{boost::uuids::random_generator()()},
name{this, "name", "", PropertyFlags::AccessWCC},
commandStationList{std::make_shared<CommandStationList>("command_station_list")},
decoderList{std::make_shared<DecoderList>("decoder_list")}
{
}
World::World(const std::filesystem::path& filename) :
World()
{
m_filename = filename;
load();
}
void World::load()
{
std::ifstream file(m_filename);
if(file.is_open())
{
json world = json::parse(file);
m_uuid = boost::uuids::string_generator()(std::string(world["uuid"]));
name = world[name.name()];
Traintastic::instance->console->notice(id, "Loaded world " + name.value());
}
else
throw std::runtime_error("Can't open file");
}
void World::save()
{
json world;
world["uuid"] = to_string(m_uuid);
world[name.name()] = name.value();
std::ofstream file(m_filename);
if(file.is_open())
{
file << world.dump(2);
Traintastic::instance->console->notice(id, "Saved world " + name.value());
}
else
Traintastic::instance->console->critical(id, "Can't write to world file");
}

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