/** * server/src/hardware/input/inputcontroller.cpp * * This file is part of the traintastic source code. * * Copyright (C) 2021-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 "inputcontroller.hpp" #include "input.hpp" #include "list/inputlist.hpp" #include "list/inputlisttablemodel.hpp" #include "monitor/inputmonitor.hpp" #include "../../core/attributes.hpp" #include "../../core/objectproperty.tpp" #include "../../utils/displayname.hpp" #include "../../utils/inrange.hpp" #include "../../world/world.hpp" InputController::InputController(IdObject& interface) : inputs{&interface, "inputs", nullptr, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::SubObject} { Attributes::addDisplayName(inputs, DisplayName::Hardware::inputs); } bool InputController::isInputChannel(uint32_t channel) const { const auto* channels = inputChannels(); if(!channels || channels->empty()) return channel == defaultInputChannel; auto it = std::find(channels->begin(), channels->end(), channel); assert(it == channels->end() || *it != defaultInputChannel); return it != channels->end(); } bool InputController::isInputAddressAvailable(uint32_t channel, uint32_t address) const { assert(isInputChannel(channel)); return inRange(address, inputAddressMinMax(channel)) && m_inputs.find({channel, address}) == m_inputs.end(); } uint32_t InputController::getUnusedInputAddress(uint32_t channel) const { assert(isInputChannel(channel)); const auto end = m_inputs.cend(); const auto range = inputAddressMinMax(channel); for(uint32_t address = range.first; address < range.second; address++) if(m_inputs.find({channel, address}) == end) return address; return Input::invalidAddress; } bool InputController::changeInputChannelAddress(Input& input, uint32_t newChannel, uint32_t newAddress) { assert(input.interface.value().get() == this); assert(isInputChannel(newChannel)); if(!isInputAddressAvailable(newChannel, newAddress)) return false; auto node = m_inputs.extract({input.channel, input.address}); node.key() = {newChannel, newAddress}; m_inputs.insert(std::move(node)); input.value.setValueInternal(TriState::Undefined); return true; } bool InputController::addInput(Input& input) { if(isInputChannel(input.channel) && isInputAddressAvailable(input.channel, input.address)) { m_inputs.insert({{input.channel, input.address}, input.shared_ptr()}); input.value.setValueInternal(TriState::Undefined); inputs->addObject(input.shared_ptr()); return true; } return false; } bool InputController::removeInput(Input& input) { assert(input.interface.value().get() == this); auto it = m_inputs.find({input.channel, input.address}); if(it != m_inputs.end() && it->second.get() == &input) { m_inputs.erase(it); input.value.setValueInternal(TriState::Undefined); inputs->removeObject(input.shared_ptr()); return true; } return false; } void InputController::updateInputValue(uint32_t channel, uint32_t address, TriState value) { if(auto it = m_inputs.find({channel, address}); it != m_inputs.end()) it->second->updateValue(value); if(auto monitor = m_inputMonitors[channel].lock()) monitor->inputValueChanged(*monitor, address, value); } std::shared_ptr InputController::inputMonitor(uint32_t channel) { assert(isInputChannel(channel)); auto monitor = m_inputMonitors[channel].lock(); if(!monitor) { monitor = std::make_shared(*this, channel); m_inputMonitors[channel] = monitor; } return monitor; } void InputController::addToWorld(InputListColumn columns) { auto& object = interface(); inputs.setValueInternal(std::make_shared(object, inputs.name(), columns)); object.world().inputControllers->add(std::dynamic_pointer_cast(object.shared_from_this())); } void InputController::destroying() { auto& object = interface(); while(!inputs->empty()) { const auto& input = inputs->front(); assert(input->interface.value() == std::dynamic_pointer_cast(object.shared_from_this())); input->interface = nullptr; // removes object form the list } object.world().inputControllers->remove(std::dynamic_pointer_cast(object.shared_from_this())); } IdObject& InputController::interface() { auto* object = dynamic_cast(this); assert(object); return *object; }