removed old command station objects
Dieser Commit ist enthalten in:
Ursprung
14eb29e95b
Commit
f84be624b6
@ -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})
|
||||||
|
|||||||
@ -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);
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
@ -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>();
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
@ -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;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
@ -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;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
@ -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); });
|
|
||||||
});*/
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
@ -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);
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
Laden…
x
In neuem Issue referenzieren
Einen Benutzer sperren