• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

traintastic / traintastic / 23088148108

14 Mar 2026 12:40PM UTC coverage: 26.661% (-0.2%) from 26.84%
23088148108

push

github

reinder
[cbus] added input support for short/long events

0 of 133 new or added lines in 4 files covered. (0.0%)

587 existing lines in 20 files now uncovered.

8246 of 30929 relevant lines covered (26.66%)

185.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

17.82
/server/src/hardware/input/inputcontroller.cpp
1
/**
2
 * This file is part of Traintastic,
3
 * see <https://github.com/traintastic/traintastic>.
4
 *
5
 * Copyright (C) 2021-2026 Reinder Feenstra
6
 *
7
 * This program is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU General Public License
9
 * as published by the Free Software Foundation; either version 2
10
 * of the License, or (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
 */
21

22
#include "inputcontroller.hpp"
23
#include "input.hpp"
24
#include "list/inputlist.hpp"
25
#include "list/inputlisttablemodel.hpp"
26
#include "monitor/inputmonitor.hpp"
27
#include "../../core/attributes.hpp"
28
#include "../../core/controllerlist.hpp"
29
#include "../../core/objectproperty.tpp"
30
#include "../../utils/contains.hpp"
31
#include "../../utils/displayname.hpp"
32
#include "../../utils/inrange.hpp"
33
#include "../../world/world.hpp"
34
#include "../../world/getworld.hpp"
35

36
InputController::InputController(IdObject& interface)
61✔
37
  : inputs{&interface, "inputs", nullptr, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::SubObject}
61✔
38
{
39
  Attributes::addDisplayName(inputs, DisplayName::Hardware::inputs);
61✔
40
}
61✔
41

UNCOV
42
bool InputController::isInputChannel(InputChannel channel) const
×
43
{
UNCOV
44
  assert(!inputChannels().empty());
×
45
  return contains(inputChannels(), channel);
×
46
}
47

UNCOV
48
bool InputController::isInputLocation(InputChannel channel, const InputLocation& location) const
×
49
{
UNCOV
50
  if(hasAddressLocation(channel))
×
51
  {
UNCOV
52
    return inRange(std::get<InputAddress>(location).address, inputAddressMinMax(channel));
×
53
  }
54
  return false;
×
55
}
56

57
bool InputController::isInputAvailable(InputChannel channel, const InputLocation& location) const
×
58
{
59
  assert(isInputChannel(channel));
×
60
  return
61
    isInputLocation(channel, location) &&
×
62
    m_inputs.find({channel, location}) == m_inputs.end();
×
63
}
64

UNCOV
65
std::optional<uint32_t> InputController::getUnusedInputAddress(InputChannel channel) const
×
66
{
UNCOV
67
  assert(isInputChannel(channel));
×
UNCOV
68
  if(hasAddressLocation(channel))
×
69
  {
UNCOV
70
    const auto end = m_inputs.cend();
×
UNCOV
71
    const auto range = inputAddressMinMax(channel);
×
72
    for(uint32_t address = range.first; address < range.second; address++)
×
73
    {
74
      if(m_inputs.find({channel, InputAddress(address)}) == end)
×
75
      {
76
        return address;
×
77
      }
78
    }
79
  }
80
  return std::nullopt;
×
81
}
82

83
std::shared_ptr<Input> InputController::getInput(InputChannel channel, const InputLocation& location, Object& usedBy)
×
84
{
UNCOV
85
  if(!isInputChannel(channel) || !isInputLocation(channel, location))
×
86
  {
87
    return {};
×
88
  }
89

90
  // Check if already exists:
91
  if(auto it = m_inputs.find({channel, location}); it != m_inputs.end())
×
92
  {
93
    it->second->m_usedBy.emplace(usedBy.shared_from_this());
×
UNCOV
94
    return it->second;
×
95
  }
96

97
  // Create new input:
98
  std::shared_ptr<Input> input;
×
99
  if(hasAddressLocation(channel))
×
100
  {
101
    assert(std::holds_alternative<InputAddress>(location));
×
UNCOV
102
    input = std::make_shared<Input>(shared_ptr(), channel, std::nullopt, std::get<InputAddress>(location).address);
×
103
  }
104
  else if(hasNodeAddressLocation(channel))
×
105
  {
UNCOV
106
    assert(std::holds_alternative<InputNodeAddress>(location));
×
107
    input = std::make_shared<Input>(shared_ptr(), channel, std::get<InputNodeAddress>(location).node, std::get<InputNodeAddress>(location).address);
×
108
  }
UNCOV
109
  else [[unlikely]]
×
110
  {
111
    assert(false);
×
112
    return {};
113
  }
114
  input->m_usedBy.emplace(usedBy.shared_from_this());
×
UNCOV
115
  m_inputs.emplace(InputMapKey{channel, location}, input);
×
116
  inputs->addObject(input);
×
UNCOV
117
  getWorld(inputs.object()).inputs->addObject(input);
×
118

119
  if(auto monitor = m_inputMonitors[channel].lock(); monitor && hasAddressLocation(channel))
×
120
  {
121
    monitor->fireInputUsedChanged(input->address, true);
×
UNCOV
122
  }
×
123

UNCOV
124
  return input;
×
125
}
×
126

127
void InputController::releaseInput(Input& input, Object& usedBy)
×
128
{
129
  auto inputShared = input.shared_ptr<Input>();
×
UNCOV
130
  input.m_usedBy.erase(usedBy.shared_from_this());
×
131
  if(input.m_usedBy.empty())
×
132
  {
133
    const auto channel = input.channel.value();
×
UNCOV
134
    const auto location = input.location();
×
135

UNCOV
136
    m_inputs.erase({channel, location});
×
137
    inputs->removeObject(inputShared);
×
138
    getWorld(inputs.object()).inputs->removeObject(inputShared);
×
139
    inputShared->destroy();
×
UNCOV
140
    inputShared.reset();
×
141

142
    if(auto monitor = m_inputMonitors[channel].lock(); monitor && hasAddressLocation(channel))
×
143
    {
144
      monitor->fireInputUsedChanged(std::get<InputAddress>(location).address, false);
×
145
    }
×
146
  }
UNCOV
147
}
×
148

UNCOV
149
void InputController::updateInputValue(InputChannel channel, const InputLocation& location, TriState value)
×
150
{
UNCOV
151
  if(auto it = m_inputs.find({channel, location}); it != m_inputs.end())
×
152
  {
UNCOV
153
    it->second->updateValue(value);
×
154
  }
UNCOV
155
  if(auto monitor = m_inputMonitors[channel].lock(); monitor && std::holds_alternative<InputAddress>(location))
×
156
  {
UNCOV
157
    monitor->fireInputValueChanged(std::get<InputAddress>(location).address, value);
×
UNCOV
158
  }
×
159
}
×
160

161
std::shared_ptr<InputMonitor> InputController::inputMonitor(InputChannel channel)
×
162
{
163
  assert(isInputChannel(channel));
×
UNCOV
164
  auto monitor = m_inputMonitors[channel].lock();
×
UNCOV
165
  if(!monitor)
×
166
  {
UNCOV
167
    monitor = std::make_shared<InputMonitor>(*this, channel);
×
UNCOV
168
    m_inputMonitors[channel] = monitor;
×
169
  }
UNCOV
170
  return monitor;
×
UNCOV
171
}
×
172

173
void InputController::addToWorld(InputListColumn columns)
61✔
174
{
175
  auto& object = interface();
61✔
176
  inputs.setValueInternal(std::make_shared<InputList>(object, inputs.name(), columns));
61✔
177
  object.world().inputControllers->add(std::dynamic_pointer_cast<InputController>(object.shared_from_this()));
61✔
178
}
61✔
179

180
void InputController::destroying()
61✔
181
{
182
  auto& object = interface();
61✔
183
  while(!inputs->empty())
61✔
184
  {
UNCOV
185
    const auto& input = inputs->front();
×
UNCOV
186
    assert(input->interface.value() == std::dynamic_pointer_cast<InputController>(object.shared_from_this()));
×
UNCOV
187
    input->interface.setValueInternal(nullptr);
×
UNCOV
188
    input->destroy(); // notify consumers we're dying
×
UNCOV
189
    inputs->removeObject(input);
×
190
  }
191
  object.world().inputControllers->remove(std::dynamic_pointer_cast<InputController>(object.shared_from_this()));
61✔
192
}
61✔
193

194
IdObject& InputController::interface()
122✔
195
{
196
  auto* object = dynamic_cast<IdObject*>(this);
122✔
197
  assert(object);
122✔
198
  return *object;
122✔
199
}
200

UNCOV
201
std::shared_ptr<InputController> InputController::shared_ptr()
×
202
{
UNCOV
203
  auto self = std::dynamic_pointer_cast<InputController>(inputs.object().shared_from_this());
×
UNCOV
204
  assert(self);
×
UNCOV
205
  return self;
×
206
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc