204 Zeilen
7.4 KiB
C++

/**
* server/src/hardware/identification/identification.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2022 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 "identification.hpp"
#include "list/identificationlist.hpp"
#include "../../world/world.hpp"
#include "list/identificationlisttablemodel.hpp"
#include "../interface/loconetinterface.hpp"
#include "../../core/attributes.hpp"
#include "../../core/objectproperty.tpp"
#include "../../core/objectvectorproperty.tpp"
#include "../../log/log.hpp"
#include "../../utils/displayname.hpp"
Identification::Identification(World& world, std::string_view _id)
: IdObject(world, _id)
, name{this, "name", id, PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::ScriptReadOnly}
, interface{this, "interface", nullptr, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](const std::shared_ptr<IdentificationController>& /*newValue*/)
{
interfaceChanged();
},
[this](const std::shared_ptr<IdentificationController>& newValue)
{
if(interface.value() && !interface->removeIdentification(*this))
return false;
if(newValue)
{
if(!newValue->isIdentificationChannel(channel))
{
const auto* const channels = newValue->identificationChannels();
if(channels && !channels->empty())
channel.setValueInternal(channels->front());
else
channel.setValueInternal(IdentificationController::defaultIdentificationChannel);
}
if(!newValue->isIdentificationAddressAvailable(channel, address))
{
const uint32_t newAddress = newValue->getUnusedIdentificationAddress(channel);
if(newAddress == Identification::invalidAddress)
return false; // no free address available
address.setValueInternal(newAddress);
}
return newValue->addIdentification(*this);
}
return true;
}}
, channel{this, "channel", IdentificationController::defaultIdentificationChannel, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](const uint32_t& /*newValue*/)
{
channelChanged();
},
[this](const uint32_t& newValue)
{
if(interface)
return interface->changeIdentificationChannelAddress(*this, newValue, address);
return true;
}}
, address{this, "address", 1, PropertyFlags::ReadWrite | PropertyFlags::Store, nullptr,
[this](const uint32_t& newValue)
{
if(interface)
return interface->changeIdentificationChannelAddress(*this, channel, newValue);
return true;
}}
, opcMultiSenseDirection{this, "opc_multi_sense_direction", OPCMultiSenseDirection::None, PropertyFlags::ReadWrite | PropertyFlags::Store}
, consumers{*this, "consumers", {}, PropertyFlags::ReadOnly | PropertyFlags::NoStore}
, onEvent{*this, "on_event", EventFlags::Scriptable}
{
const bool editable = contains(m_world.state.value(), WorldState::Edit);
Attributes::addDisplayName(name, DisplayName::Object::name);
Attributes::addEnabled(name, editable);
m_interfaceItems.add(name);
Attributes::addDisplayName(interface, DisplayName::Hardware::interface);
Attributes::addEnabled(interface, editable);
Attributes::addObjectList(interface, m_world.identificationControllers);
m_interfaceItems.add(interface);
Attributes::addDisplayName(channel, DisplayName::Hardware::channel);
Attributes::addEnabled(channel, editable);
Attributes::addVisible(channel, false);
Attributes::addValues(channel, IdentificationController::noIdentificationChannels);
Attributes::addAliases(channel, IdentificationController::noIdentificationChannels, nullptr);
m_interfaceItems.add(channel);
Attributes::addDisplayName(address, DisplayName::Hardware::address);
Attributes::addEnabled(address, editable);
Attributes::addVisible(address, false);
Attributes::addMinMax(address, addressMinDefault, addressMaxDefault);
m_interfaceItems.add(address);
Attributes::addEnabled(opcMultiSenseDirection, editable);
Attributes::addVisible(opcMultiSenseDirection, false);
Attributes::addValues(opcMultiSenseDirection, opcMultiSenseDirectionValues);
m_interfaceItems.add(opcMultiSenseDirection);
Attributes::addObjectEditor(consumers, false); //! \todo add client support first
m_interfaceItems.add(consumers);
m_interfaceItems.add(onEvent);
}
void Identification::addToWorld()
{
IdObject::addToWorld();
m_world.identifications->addObject(shared_ptr<Identification>());
}
void Identification::loaded()
{
IdObject::loaded();
if(interface)
{
if(!interface->addIdentification(*this))
{
if(auto object = std::dynamic_pointer_cast<Object>(interface.value()))
Log::log(*this, LogMessage::C2001_ADDRESS_ALREADY_USED_AT_X, *object);
interface.setValueInternal(nullptr);
}
interfaceChanged();
}
}
void Identification::destroying()
{
if(interface.value())
interface = nullptr;
m_world.identifications->removeObject(shared_ptr<Identification>());
IdObject::destroying();
}
void Identification::worldEvent(WorldState state, WorldEvent event)
{
IdObject::worldEvent(state, event);
const bool editable = contains(state, WorldState::Edit);
Attributes::setEnabled(name, editable);
Attributes::setEnabled(interface, editable);
Attributes::setEnabled(channel, editable);
Attributes::setEnabled(address, editable);
Attributes::setEnabled(opcMultiSenseDirection, editable);
}
void Identification::fireEvent(IdentificationEventType type, uint16_t identifier, Direction direction, uint8_t category)
{
Object::fireEvent(onEvent, type, identifier, direction, category);
}
void Identification::interfaceChanged()
{
const bool opcMultiSenseVisible = hasLocoNetInterface();
Attributes::setValues(channel, interface ? interface->identificationChannels() : IdentificationController::noIdentificationChannels);
Attributes::setAliases(channel, interface ? interface->identificationChannels() : IdentificationController::noIdentificationChannels, interface ? interface->identificationChannelNames() : nullptr);
Attributes::setVisible(channel, interface && interface->identificationChannels() && !interface->identificationChannels()->empty());
Attributes::setVisible(address, interface);
Attributes::setVisible(opcMultiSenseDirection, opcMultiSenseVisible);
channelChanged();
}
void Identification::channelChanged()
{
if(interface)
{
const auto limits = interface->identificationAddressMinMax(channel);
Attributes::setMinMax(address, limits.first, limits.second);
}
else
Attributes::setMinMax(address, addressMinDefault, addressMaxDefault);
}
bool Identification::hasLocoNetInterface() const
{
return dynamic_cast<const LocoNetInterface*>(&*interface);
}