removed old command station objects

Dieser Commit ist enthalten in:
Reinder Feenstra 2022-01-02 19:07:36 +01:00
Ursprung 14eb29e95b
Commit f84be624b6
25 geänderte Dateien mit 0 neuen und 2694 gelöschten Zeilen

Datei anzeigen

@ -224,29 +224,6 @@ else()
add_definitions(-DDISABLE_LUA_SCRIPTING) add_definitions(-DDISABLE_LUA_SCRIPTING)
endif() endif()
option(USB_XPRESSNET "USB XpressNet interface/controller support" ON)
message(STATUS "USB XpressNet interface/controller support: ${USB_XPRESSNET}")
if(ENABLE_USB_XPRESSNET_INTERFACE)
#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()
add_definitions(-DUSB_XPRESSNET)
target_include_directories(traintastic-server PRIVATE ${USBXPRESSNET_INCLUDE_DIR})
target_link_libraries(traintastic-server PRIVATE ${USBXPRESSNET_LIB})
target_include_directories(traintastic-server-test PRIVATE ${USBXPRESSNET_INCLUDE_DIR})
target_link_libraries(traintastic-server-test PRIVATE ${USBXPRESSNET_LIB})
else()
list(FILTER SOURCES EXCLUDE REGEX ".*usbxpressnet(interface|controller)\.(hpp|cpp)$")
endif()
### OPTIONS END ### ### OPTIONS END ###
target_sources(traintastic-server PRIVATE ${SOURCES}) target_sources(traintastic-server PRIVATE ${SOURCES})

Datei anzeigen

@ -1,204 +0,0 @@
/**
* server/src/hardware/commandstation/commandstation.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 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 "commandstation.hpp"
#include "commandstationlist.hpp"
#include "commandstationlisttablemodel.hpp"
#include <functional>
#include "../../world/world.hpp"
#include "../decoder/decoder.hpp"
#include "../decoder/decoderlist.hpp"
#include "../../core/attributes.hpp"
#include "../../utils/displayname.hpp"
#include "../../utils/almostzero.hpp"
CommandStation::CommandStation(const std::weak_ptr<World>& world, std::string_view _id) :
IdObject(world, _id),
name{this, "name", "", PropertyFlags::ReadWrite | PropertyFlags::Store},
online{this, "online", false, PropertyFlags::ReadWrite | PropertyFlags::NoStore,
[this](bool value)
{
Attributes::setEnabled(emergencyStop, value);
Attributes::setEnabled(powerOn, value);
},
std::bind(&CommandStation::setOnline, this, std::placeholders::_1)},
//status{this, "status", CommandStationStatus::Offline, PropertyFlags::ReadOnly},
emergencyStop{this, "emergency_stop", true, PropertyFlags::ReadWrite | PropertyFlags::NoStore,
[this](bool value)
{
emergencyStopChanged(value);
}},
powerOn{this, "power_on", false, PropertyFlags::ReadWrite | PropertyFlags::NoStore,
[this](bool value)
{
powerOnChanged(value);
}},
decoders{this, "decoders", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject},
controllers{this, "controllers", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject},
notes{this, "notes", "", PropertyFlags::ReadWrite | PropertyFlags::Store}
{
decoders.setValueInternal(std::make_shared<DecoderList>(*this, decoders.name()));
controllers.setValueInternal(std::make_shared<ControllerList>(*this, controllers.name()));
auto w = world.lock();
const bool editable = w && contains(w->state.value(), WorldState::Edit);
Attributes::addDisplayName(name, DisplayName::Object::name);
Attributes::addEnabled(name, editable);
m_interfaceItems.add(name);
Attributes::addDisplayName(online, DisplayName::CommandStation::online);
m_interfaceItems.add(online);
Attributes::addDisplayName(emergencyStop, DisplayName::CommandStation::emergencyStop);
Attributes::addEnabled(emergencyStop, online);
Attributes::addObjectEditor(emergencyStop, false);
m_interfaceItems.insertBefore(emergencyStop, notes);
Attributes::addDisplayName(powerOn, DisplayName::CommandStation::powerOn);
Attributes::addEnabled(powerOn, online);
Attributes::addObjectEditor(powerOn, false);
m_interfaceItems.insertBefore(powerOn, notes);
Attributes::addDisplayName(decoders, DisplayName::World::decoders);
m_interfaceItems.add(decoders);
Attributes::addDisplayName(controllers, DisplayName::World::controllers);
m_interfaceItems.add(controllers);
Attributes::addDisplayName(notes, DisplayName::Object::notes);
m_interfaceItems.add(notes);
}
void CommandStation::loaded()
{
IdObject::loaded();
checkAllDecoders();
}
void CommandStation::addToWorld()
{
IdObject::addToWorld();
if(auto world = m_world.lock())
world->commandStations->addObject(shared_ptr<CommandStation>());
}
void CommandStation::destroying()
{
for(const auto& decoder : *decoders)
{
assert(decoder->commandStation.value() == shared_ptr<CommandStation>());
decoder->commandStation = nullptr;
}
if(auto world = m_world.lock())
world->commandStations->removeObject(shared_ptr<CommandStation>());
IdObject::destroying();
}
void CommandStation::worldEvent(WorldState state, WorldEvent event)
{
IdObject::worldEvent(state, event);
Attributes::setEnabled(name, contains(state, WorldState::Edit));
try
{
switch(event)
{
case WorldEvent::Offline:
online = false;
break;
case WorldEvent::Online:
online = true;
break;
case WorldEvent::PowerOff:
powerOn = false;
break;
case WorldEvent::PowerOn:
powerOn = true;
if(!emergencyStop)
restoreSpeed();
break;
case WorldEvent::Stop:
emergencyStop = true;
break;
case WorldEvent::Run:
emergencyStop = false;
if(powerOn)
restoreSpeed();
break;
default:
break;
}
}
catch(...)
{
}
}
const std::shared_ptr<::Decoder>& CommandStation::getDecoder(DecoderProtocol protocol, uint16_t address, bool longAddress) const
{
auto it = std::find_if(decoders->begin(), decoders->end(), [=](auto& decoder){ return decoder->protocol.value() == protocol && decoder->address.value() == address && decoder->longAddress == longAddress; });
if(it != decoders->end())
return *it;
return Decoder::null;
}
void CommandStation::emergencyStopChanged(bool value)
{
for(auto& controller : *controllers)
controller->emergencyStopChanged(value);
}
void CommandStation::powerOnChanged(bool value)
{
for(auto& controller : *controllers)
controller->powerOnChanged(value);
}
void CommandStation::checkAllDecoders() const
{
for(const auto& decoder : *decoders)
checkDecoder(*decoder);
}
void CommandStation::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
for(auto& controller : *controllers)
controller->decoderChanged(decoder, changes, functionNumber);
}
//! \brief restore speed of all decoders that are not (emergency) stopped
void CommandStation::restoreSpeed()
{
for(const auto& decoder : *decoders)
if(!decoder->emergencyStop && !almostZero(decoder->throttle.value()))
decoderChanged(*decoder, DecoderChangeFlags::Throttle, 0);
}

Datei anzeigen

@ -1,68 +0,0 @@
/**
* server/src/hardware/commandstation/commandstation.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_COMMANDSTATION_HPP
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_COMMANDSTATION_HPP
#include "../../core/idobject.hpp"
#include "../../core/objectproperty.hpp"
#include "../../enum/commandstationstatus.hpp"
#include "../decoder/decoderlist.hpp"
#include "../controller/controllerlist.hpp"
class Decoder;
enum class DecoderChangeFlags;
class CommandStation : public IdObject
{
friend class ::Decoder;
protected:
void loaded() override;
void addToWorld() final;
void destroying() override;
void worldEvent(WorldState state, WorldEvent event) override;
virtual bool setOnline(bool& value) = 0;
virtual void emergencyStopChanged(bool value);
virtual void powerOnChanged(bool value);
void checkAllDecoders() const;
virtual void checkDecoder(const Decoder& /*decoder*/) const {}
virtual void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber);
void restoreSpeed();
public:
CommandStation(const std::weak_ptr<World>& world, std::string_view _id);
Property<std::string> name;
Property<bool> online;
Property<bool> emergencyStop;
Property<bool> powerOn;
ObjectProperty<DecoderList> decoders;
ObjectProperty<ControllerList> controllers;
Property<std::string> notes;
const std::shared_ptr<Decoder>& getDecoder(DecoderProtocol protocol, uint16_t address, bool longAddress = false) const;
};
#endif

Datei anzeigen

@ -1,80 +0,0 @@
/**
* server/src/hardware/commandstation/commandstationlist.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2020 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 "commandstationlist.hpp"
#include "commandstationlisttablemodel.hpp"
#include "commandstations.hpp"
#include "../../world/world.hpp"
#include "../../world/getworld.hpp"
#include "../../core/attributes.hpp"
#include "../../utils/displayname.hpp"
CommandStationList::CommandStationList(Object& _parent, const std::string& parentPropertyName) :
ObjectList<CommandStation>(_parent, parentPropertyName),
add{*this, "add",
[this](std::string_view commandStationClassId)
{
auto world = getWorld(this);
if(!world)
return std::shared_ptr<CommandStation>();
return CommandStations::create(world, commandStationClassId, world->getUniqueId("cs"));
}},
remove{*this, "remove",
[this](const std::shared_ptr<CommandStation>& object)
{
if(containsObject(object))
object->destroy();
assert(!containsObject(object));
}}
{
auto world = getWorld(&_parent);
const bool editable = world && contains(world->state.value(), WorldState::Edit);
Attributes::addDisplayName(add, DisplayName::List::add);
Attributes::addEnabled(add, editable);
Attributes::addClassList(add, CommandStations::classList);
m_interfaceItems.add(add);
Attributes::addDisplayName(remove, DisplayName::List::remove);
Attributes::addEnabled(remove, editable);
m_interfaceItems.add(remove);
}
TableModelPtr CommandStationList::getModel()
{
return std::make_shared<CommandStationListTableModel>(*this);
}
void CommandStationList::worldEvent(WorldState state, WorldEvent event)
{
ObjectList<CommandStation>::worldEvent(state, event);
const bool editable = contains(state, WorldState::Edit);
Attributes::setEnabled(add, editable);
Attributes::setEnabled(remove, editable);
}
bool CommandStationList::isListedProperty(const std::string& name)
{
return CommandStationListTableModel::isListedProperty(name);
}

Datei anzeigen

@ -1,46 +0,0 @@
/**
* server/src/hardware/commandstation/commandstationlist.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2020 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_COMMANDSTATIONLIST_HPP
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_COMMANDSTATIONLIST_HPP
#include "../../core/objectlist.hpp"
#include "commandstation.hpp"
class CommandStationList : public ObjectList<CommandStation>
{
protected:
void worldEvent(WorldState state, WorldEvent event) final;
bool isListedProperty(const std::string& name) final;
public:
CLASS_ID("command_station_list")
Method<std::shared_ptr<CommandStation>(std::string_view)> add;
Method<void(const std::shared_ptr<CommandStation>&)> remove;
CommandStationList(Object& _parent, const std::string& parentPropertyName);
TableModelPtr getModel() final;
};
#endif

Datei anzeigen

@ -1,100 +0,0 @@
/**
* server/src/core/
*
* This file is part of the traintastic source code
*
* Copyright (C) 2019-2021 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 "commandstationlisttablemodel.hpp"
#include "commandstationlist.hpp"
#include "../../utils/utf8.hpp"
#include "../../utils/displayname.hpp"
constexpr uint32_t columnId = 0;
constexpr uint32_t columnName = 1;
constexpr uint32_t columnOnline = 2;
constexpr uint32_t columnEmergencyStop = 3;
constexpr uint32_t columnTrackPower = 4;
bool CommandStationListTableModel::isListedProperty(const std::string& name)
{
return
name == "id" ||
name == "name" ||
name == "online" ||
name == "emergency_stop" ||
name == "power_on";
}
CommandStationListTableModel::CommandStationListTableModel(CommandStationList& list) :
ObjectListTableModel<CommandStation>(list)
{
setColumnHeaders({
DisplayName::Object::id,
DisplayName::Object::name,
DisplayName::CommandStation::online,
DisplayName::CommandStation::emergencyStop,
DisplayName::CommandStation::powerOn,
});
}
std::string CommandStationListTableModel::getText(uint32_t column, uint32_t row) const
{
if(row < rowCount())
{
const CommandStation& cs = getItem(row);
switch(column)
{
case columnId:
return cs.id;
case columnName:
return cs.name;
case columnOnline:
return cs.online ? "\u2022" : "";
case columnEmergencyStop:
return cs.emergencyStop ? "\u2022" : "";
case columnTrackPower:
return cs.powerOn ? UTF8_CHECKMARK : UTF8_BALLOT_X;
default:
assert(false);
break;
}
}
return "";
}
void CommandStationListTableModel::propertyChanged(BaseProperty& property, uint32_t row)
{
if(property.name() == "id")
changed(row, columnId);
else if(property.name() == "name")
changed(row, columnName);
else if(property.name() == "online")
changed(row, columnOnline);
else if(property.name() == "emergency_stop")
changed(row, columnEmergencyStop);
else if(property.name() == "track_voltage_off")
changed(row, columnTrackPower);
}

Datei anzeigen

@ -1,48 +0,0 @@
/**
* server/src/hardware/commandstation/commandstationlisttablemodel.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_COMMANDSTATIONLISTTABLEMODEL_HPP
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_COMMANDSTATIONLISTTABLEMODEL_HPP
#include "../../core/objectlisttablemodel.hpp"
#include "commandstationlist.hpp"
class CommandStationList;
class CommandStationListTableModel : public ObjectListTableModel<CommandStation>
{
friend class CommandStationList;
protected:
void propertyChanged(BaseProperty& property, uint32_t row) final;
public:
CLASS_ID("command_station_list_table_model")
static bool isListedProperty(const std::string& name);
CommandStationListTableModel(CommandStationList& commandStationList);
std::string getText(uint32_t column, uint32_t row) const final;
};
#endif

Datei anzeigen

@ -1,45 +0,0 @@
/**
* server/src/hardware/commandstation/commandstations.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 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 "commandstations.hpp"
std::shared_ptr<CommandStation> CommandStations::create(const std::weak_ptr<World>& world, std::string_view classId, std::string_view id)
{
if(classId == DCCPlusPlusSerial::classId)
return DCCPlusPlusSerial::create(world, id);
if(classId == LocoNetSerial::classId)
return LocoNetSerial::create(world, id);
else if(classId == LocoNetTCPBinary::classId)
return LocoNetTCPBinary::create(world, id);
#ifdef USB_XPRESSNET
else if(classId == USBXpressNetInterface::classId)
return USBXpressNetInterface::create(world, id);
#endif
else if(classId == XpressNetSerial::classId)
return XpressNetSerial::create(world, id);
else if(classId == RocoZ21::classId)
return RocoZ21::create(world, id);
else if(classId == VirtualCommandStation::classId)
return VirtualCommandStation::create(world, id);
else
return std::shared_ptr<CommandStation>();
}

Datei anzeigen

@ -1,58 +0,0 @@
/**
* server/src/hardware/commandstation/create.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_COMMANDSTATIONS_HPP
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_COMMANDSTATIONS_HPP
#include "commandstation.hpp"
#include "../../utils/makearray.hpp"
#include "dccplusplusserial.hpp"
#include "loconetserial.hpp"
#include "loconettcpbinary.hpp"
#ifdef USB_XPRESSNET
#include "usbxpressnetinterface.hpp"
#endif
#include "xpressnetserial.hpp"
#include "rocoz21.hpp"
#include "virtualcommandstation.hpp"
struct CommandStations
{
static constexpr std::string_view classIdPrefix = "command_station.";
static constexpr auto classList = makeArray(
DCCPlusPlusSerial::classId,
LocoNetSerial::classId,
LocoNetTCPBinary::classId,
#ifdef USB_XPRESSNET
USBXpressNetInterface::classId,
#endif
XpressNetSerial::classId,
RocoZ21::classId,
VirtualCommandStation::classId
);
static std::shared_ptr<CommandStation> create(const std::weak_ptr<World>& world, std::string_view classId, std::string_view id);
};
#endif

Datei anzeigen

@ -1,133 +0,0 @@
/**
* server/src/hardware/commandstation/dccplusplusserial.cpp
*
* This file is part of the traintastic source code
*
* Copyright (C) 2021 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 "dccplusplusserial.hpp"
#include "../protocol/dccplusplus/messages.hpp"
#include "../../core/eventloop.hpp"
#include "../../log/log.hpp"
DCCPlusPlusSerial::DCCPlusPlusSerial(const std::weak_ptr<World>& world, std::string_view _id) :
SerialCommandStation(world, _id),
dccPlusPlus{this, "dcc_plus_plus", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
{
name = "DCC++ (serial)";
baudrate = 115200;
dccPlusPlus.setValueInternal(std::make_shared<DCCPlusPlus::DCCPlusPlus>(*this, dccPlusPlus.name(), std::bind(&DCCPlusPlusSerial::send, this, std::placeholders::_1)));
m_interfaceItems.insertBefore(dccPlusPlus, notes);
}
void DCCPlusPlusSerial::emergencyStopChanged(bool value)
{
CommandStation::emergencyStopChanged(value);
if(online)
dccPlusPlus->emergencyStopChanged(value);
}
void DCCPlusPlusSerial::powerOnChanged(bool value)
{
CommandStation::powerOnChanged(value);
if(online)
dccPlusPlus->powerOnChanged(value);
}
void DCCPlusPlusSerial::checkDecoder(const Decoder& decoder) const
{
CommandStation::checkDecoder(decoder);
dccPlusPlus->checkDecoder(decoder);
}
void DCCPlusPlusSerial::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
CommandStation::decoderChanged(decoder, changes, functionNumber);
if(online)
dccPlusPlus->decoderChanged(decoder, changes, functionNumber);
}
bool DCCPlusPlusSerial::send(std::string_view message)
{
if(!m_serialPort.is_open())
return false;
// for now a blocking implementation, will be replaced by async.
boost::system::error_code ec;
size_t todo = message.size();
while(todo > 0)
{
todo -= m_serialPort.write_some(boost::asio::buffer(message.data(), message.size()), ec);
if(ec)
{
Log::log(*this, LogMessage::E2001_SERIAL_WRITE_FAILED_X, ec);
return false;
}
}
return true;
}
void DCCPlusPlusSerial::started()
{
send(DCCPlusPlus::Ex::setSpeedSteps(dccPlusPlus->speedSteps));
}
void DCCPlusPlusSerial::read()
{
m_serialPort.async_read_some(boost::asio::buffer(m_readBuffer.data() + m_readBufferOffset, m_readBuffer.size() - m_readBufferOffset),
[this](const boost::system::error_code& ec, std::size_t bytesTransferred)
{
if(!ec)
{
const char* pos = reinterpret_cast<const char*>(m_readBuffer.data());
bytesTransferred += m_readBufferOffset;
size_t i = 0;
while(i < bytesTransferred)
{
if(*(pos + i) == '\n')
{
dccPlusPlus->receive(std::string_view{pos, i + 1});
pos += i + 1;
bytesTransferred -= i + 1;
i = 0;
}
else
i++;
}
if(bytesTransferred != 0)
memmove(m_readBuffer.data(), pos, bytesTransferred);
m_readBufferOffset = bytesTransferred;
read();
}
else
EventLoop::call(
[this, ec]()
{
Log::log(*this, LogMessage::E2002_SERIAL_READ_FAILED_X, ec);
online = false;
});
});
}

Datei anzeigen

@ -1,50 +0,0 @@
/**
* server/src/hardware/commandstation/dccplusplusserial.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_dccplusplusserial_HPP
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_dccplusplusserial_HPP
#include "serialcommandstation.hpp"
#include "../protocol/dccplusplus/dccplusplus.hpp"
class DCCPlusPlusSerial : public SerialCommandStation
{
protected:
void emergencyStopChanged(bool value) final;
void powerOnChanged(bool value) final;
void checkDecoder(const Decoder& decoder) const;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
bool send(std::string_view message);
void started() final;
void read() final;
public:
CLASS_ID("command_station.dccplusplus_serial")
CREATE(DCCPlusPlusSerial)
ObjectProperty<DCCPlusPlus::DCCPlusPlus> dccPlusPlus;
DCCPlusPlusSerial(const std::weak_ptr<World>& world, std::string_view _id);
};
#endif

Datei anzeigen

@ -1,162 +0,0 @@
/**
* hardware/commandstation/loconetserial.cpp
*
* This file is part of the traintastic source code
*
* Copyright (C) 2019-2021 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 "loconetserial.hpp"
#include "../../core/traintastic.hpp"
#include "../../core/eventloop.hpp"
#include "../../core/attributes.hpp"
#include "../../log/log.hpp"
#include "../../utils/displayname.hpp"
LocoNetSerial::LocoNetSerial(const std::weak_ptr<World>& world, std::string_view _id) :
SerialCommandStation(world, _id),
interface{this, "interface", LocoNetSerialInterface::Custom, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](LocoNetSerialInterface value)
{
switch(value)
{
case LocoNetSerialInterface::Custom:
break;
case LocoNetSerialInterface::DigikeijsDR5000:
baudrate = 115200;
flowControl = SerialFlowControl::Hardware;
break;
case LocoNetSerialInterface::RoSoftLocoNetInterface:
baudrate = 19200;
flowControl = SerialFlowControl::Hardware;
break;
}
}},
loconet{this, "loconet", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
{
name = "LocoNet (serial)";
loconet.setValueInternal(LocoNet::LocoNet::create(*this, loconet.name(), std::bind(&LocoNetSerial::send, this, std::placeholders::_1)));
Attributes::addEnabled(interface, !online);
Attributes::addValues(interface, LocoNetSerialInterfaceValues);
m_interfaceItems.insertBefore(interface, baudrate);
Attributes::addDisplayName(loconet, DisplayName::Hardware::loconet);
m_interfaceItems.insertBefore(loconet, notes);
}
void LocoNetSerial::emergencyStopChanged(bool value)
{
CommandStation::emergencyStopChanged(value);
if(online)
loconet->emergencyStopChanged(value);
}
void LocoNetSerial::powerOnChanged(bool value)
{
CommandStation::powerOnChanged(value);
if(online)
loconet->powerOnChanged(value);
}
void LocoNetSerial::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
CommandStation::decoderChanged(decoder, changes, functionNumber);
if(online)
loconet->decoderChanged(decoder, changes, functionNumber);
}
bool LocoNetSerial::send(const LocoNet::Message& message)
{
if(!m_serialPort.is_open())
return false;
boost::system::error_code ec;
m_serialPort.write_some(boost::asio::buffer(static_cast<const void*>(&message), message.size()), ec); // TODO async
if(ec)
{
Log::log(*this, LogMessage::E2001_SERIAL_WRITE_FAILED_X, ec);
return false;
}
return true;
}
void LocoNetSerial::started()
{
loconet->queryLocoSlots();
}
void LocoNetSerial::read()
{
m_serialPort.async_read_some(boost::asio::buffer(m_readBuffer.data() + m_readBufferOffset, m_readBuffer.size() - m_readBufferOffset),
[this](const boost::system::error_code& ec, std::size_t bytesTransferred)
{
if(!ec)
{
const uint8_t* pos = m_readBuffer.data();
bytesTransferred += m_readBufferOffset;
while(bytesTransferred > 1)
{
const LocoNet::Message* message = reinterpret_cast<const LocoNet::Message*>(pos);
size_t drop = 0;
while((message->size() == 0 || (message->size() <= bytesTransferred && !LocoNet::isValid(*message))) && drop < bytesTransferred)
{
drop++;
pos++;
bytesTransferred--;
message = reinterpret_cast<const LocoNet::Message*>(pos);
}
if(drop != 0)
{
EventLoop::call(
[this, drop]()
{
Log::log(*this, LogMessage::W2001_RECEIVED_MALFORMED_DATA_DROPPED_X_BYTES, drop);
});
}
else if(message->size() <= bytesTransferred)
{
loconet->receive(*message);
pos += message->size();
bytesTransferred -= message->size();
}
else
break;
}
if(bytesTransferred != 0)
memmove(m_readBuffer.data(), pos, bytesTransferred);
m_readBufferOffset = bytesTransferred;
read();
}
else
EventLoop::call(
[this, ec]()
{
Log::log(*this, LogMessage::E2002_SERIAL_READ_FAILED_X, ec);
online = false;
});
});
}

Datei anzeigen

@ -1,52 +0,0 @@
/**
* server/src/hardware/commandstation/loconetserial.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_LOCONETSERIAL_HPP
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_LOCONETSERIAL_HPP
#include "serialcommandstation.hpp"
#include "../protocol/loconet/loconet.hpp"
#include "../../enum/loconetserialinterface.hpp"
class LocoNetSerial : public SerialCommandStation
{
protected:
void emergencyStopChanged(bool value) final;
void powerOnChanged(bool value) final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
bool start();
bool send(const LocoNet::Message& msg);
void started() final;
void read() final;
public:
CLASS_ID("command_station.loconet_serial")
CREATE(LocoNetSerial)
Property<LocoNetSerialInterface> interface;
ObjectProperty<LocoNet::LocoNet> loconet;
LocoNetSerial(const std::weak_ptr<World>& world, std::string_view _id);
};
#endif

Datei anzeigen

@ -1,192 +0,0 @@
/**
* hardware/commandstation/loconettcpbinary.cpp
*
* This file is part of the traintastic source code
*
* Copyright (C) 2021 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 "loconettcpbinary.hpp"
#include "../../core/traintastic.hpp"
#include "../../core/eventloop.hpp"
#include "../../core/attributes.hpp"
#include "../../log/log.hpp"
#include "../../utils/displayname.hpp"
LocoNetTCPBinary::LocoNetTCPBinary(const std::weak_ptr<World>& world, std::string_view _id) :
CommandStation(world, _id),
m_socket{Traintastic::instance->ioContext()},
hostname{this, "hostname", "", PropertyFlags::ReadWrite | PropertyFlags::Store},
port{this, "port", 5550, PropertyFlags::ReadWrite | PropertyFlags::Store},
loconet{this, "loconet", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
{
name = "LocoNet (TCP binary)";
loconet.setValueInternal(LocoNet::LocoNet::create(*this, loconet.name(), std::bind(&LocoNetTCPBinary::send, this, std::placeholders::_1)));
Attributes::addDisplayName(hostname, DisplayName::IP::hostname);
Attributes::addEnabled(hostname, true);
m_interfaceItems.insertBefore(hostname, notes);
Attributes::addDisplayName(port, DisplayName::IP::port);
Attributes::addEnabled(port, true);
m_interfaceItems.insertBefore(port, notes);
Attributes::addDisplayName(loconet, DisplayName::Hardware::loconet);
m_interfaceItems.insertBefore(loconet, notes);
}
bool LocoNetTCPBinary::setOnline(bool& value)
{
if(!m_socket.is_open() && value)
{
boost::system::error_code ec;
boost::asio::ip::tcp::endpoint endpoint;
endpoint.port(port);
endpoint.address(boost::asio::ip::make_address(hostname, ec));
if(ec)
{
Log::log(*this, LogMessage::E2003_MAKE_ADDRESS_FAILED_X, ec);
return false;
}
m_socket.connect(endpoint, ec);
if(ec)
{
Log::log(*this, LogMessage::E2005_SOCKET_CONNECT_FAILED_X, ec);
return false;
}
m_socket.set_option(boost::asio::socket_base::linger(true, 0));
m_socket.set_option(boost::asio::ip::tcp::no_delay(true));
receive(); // start receiving messages
Attributes::setEnabled(hostname, false);
Attributes::setEnabled(port, false);
}
else if(m_socket.is_open() && !value)
{
Attributes::setEnabled(hostname, true);
Attributes::setEnabled(port, true);
m_socket.close();
}
return true;
}
void LocoNetTCPBinary::emergencyStopChanged(bool value)
{
CommandStation::emergencyStopChanged(value);
if(online)
loconet->emergencyStopChanged(value);
}
void LocoNetTCPBinary::powerOnChanged(bool value)
{
CommandStation::powerOnChanged(value);
if(online)
loconet->powerOnChanged(value);
}
void LocoNetTCPBinary::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
CommandStation::decoderChanged(decoder, changes, functionNumber);
if(online)
loconet->decoderChanged(decoder, changes, functionNumber);
}
void LocoNetTCPBinary::receive()
{
m_socket.async_read_some(boost::asio::buffer(m_readBuffer.data() + m_readBufferOffset, m_readBuffer.size() - m_readBufferOffset),
[this](const boost::system::error_code& ec, std::size_t bytesTransferred)
{
if(!ec)
{
const uint8_t* pos = m_readBuffer.data();
bytesTransferred += m_readBufferOffset;
while(bytesTransferred > 1)
{
const LocoNet::Message* message = reinterpret_cast<const LocoNet::Message*>(pos);
size_t drop = 0;
while((message->size() == 0 || (message->size() <= bytesTransferred && !LocoNet::isValid(*message))) && drop < bytesTransferred)
{
drop++;
pos++;
bytesTransferred--;
message = reinterpret_cast<const LocoNet::Message*>(pos);
}
if(drop != 0)
{
EventLoop::call(
[this, drop]()
{
Log::log(*this, LogMessage::W2001_RECEIVED_MALFORMED_DATA_DROPPED_X_BYTES, drop);
});
}
else if(message->size() <= bytesTransferred)
{
loconet->receive(*message);
pos += message->size();
bytesTransferred -= message->size();
}
else
break;
}
if(bytesTransferred != 0)
memmove(m_readBuffer.data(), pos, bytesTransferred);
m_readBufferOffset = bytesTransferred;
receive();
}
else
EventLoop::call(
[this, ec]()
{
Log::log(*this, LogMessage::E2008_SOCKET_READ_FAILED_X, ec);
online = false;
});
});
}
bool LocoNetTCPBinary::send(const LocoNet::Message& message)
{
if(!m_socket.is_open())
return false;
// for now a blocking implementation, will be replaced by async.
boost::system::error_code ec;
size_t todo = message.size();
while(todo > 0)
{
todo -= m_socket.write_some(boost::asio::buffer(static_cast<const void*>(&message), message.size()), ec);
if(ec)
{
Log::log(*this, LogMessage::E2007_SOCKET_WRITE_FAILED_X, ec);
return false;
}
}
return true;
}

Datei anzeigen

@ -1,56 +0,0 @@
/**
* server/src/hardware/commandstation/loconettcpbinary.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_LOCONETTCPBINARY_HPP
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_LOCONETTCPBINARY_HPP
#include "commandstation.hpp"
#include <boost/asio.hpp>
#include "../protocol/loconet/loconet.hpp"
class LocoNetTCPBinary : public CommandStation
{
protected:
boost::asio::ip::tcp::socket m_socket;
std::array<uint8_t, 1024> m_readBuffer;
uint16_t m_readBufferOffset;
bool setOnline(bool& value) final;
void emergencyStopChanged(bool value) final;
void powerOnChanged(bool value) final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
void receive();
bool send(const LocoNet::Message& msg);
public:
CLASS_ID("command_station.loconet_tcp_binary")
CREATE(LocoNetTCPBinary)
Property<std::string> hostname;
Property<uint16_t> port;
ObjectProperty<LocoNet::LocoNet> loconet;
LocoNetTCPBinary(const std::weak_ptr<World>& world, std::string_view _id);
};
#endif

Datei anzeigen

@ -1,524 +0,0 @@
/**
* server/src/hardware/commandstation/rocoz21.cpp
*
* This file is part of the traintastic source code
*
* Copyright (C) 2019-2021 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 "rocoz21.hpp"
#include "../../core/traintastic.hpp"
#include "../../world/world.hpp"
#include "../../core/eventloop.hpp"
#include "../../core/attributes.hpp"
#include "../decoder/decoderchangeflags.hpp"
#include "../protocol/xpressnet/messages.hpp"
#include "../protocol/z21/messages.hpp"
#include "../../utils/tohex.hpp"
#include "../../utils/category.hpp"
#include "../../log/log.hpp"
#include "../../utils/displayname.hpp"
#define SET_ADDRESS \
if(decoder.longAddress) \
{ \
cmd.addressHigh = 0xc0 | (decoder.address >> 8); \
cmd.addressLow = decoder.address & 0xff; \
} \
else \
{ \
cmd.addressHigh = 0; \
cmd.addressLow = decoder.address; \
}
RocoZ21::RocoZ21(const std::weak_ptr<World>& world, std::string_view _id) :
CommandStation(world, _id),
m_socket{Traintastic::instance->ioContext()},
hostname{this, "hostname", "", PropertyFlags::ReadWrite | PropertyFlags::Store},
port{this, "port", 21105, PropertyFlags::ReadWrite | PropertyFlags::Store},
loconet{this, "loconet", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject},
serialNumber{this, "serial_number", "", PropertyFlags::ReadOnly},
hardwareType{this, "hardware_type", "", PropertyFlags::ReadOnly},
firmwareVersion{this, "firmware_version", "", PropertyFlags::ReadOnly},
mainCurrent{this, "main_current", std::numeric_limits<float>::quiet_NaN(), PropertyFlags::ReadOnly},
progCurrent{this, "prog_current", std::numeric_limits<float>::quiet_NaN(), PropertyFlags::ReadOnly},
filteredMainCurrent{this, "filtered_main_current", std::numeric_limits<float>::quiet_NaN(), PropertyFlags::ReadOnly},
temperature{this, "temperature", std::numeric_limits<float>::quiet_NaN(), PropertyFlags::ReadOnly},
supplyVoltage{this, "supply_voltage", std::numeric_limits<float>::quiet_NaN(), PropertyFlags::ReadOnly},
vccVoltage{this, "vcc_voltage", std::numeric_limits<float>::quiet_NaN(), PropertyFlags::ReadOnly},
shortCircuit{this, "short_circuit", false, PropertyFlags::ReadOnly},
programmingModeActive{this, "programming_mode_active", false, PropertyFlags::ReadOnly},
highTemperature{this, "high_temperature", false, PropertyFlags::ReadOnly},
powerLost{this, "power_lost", false, PropertyFlags::ReadOnly},
shortCircutInternal{this, "short_circut_internal", false, PropertyFlags::ReadOnly},
shortCircutExternal{this, "short_circut_external", false, PropertyFlags::ReadOnly}
{
name = "Z21";
loconet.setValueInternal(LocoNet::LocoNet::create(*this, loconet.name(),
[/*this*/](const ::LocoNet::Message& /*msg*/)
{
return false;
}));
Attributes::addDisplayName(hostname, DisplayName::IP::hostname);
Attributes::addEnabled(hostname, true);
m_interfaceItems.insertBefore(hostname, notes);
Attributes::addDisplayName(port, DisplayName::IP::port);
Attributes::addEnabled(port, true);
m_interfaceItems.insertBefore(port, notes);
Attributes::addDisplayName(loconet, DisplayName::Hardware::loconet);
m_interfaceItems.insertBefore(loconet, notes);
Attributes::addCategory(serialNumber, Category::info);
m_interfaceItems.insertBefore(serialNumber, notes);
Attributes::addCategory(hardwareType, Category::info);
m_interfaceItems.insertBefore(hardwareType, notes);
Attributes::addCategory(firmwareVersion, Category::info);
m_interfaceItems.insertBefore(firmwareVersion, notes);
Attributes::addCategory(mainCurrent, Category::info);
m_interfaceItems.insertBefore(mainCurrent, notes);
Attributes::addCategory(progCurrent, Category::info);
m_interfaceItems.insertBefore(progCurrent, notes);
Attributes::addCategory(filteredMainCurrent, Category::info);
m_interfaceItems.insertBefore(filteredMainCurrent, notes);
Attributes::addCategory(temperature, Category::info);
m_interfaceItems.insertBefore(temperature, notes);
Attributes::addCategory(supplyVoltage, Category::info);
m_interfaceItems.insertBefore(supplyVoltage, notes);
Attributes::addCategory(vccVoltage, Category::info);
m_interfaceItems.insertBefore(vccVoltage, notes);
Attributes::addCategory(shortCircuit, Category::info);
m_interfaceItems.insertBefore(shortCircuit, notes);
Attributes::addCategory(programmingModeActive, Category::info);
m_interfaceItems.insertBefore(programmingModeActive, notes);
Attributes::addCategory(highTemperature, Category::info);
m_interfaceItems.insertBefore(highTemperature, notes);
Attributes::addCategory(powerLost, Category::info);
m_interfaceItems.insertBefore(powerLost, notes);
Attributes::addCategory(shortCircutInternal, Category::info);
m_interfaceItems.insertBefore(shortCircutInternal, notes);
Attributes::addCategory(shortCircutExternal, Category::info);
m_interfaceItems.insertBefore(shortCircutExternal, notes);
}
void RocoZ21::emergencyStopChanged(bool value)
{
if(online && value)
send(Z21::LanXSetStop());
}
void RocoZ21::powerOnChanged(bool value)
{
if(online)
{
if(value)
send(Z21::LanXSetTrackPowerOn());
else
send(Z21::LanXSetTrackPowerOff());
}
}
/*
bool RocoZ21::isDecoderSupported(Decoder& decoder) const
{
return
decoder.protocol == DecoderProtocol::DCC &&
decoder.address >= 1 &&
decoder.address <= (decoder.longAddress ? 9999 : 127);
}
*/
void RocoZ21::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
if(has(changes, DecoderChangeFlags::EmergencyStop | DecoderChangeFlags::Direction | DecoderChangeFlags::Throttle | DecoderChangeFlags::SpeedSteps))
{
Z21::LanXSetLocoDrive cmd;
//cmd.dataLen = sizeof(cmd);
//cmd.header = Z21::LAN_X;
SET_ADDRESS;
switch(decoder.speedSteps)
{
case 14:
{
const uint8_t speedStep = Decoder::throttleToSpeedStep(decoder.throttle, 14);
cmd.db0 = 0x10;
if(decoder.emergencyStop)
cmd.speedAndDirection = 0x01;
else if(speedStep > 0)
cmd.speedAndDirection = speedStep + 1;
break;
}
case 28:
{
uint8_t speedStep = Decoder::throttleToSpeedStep(decoder.throttle, 28);
cmd.db0 = 0x12;
if(decoder.emergencyStop)
cmd.speedAndDirection = 0x01;
else if(speedStep > 0)
{
speedStep++;
cmd.speedAndDirection = ((speedStep & 0x01) << 4) | (speedStep >> 1);
}
break;
}
case 126:
case 128:
default:
{
const uint8_t speedStep = Decoder::throttleToSpeedStep(decoder.throttle, 126);
cmd.db0 = 0x13;
if(decoder.emergencyStop)
cmd.speedAndDirection = 0x01;
else if(speedStep > 0)
cmd.speedAndDirection = speedStep + 1;
break;
}
}
if(decoder.direction.value() == Direction::Forward)
cmd.speedAndDirection |= 0x80;
cmd.checksum = XpressNet::calcChecksum(*reinterpret_cast<const XpressNet::Message*>(&cmd.xheader));
send(cmd);
}
else if(has(changes, DecoderChangeFlags::FunctionValue))
{
if(functionNumber <= 28)
{
if(const auto& f = decoder.getFunction(functionNumber))
{
Z21::LanXSetLocoFunction cmd;
//cmd.dataLen = sizeof(cmd);
//cmd.header = Z21_LAN_X;
SET_ADDRESS;
cmd.db3 = (f->value ? 0x40 : 0x00) | static_cast<uint8_t>(functionNumber);
cmd.checksum = XpressNet::calcChecksum(*reinterpret_cast<const XpressNet::Message*>(&cmd.xheader));
send(cmd);
}
}
}
}
bool RocoZ21::setOnline(bool& value)
{
if(!m_socket.is_open() && value)
{
boost::system::error_code ec;
m_remoteEndpoint.port(port);
m_remoteEndpoint.address(boost::asio::ip::make_address(hostname, ec));
if(ec)
{
Log::log(*this, LogMessage::E2003_MAKE_ADDRESS_FAILED_X, ec);
return false;
}
if(m_socket.open(boost::asio::ip::udp::v4(), ec))
{
Log::log(*this, LogMessage::E2004_SOCKET_OPEN_FAILED_X, ec);
return false;
}
else if(m_socket.bind(boost::asio::ip::udp::endpoint(boost::asio::ip::address_v4::any(), port), ec))
{
m_socket.close();
Log::log(*this, LogMessage::E2006_SOCKET_BIND_FAILED_X, ec);
return false;
}
receive(); // start receiving messages
send(Z21::LanSetBroadcastFlags(0x07000000 | /*0x00010000 |*/ 0x00000100 | 0x00000001));
// try to communicate with Z21
send(Z21::LanGetSerialNumber());
send(Z21::LanGetHardwareInfo());
send(Z21::LanGetBroadcastFlags());
send(Z21::LanSystemStateGetData());
for(auto& decoder : *decoders)
send(Z21::LanXGetLocoInfo(decoder->address, decoder->longAddress));
Attributes::setEnabled(hostname, false);
Attributes::setEnabled(port, false);
}
else if(m_socket.is_open() && !value)
{
send(Z21::LanLogoff());
serialNumber.setValueInternal("");
hardwareType.setValueInternal("");
firmwareVersion.setValueInternal("");
Attributes::setEnabled(hostname, true);
Attributes::setEnabled(port, true);
m_socket.close();
}
return true;
}
void RocoZ21::receive()
{
m_socket.async_receive_from(boost::asio::buffer(m_receiveBuffer), m_receiveEndpoint,
[this](const boost::system::error_code& ec, std::size_t bytesReceived)
{
if(!ec)
{
if((bytesReceived >= sizeof(Z21::Message)))
{
const Z21::Message* message = reinterpret_cast<const Z21::Message*>(m_receiveBuffer.data());
//const z21_lan_header* cmd = reinterpret_cast<const z21_lan_header*>(m_receiveBuffer.data());
switch(message->header())
{
case Z21::LAN_GET_SERIAL_NUMBER:
if(message->dataLen() == sizeof(Z21::LanGetSerialNumberReply))
{
EventLoop::call(
[this, value=std::to_string(static_cast<const Z21::LanGetSerialNumberReply*>(message)->serialNumber())]()
{
serialNumber.setValueInternal(value);
});
}
break;
case Z21::LAN_GET_HWINFO:
{
const Z21::LanGetHardwareInfoReply* reply = static_cast<const Z21::LanGetHardwareInfoReply*>(message);
std::string hwType;
switch(reply->hardwareType())
{
case Z21::HWT_Z21_OLD:
hwType = "Black Z21 (hardware variant from 2012)";
break;
case Z21::HWT_Z21_NEW:
hwType = "Black Z21 (hardware variant from 2013)";
break;
case Z21::HWT_SMARTRAIL:
hwType = "SmartRail (from 2012)";
break;
case Z21::HWT_Z21_SMALL:
hwType = "White Z21 (starter set variant from 2013)";
break;
case Z21::HWT_Z21_START :
hwType = "Z21 start (starter set variant from 2016)";
break;
default:
hwType = "0x" + toHex(reply->hardwareType());
break;
}
const std::string fwVersion = std::to_string(reply->firmwareVersionMajor()) + "." + std::to_string(reply->firmwareVersionMinor());
EventLoop::call(
[this, hwType, fwVersion]()
{
hardwareType.setValueInternal(hwType);
firmwareVersion.setValueInternal(fwVersion);
});
break;
}
case Z21::LAN_X:
{
// TODO check XOR
const uint8_t xheader = static_cast<const Z21::LanX*>(message)->xheader;
switch(xheader)
{
case Z21::LAN_X_LOCO_INFO:
{
const Z21::LanXLocoInfo* info = static_cast<const Z21::LanXLocoInfo*>(message);
const uint16_t address = (static_cast<uint16_t>(info->addressHigh) << 8) | info->addressLow;
const uint8_t speedStepMode = info->db2 & 0x07;
const Direction direction = (info->speedAndDirection & 0x80) ? Direction::Forward : Direction::Reverse;
const uint8_t speedStep = info->speedAndDirection & 0x7f;
const uint32_t functions =
((info->db4 & 0x10) >> 4) |
((info->db4 & 0x0f) << 1) |
(static_cast<uint32_t>(info->f5f12) << 5) |
(static_cast<uint32_t>(info->f13f20) << 13) |
(static_cast<uint32_t>(info->f21f28) << 21);
EventLoop::call(
[this, address, speedStepMode, direction, speedStep, functions]()
{
const std::shared_ptr<Decoder>& decoder = getDecoder(DecoderProtocol::DCC, address & 0x3fff, address & 0xc000);
if(decoder)
{
decoder->direction = direction;
switch(speedStepMode)
{
case 0:
decoder->throttle.setValueInternal(Decoder::speedStepToThrottle(speedStep, 14));
break;
case 2:
decoder->throttle.setValueInternal(Decoder::speedStepToThrottle(speedStep, 28));
break;
case 4:
decoder->throttle.setValueInternal(Decoder::speedStepToThrottle(speedStep, 126));
break;
}
for(auto& function : *decoder->functions)
{
const uint8_t number = function->number;
if(number <= 28)
function->value.setValueInternal(functions & (1 << number));
}
}
});
break;
}
case Z21::LAN_X_BC:
{
break;
}
case Z21::LAN_X_BC_STOPPED:
EventLoop::call(
[this]()
{
emergencyStop.setValueInternal(true);
});
break;
default:
EventLoop::call([this, xheader](){ Log::log(*this, LogMessage::D2003_UNKNOWN_XHEADER_0XX, toHex(xheader)); });
break;
}
break;
}
case Z21::LAN_SYSTEMSTATE_DATACHANGED:
{
/*
const Z21::LanSystemStateDataChanged state = *reinterpret_cast<const Z21::LanSystemStateDataChanged*>(m_receiveBuffer.data());
EventLoop::call(
[this, state]()
{
mainCurrent.setValueInternal(state.mainCurrent / 1e3); //!< Current on the main track in mA
progCurrent.setValueInternal(state.progCurrent / 1e3); //!< Current on programming track in mA;
filteredMainCurrent.setValueInternal(state.filteredMainCurrent / 1e3); //!< Smoothed current on the main track in mA
temperature.setValueInternal(state.temperature); //!< Command station internal temperature in °C
supplyVoltage.setValueInternal(state.supplyVoltage / 1e3); //!< Supply voltage in mV
vccVoltage.setValueInternal(state.vccVoltage / 1e3); //!< Internal voltage, identical to track voltage in mV
emergencyStop.setValueInternal(state.centralState & Z21_CENTRALSTATE_EMERGENCYSTOP);
trackVoltageOff.setValueInternal(state.centralState & Z21_CENTRALSTATE_TRACKVOLTAGEOFF);
shortCircuit.setValueInternal(state.centralState & Z21_CENTRALSTATE_SHORTCIRCUIT);
programmingModeActive.setValueInternal(state.centralState & Z21_CENTRALSTATE_PROGRAMMINGMODEACTIVE);
highTemperature.setValueInternal(state.centralStateEx & Z21_CENTRALSTATEEX_HIGHTEMPERATURE);
powerLost.setValueInternal(state.centralStateEx & Z21_CENTRALSTATEEX_POWERLOST);
shortCircutInternal.setValueInternal(state.centralStateEx & Z21_CENTRALSTATEEX_SHORTCIRCUITEXTERNAL);
shortCircutExternal.setValueInternal(state.centralStateEx & Z21_CENTRALSTATEEX_SHORTCIRCUITINTERNAL);
});
*/
break;
}
case Z21::LAN_LOCONET_Z21_RX:
//case Z21_LAN_LOCONET_Z21_TX:
//case Z21_LAN_LOCONET_Z21_LAN:
loconet->receive(*reinterpret_cast<const ::LocoNet::Message*>(m_receiveBuffer.data() + sizeof(Z21::Message)));
break;
/*
using LocoNet = LocoNet;
const LocoNet::Header* message = reinterpret_cast<const LocoNet::Header*>(m_receiveBuffer.data() + sizeof(z21_lan_header));
switch(message->opCode)
{
case LocoNet::OPC_INPUT_REP:
{
const LocoNet::InputRep* inputRep = static_cast<const LocoNet::InputRep*>(message);
//if(debugEnabled)
{
const std::string message = "loconet rx OPC_INPUT_REP:"
" address=" + std::to_string(inputRep->address()) +
" input=" + (inputRep->isAuxInput() ? "aux" : "switch") +
" value=" + (inputRep->value() ? "high" : "low");
}
break;
}
default:
//if(debugEnabled)
{
std::string message = "unknown loconet message: ";
for(int i = 4; i < cmd->dataLen; i++)
message += toHex(reinterpret_cast<const uint8_t*>(cmd)[i]);
}
break;
}
*/
default:
//if(debugEnabled)
{
std::string data = "dataLen=0x" + toHex(message->dataLen()) + ", header=0x" + toHex(message->header());
if(message->dataLen() > 4)
{
data += ", data=";
for(int i = sizeof(Z21::Message); i < message->dataLen(); i++)
data += toHex(reinterpret_cast<const uint8_t*>(message)[i]);
}
EventLoop::call([this, data](){ Log::log(*this, LogMessage::D2006_UNKNOWN_MESSAGE_X, data); });
}
break;
}
}
receive();
}
else
EventLoop::call([this, ec](){ Log::log(*this, LogMessage::E2009_SOCKET_RECEIVE_FAILED_X, ec); });
});
}
void RocoZ21::send(const Z21::Message& message)
{
// TODO async
// TODO: add to queue, send async
boost::system::error_code ec;
m_socket.send_to(boost::asio::buffer(&message, message.dataLen()), m_remoteEndpoint, 0, ec);
if(ec)
Log::log(*this, LogMessage::E2011_SOCKET_SEND_FAILED_X, ec);
/*
m_socket.send_to(boost::asio:buffer(&message, message.dataLen()), 0, m_remoteEndpoint);,
[this](const boost::system::error_code& ec, std::size_t)
{
if(ec)
EventLoop::call([this, ec](){ Log::log(*this, LogMessage::E2011_SOCKET_SEND_FAILED_X, ec); });
});*/
}

Datei anzeigen

@ -1,84 +0,0 @@
/**
* server/src/hardware/commandstation/rocoz21.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_ROCOZ21_HPP
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_ROCOZ21_HPP
#include "commandstation.hpp"
#include <boost/asio.hpp>
#include "../../core/objectproperty.hpp"
#include "../protocol/loconet/loconet.hpp"
struct z21_lan_header;
namespace Z21 {
class Message;
}
class RocoZ21 : public CommandStation
{
protected:
boost::asio::ip::udp::socket m_socket;
boost::asio::ip::udp::endpoint m_remoteEndpoint;
boost::asio::ip::udp::endpoint m_receiveEndpoint;
std::array<uint8_t,64> m_receiveBuffer;
bool setOnline(bool& value) final;
//bool isDecoderSupported(Decoder& decoder) const final;
void emergencyStopChanged(bool value) final;
void powerOnChanged(bool value) final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
void receive();
void send(const Z21::Message& message);
void send(const z21_lan_header* msg);
inline void send(const z21_lan_header& msg) { send(&msg); }
public:
CLASS_ID("command_station.z21")
CREATE(RocoZ21)
RocoZ21(const std::weak_ptr<World>& world, std::string_view _id);
Property<std::string> hostname;
Property<uint16_t> port;
ObjectProperty<LocoNet::LocoNet> loconet;
Property<std::string> serialNumber;
Property<std::string> hardwareType;
Property<std::string> firmwareVersion;
Property<float> mainCurrent;
Property<float> progCurrent;
Property<float> filteredMainCurrent;
Property<float> temperature;
Property<float> supplyVoltage;
Property<float> vccVoltage;
//Property<bool> emergencyStop;
//Property<bool> trackVoltageOff;
Property<bool> shortCircuit;
Property<bool> programmingModeActive;
Property<bool> highTemperature;
Property<bool> powerLost;
Property<bool> shortCircutInternal;
Property<bool> shortCircutExternal;
};
#endif

Datei anzeigen

@ -1,133 +0,0 @@
/**
* server/src/hardware/commandstation/serialcommandstation.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 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 "serialcommandstation.hpp"
#include "../../core/traintastic.hpp"
#include "../../core/eventloop.hpp"
#include "../../core/attributes.hpp"
#include "../../log/log.hpp"
#include "../../utils/displayname.hpp"
SerialCommandStation::SerialCommandStation(const std::weak_ptr<World>& world, std::string_view _id) :
CommandStation(world, _id),
m_serialPort{Traintastic::instance->ioContext()},
m_readBufferOffset{0},
port{this, "port", "/dev/ttyUSB0", PropertyFlags::ReadWrite | PropertyFlags::Store},
baudrate{this, "baudrate", 19200, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](uint32_t)
{
//interface = LocoNetSerialInterface::Custom;
}},
flowControl{this, "flow_control", SerialFlowControl::None, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](SerialFlowControl)
{
//interface = LocoNetSerialInterface::Custom;
}}
{
Attributes::addDisplayName(port, DisplayName::Serial::port);
Attributes::addEnabled(port, !online);
m_interfaceItems.insertBefore(port, notes);
Attributes::addDisplayName(baudrate, DisplayName::Serial::baudrate);
Attributes::addEnabled(baudrate, !online);
m_interfaceItems.insertBefore(baudrate, notes);
Attributes::addDisplayName(flowControl, DisplayName::Serial::flowControl);
Attributes::addEnabled(flowControl, !online);
Attributes::addValues(flowControl, SerialFlowControlValues);
m_interfaceItems.insertBefore(flowControl, notes);
}
void SerialCommandStation::loaded()
{
CommandStation::loaded();
updateEnabled();
}
void SerialCommandStation::worldEvent(WorldState state, WorldEvent event)
{
CommandStation::worldEvent(state, event);
if(event == WorldEvent::EditEnabled || event == WorldEvent::EditDisabled)
updateEnabled();
}
bool SerialCommandStation::setOnline(bool& value)
{
if(!m_serialPort.is_open() && value)
{
if(!start())
{
value = false;
return false;
}
m_readBufferOffset = 0;
read();
started();
}
else if(m_serialPort.is_open() && !value)
stop();
return true;
}
bool SerialCommandStation::start()
{
boost::system::error_code ec;
m_serialPort.open(port, ec);
if(ec)
{
Log::log(*this, LogMessage::E2010_SERIAL_PORT_OPEN_FAILED_X, ec);
return false;
}
m_serialPort.set_option(boost::asio::serial_port_base::baud_rate(baudrate));
m_serialPort.set_option(boost::asio::serial_port_base::character_size(8));
m_serialPort.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
m_serialPort.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
switch(flowControl)
{
case SerialFlowControl::None:
m_serialPort.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none));
break;
case SerialFlowControl::Hardware:
m_serialPort.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::hardware));
break;
}
return true;
}
void SerialCommandStation::stop()
{
// TODO: send power off cmd??
m_serialPort.close();
}
void SerialCommandStation::updateEnabled()
{
auto w = m_world.lock();
bool enabled = w && contains(w->state.value(), WorldState::Edit) && !online;
Attributes::setEnabled(port, enabled);
Attributes::setEnabled(baudrate, enabled);
Attributes::setEnabled(flowControl, enabled);
}

Datei anzeigen

@ -1,59 +0,0 @@
/**
* server/src/hardware/commandstation/serialcommandstation.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_SERIALCOMMANDSTATION_HPP
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_SERIALCOMMANDSTATION_HPP
#include "commandstation.hpp"
#include "../../enum/serialflowcontrol.hpp"
#include <boost/asio/serial_port.hpp>
class SerialCommandStation : public CommandStation
{
private:
void updateEnabled();
protected:
boost::asio::serial_port m_serialPort;
std::array<uint8_t, 1024> m_readBuffer;
uint16_t m_readBufferOffset;
void loaded() override;
void worldEvent(WorldState state, WorldEvent event) override;
bool setOnline(bool& value) final;
bool start();
virtual void stop();
virtual void started() {}
virtual void read() = 0;
public:
Property<std::string> port;
Property<uint32_t> baudrate;
Property<SerialFlowControl> flowControl;
SerialCommandStation(const std::weak_ptr<World>& world, std::string_view _id);
};
#endif

Datei anzeigen

@ -1,112 +0,0 @@
/**
* server/src/hardware/commandstation/usbxpressnetinterface.cpp
*
* Copyright (C) 2019-2021 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 "usbxpressnetinterface.hpp"
#include "../../core/traintastic.hpp"
#include "../../world/world.hpp"
USBXpressNetInterface::USBXpressNetInterface(const std::weak_ptr<World>& world, std::string_view _id) :
CommandStation(world, _id),
m_handle{nullptr},
serial{this, "serial", "", PropertyFlags::ReadWrite | PropertyFlags::Store},
address{this, "address", 31, PropertyFlags::ReadWrite | PropertyFlags::Store},
xpressnet{this, "xpressnet", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
{
name = "USB XpressNet interface";
xpressnet.setValueInternal(std::make_shared<XpressNet::XpressNet>(*this, xpressnet.name(), std::bind(&USBXpressNetInterface::send, this, std::placeholders::_1)));
m_interfaceItems.insertBefore(serial, notes);
m_interfaceItems.insertBefore(address, notes);
m_interfaceItems.insertBefore(xpressnet, notes);
usbxpressnet_init();
}
USBXpressNetInterface::~USBXpressNetInterface()
{
if(m_handle)
{
usbxpressnet_reset(m_handle);
usbxpressnet_close(m_handle);
}
usbxpressnet_fini();
}
bool USBXpressNetInterface::setOnline(bool& value)
{
if(!m_handle && value)
{
usbxpressnet_status status = usbxpressnet_open(!serial.value().empty() ? serial.value().c_str() : nullptr, &m_handle);
if(status != USBXPRESSNET_STATUS_SUCCESS)
{
// \todo Log::log(*this, LogMessage::E0001_X, std::string("usbxpressnet_open: ") + usbxpressnet_status_str(status));
return false;
}
status = usbxpressnet_reset(m_handle);
if(status != USBXPRESSNET_STATUS_SUCCESS)
{
// \todo Log::log(*this, LogMessage::E0001_X, std::string("usbxpressnet_reset: ") + usbxpressnet_status_str(status));
return false;
}
status = usbxpressnet_set_mode(m_handle, USBXPRESSNET_MODE_DEVICE, address);
if(status != USBXPRESSNET_STATUS_SUCCESS)
{
// \todo Log::log(*this, LogMessage::E0001_X, std::string("usbxpressnet_set_mode: ") + usbxpressnet_status_str(status));
return false;
}
}
else if(m_handle && !value)
{
usbxpressnet_close(m_handle);
m_handle = nullptr;
}
return true;
}
void USBXpressNetInterface::emergencyStopChanged(bool value)
{
}
void USBXpressNetInterface::powerOnChanged(bool value)
{
}
void USBXpressNetInterface::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
if(online)
xpressnet->decoderChanged(decoder, changes, functionNumber);
}
bool USBXpressNetInterface::send(const XpressNet::Message& msg)
{
assert(XpressNet::isChecksumValid(msg));
if(!m_handle)
return false;
usbxpressnet_status status;
if((status = usbxpressnet_send_message(m_handle, &msg)) != USBXPRESSNET_STATUS_SUCCESS)
{
Traintastic::instance->console->critical(id, std::string("usbxpressnet_send_message: ") + usbxpressnet_status_str(status));
return false;
}
return true;
}

Datei anzeigen

@ -1,57 +0,0 @@
/**
* server/src/hardware/commandstation/usbxpressnetinterface.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_USBXPRESSNETINTERFACE_HPP
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_USBXPRESSNETINTERFACE_HPP
#include "commandstation.hpp"
#include "../protocol/xpressnet/xpressnet.hpp"
#include <usbxpressnet.h>
class USBXpressNetInterface : public CommandStation
{
protected:
static const uint8_t addressMin = 1;
static const uint8_t addressMax = 31;
usbxpressnet_handle m_handle;
bool setOnline(bool& value) final;
void emergencyStopChanged(bool value) final;
void powerOnChanged(bool value) final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
bool send(const XpressNet::Message& msg);
public:
CLASS_ID("command_station.usb_xpressnet_interface")
CREATE(USBXpressNetInterface)
Property<std::string> serial;
Property<uint8_t> address;
ObjectProperty<XpressNet::XpressNet> xpressnet;
USBXpressNetInterface(const std::weak_ptr<World>& world, std::string_view _id);
~USBXpressNetInterface() final;
};
#endif

Datei anzeigen

@ -1,34 +0,0 @@
/**
* hardware/commandstation/virtualcommandstation.cpp
*
* This file is part of the traintastic source code
*
* Copyright (C) 2019-2020 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 "virtualcommandstation.hpp"
VirtualCommandStation::VirtualCommandStation(const std::weak_ptr<World>& world, std::string_view _id) :
CommandStation(world, _id)
{
name = "Virtual";
}
bool VirtualCommandStation::setOnline(bool& /*value*/)
{
return true;
}

Datei anzeigen

@ -1,40 +0,0 @@
/**
* server/src/hardware/commandstation/virtualcommandstation.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2020 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_VIRTUALCOMMANDSTATION_HPP
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_VIRTUALCOMMANDSTATION_HPP
#include "commandstation.hpp"
class VirtualCommandStation : public CommandStation
{
protected:
bool setOnline(bool& value) final;
public:
CLASS_ID("command_station.virtual")
CREATE(VirtualCommandStation)
VirtualCommandStation(const std::weak_ptr<World>& world, std::string_view _id);
};
#endif

Datei anzeigen

@ -1,273 +0,0 @@
/**
* hardware/commandstation/xpressnetserial.cpp
*
* This file is part of the traintastic source code
*
* Copyright (C) 2019-2021 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 "xpressnetserial.hpp"
#include "../../world/world.hpp"
#include "../../core/traintastic.hpp"
#include "../../core/eventloop.hpp"
#include "../../core/attributes.hpp"
#include "../../log/log.hpp"
#include "../../utils/displayname.hpp"
XpressNetSerial::XpressNetSerial(const std::weak_ptr<World>& world, std::string_view _id) :
SerialCommandStation(world, _id),
interface{this, "interface", XpressNetSerialInterface::LenzLI100, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](XpressNetSerialInterface value)
{
switch(value)
{
case XpressNetSerialInterface::Custom:
break;
case XpressNetSerialInterface::LenzLI100:
case XpressNetSerialInterface::RoSoftS88XPressNetLI:
baudrate.setValueInternal(9600);
flowControl.setValueInternal(SerialFlowControl::Hardware);
break;
case XpressNetSerialInterface::LenzLI100F:
baudrate.setValueInternal(19200);
flowControl.setValueInternal(SerialFlowControl::Hardware);
break;
case XpressNetSerialInterface::LenzLI101F:
baudrate.setValueInternal(19200);
flowControl.setValueInternal(SerialFlowControl::Hardware);
break;
}
updateEnabled();
updateVisible();
}},
xpressnet{this, "xpressnet", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject},
s88StartAddress{this, "s88_start_address", XpressNet::RoSoftS88XpressNetLI::S88StartAddress::startAddressDefault, PropertyFlags::ReadWrite | PropertyFlags::Store},
s88ModuleCount{this, "s88_module_count", XpressNet::RoSoftS88XpressNetLI::S88ModuleCount::moduleCountDefault, PropertyFlags::ReadWrite | PropertyFlags::Store}
{
name = "XpressNet (serial)";
xpressnet.setValueInternal(std::make_shared<XpressNet::XpressNet>(*this, xpressnet.name(), std::bind(&XpressNetSerial::send, this, std::placeholders::_1)));
Attributes::addValues(interface, XpressNetSerialInterfaceValues);
Attributes::addEnabled(interface, !online);
m_interfaceItems.insertBefore(interface, baudrate);
Attributes::addDisplayName(xpressnet, DisplayName::Hardware::xpressnet);
m_interfaceItems.insertBefore(xpressnet, notes);
Attributes::addMinMax(s88StartAddress, XpressNet::RoSoftS88XpressNetLI::S88StartAddress::startAddressMin, XpressNet::RoSoftS88XpressNetLI::S88StartAddress::startAddressMax);
Attributes::addEnabled(s88StartAddress, !online);
Attributes::addVisible(s88StartAddress, false);
m_interfaceItems.insertBefore(s88StartAddress, notes);
Attributes::addMinMax(s88ModuleCount, XpressNet::RoSoftS88XpressNetLI::S88ModuleCount::moduleCountMin, XpressNet::RoSoftS88XpressNetLI::S88ModuleCount::moduleCountMax);
Attributes::addEnabled(s88ModuleCount, !online);
Attributes::addVisible(s88ModuleCount, false);
m_interfaceItems.insertBefore(s88ModuleCount, notes);
updateEnabled();
updateVisible();
}
void XpressNetSerial::loaded()
{
SerialCommandStation::loaded();
updateEnabled();
updateVisible();
}
void XpressNetSerial::worldEvent(WorldState state, WorldEvent event)
{
SerialCommandStation::worldEvent(state, event);
if(event == WorldEvent::EditEnabled || event == WorldEvent::EditDisabled)
updateEnabled();
}
void XpressNetSerial::emergencyStopChanged(bool value)
{
CommandStation::emergencyStopChanged(value);
if(online)
xpressnet->emergencyStopChanged(value);
}
void XpressNetSerial::powerOnChanged(bool value)
{
CommandStation::powerOnChanged(value);
if(online)
xpressnet->powerOnChanged(value);
}
void XpressNetSerial::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
CommandStation::decoderChanged(decoder, changes, functionNumber);
if(online)
xpressnet->decoderChanged(decoder, changes, functionNumber);
}
bool XpressNetSerial::send(const XpressNet::Message& msg)
{
assert(XpressNet::isChecksumValid(msg));
if(!m_serialPort.is_open())
return false;
boost::system::error_code ec;
m_serialPort.write_some(boost::asio::buffer(static_cast<const void*>(&msg), msg.size()), ec); // TODO async
if(ec)
{
Log::log(*this, LogMessage::E2001_SERIAL_WRITE_FAILED_X, ec);
return false;
}
return true;
}
void XpressNetSerial::stop()
{
SerialCommandStation::stop();
updateEnabled();
}
void XpressNetSerial::started()
{
SerialCommandStation::started();
updateEnabled();
switch(interface.value())
{
case XpressNetSerialInterface::RoSoftS88XPressNetLI:
{
send(XpressNet::RoSoftS88XpressNetLI::S88StartAddress(s88StartAddress));
send(XpressNet::RoSoftS88XpressNetLI::S88ModuleCount(s88ModuleCount));
break;
}
default:
break;
}
}
void XpressNetSerial::read()
{
m_serialPort.async_read_some(boost::asio::buffer(m_readBuffer.data() + m_readBufferOffset, m_readBuffer.size() - m_readBufferOffset),
[this](const boost::system::error_code& ec, std::size_t bytesTransferred)
{
if(!ec)
{
const uint8_t* pos = m_readBuffer.data();
bytesTransferred += m_readBufferOffset;
while(bytesTransferred > 1)
{
const XpressNet::Message* message = reinterpret_cast<const XpressNet::Message*>(pos);
size_t drop = 0;
while(message->size() <= bytesTransferred && !XpressNet::isChecksumValid(*message) && drop < bytesTransferred)
{
drop++;
pos++;
bytesTransferred--;
message = reinterpret_cast<const XpressNet::Message*>(pos);
}
if(drop != 0)
{
EventLoop::call(
[this, drop]()
{
Log::log(*this, LogMessage::W2001_RECEIVED_MALFORMED_DATA_DROPPED_X_BYTES, drop);
});
}
if(message->size() <= bytesTransferred)
{
bool handled = false;
if(message->header == 0xF2)
{
switch(*(pos + 1))
{
case 0xF1:
{
using XpressNet::RoSoftS88XpressNetLI::S88StartAddress;
EventLoop::call(
[this, msg = *static_cast<const S88StartAddress*>(message)]()
{
assert(msg.startAddress >= S88StartAddress::startAddressMin && msg.startAddress <= S88StartAddress::startAddressMax);
s88StartAddress.setValueInternal(msg.startAddress);
});
handled = true;
break;
}
case 0xF2:
{
using XpressNet::RoSoftS88XpressNetLI::S88ModuleCount;
EventLoop::call(
[this, msg = *static_cast<const S88ModuleCount*>(message)]()
{
assert(msg.moduleCount >= S88ModuleCount::moduleCountMin && msg.moduleCount <= S88ModuleCount::moduleCountMax);
s88ModuleCount.setValueInternal(msg.moduleCount);
});
handled = true;
break;
}
}
}
if(!handled)
xpressnet->receive(*message);
pos += message->size();
bytesTransferred -= message->size();
}
else
break;
}
if(bytesTransferred != 0)
memmove(m_readBuffer.data(), pos, bytesTransferred);
m_readBufferOffset = bytesTransferred;
read();
}
else
EventLoop::call(
[this, ec]()
{
Log::log(*this, LogMessage::E2002_SERIAL_READ_FAILED_X, ec);
online = false;
});
});
}
void XpressNetSerial::updateEnabled()
{
auto w = m_world.lock();
const bool enabled = w && contains(w->state.value(), WorldState::Edit) && !online;
Attributes::setEnabled(interface, enabled);
Attributes::setEnabled(s88StartAddress, enabled);
Attributes::setEnabled(s88ModuleCount, enabled);
}
void XpressNetSerial::updateVisible()
{
const bool visible = interface == XpressNetSerialInterface::RoSoftS88XPressNetLI;
Attributes::setVisible(s88StartAddress, visible);
Attributes::setVisible(s88ModuleCount, visible);
}

Datei anzeigen

@ -1,61 +0,0 @@
/**
* server/src/hardware/commandstation/xpressnetserial.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_XPRESSNETSERIAL_HPP
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_XPRESSNETSERIAL_HPP
#include "serialcommandstation.hpp"
#include "../../enum/xpressnetserialinterface.hpp"
#include "../protocol/xpressnet/xpressnet.hpp"
class XpressNetSerial : public SerialCommandStation
{
private:
void updateEnabled();
void updateVisible();
protected:
void loaded() final;
void worldEvent(WorldState state, WorldEvent event) final;
void emergencyStopChanged(bool value) final;
void powerOnChanged(bool value) final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
bool send(const XpressNet::Message& msg);
void stop() final;
void started() final;
void read() final;
public:
CLASS_ID("command_station.xpressnet_serial")
CREATE(XpressNetSerial)
Property<XpressNetSerialInterface> interface;
ObjectProperty<XpressNet::XpressNet> xpressnet;
Property<uint8_t> s88StartAddress;
Property<uint8_t> s88ModuleCount;
XpressNetSerial(const std::weak_ptr<World>& world, std::string_view _id);
};
#endif