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

traintastic / traintastic / 23667775307

27 Mar 2026 09:13PM UTC coverage: 28.009% (-0.002%) from 28.011%
23667775307

push

github

reinder
[boost] fix timer cancel() error_code overload is removed in 1.87, see #220

0 of 5 new or added lines in 1 file covered. (0.0%)

220 existing lines in 10 files now uncovered.

8182 of 29212 relevant lines covered (28.01%)

194.75 hits per line

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

45.58
/server/src/hardware/input/inputconsumer.cpp
1
/**
2
 * This file is part of Traintastic,
3
 * see <https://github.com/traintastic/traintastic>.
4
 *
5
 * Copyright (C) 2025-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 "inputconsumer.hpp"
23
#include "input.hpp"
24
#include "inputcontroller.hpp"
25
#include "../../core/attributes.hpp"
26
#include "../../core/eventloop.hpp"
27
#include "../../core/objectproperty.tpp"
28
#include "../../log/log.hpp"
29
#include "../../utils/category.hpp"
30
#include "../../utils/displayname.hpp"
31
#include "../../utils/inrange.hpp"
32
#include "../../utils/valuestep.hpp"
33
#include "../../world/world.hpp"
34

35
namespace {
36

37
bool roundToDelayStep(uint16_t& value)
×
38
{
39
  value = valueStepRound(value, InputConsumer::delayStep);
×
40
  return true;
×
41
}
42

43
}
44

45
InputConsumer::InputConsumer(Object& object, const World& world)
16✔
46
  : m_object{object}
16✔
47
  , m_inputFilterTimer{EventLoop::ioContext()}
16✔
48
  , interface{&object, "interface", nullptr, PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::NoScript,
32✔
49
      [this](const std::shared_ptr<InputController>& /*newValue*/)
16✔
50
      {
51
        interfaceChanged();
×
52
      },
×
53
      [this](const std::shared_ptr<InputController>& newValue)
32✔
54
      {
55
        if(interface.value())
×
56
        {
57
          releaseInput();
×
58
        }
59

60
        if(newValue)
×
61
        {
62
          if(!newValue->isInputChannel(channel))
×
63
          {
64
            channel.setValueInternal(newValue->inputChannels().front());
×
65
          }
66

67
          if(auto addressMinMax = newValue->inputAddressMinMax(channel); !inRange(address.value(), addressMinMax))
×
68
          {
69
            const auto addr = newValue->getUnusedInputAddress(channel);
×
70
            address.setValueInternal(addr ? *addr : addressMinMax.first);
×
71
          }
72

73
          setInput(newValue->getInput(channel, address, m_object));
×
74
        }
75

76
        return true;
×
77
      }}
78
  , channel{&object, "channel", InputChannel::Input, PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::NoScript,
32✔
79
      [this](const InputChannel& /*newValue*/)
16✔
80
      {
81
        channelChanged();
×
82
      },
×
83
      [this](const InputChannel& newValue)
32✔
84
      {
85
        if(auto obj = interface->getInput(newValue, address, m_object))
×
86
        {
87
          setInput(obj);
×
88
          return true;
×
89
        }
×
90
        return false;
×
91
      }}
92
  , address{&object, "address", 0, PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::NoScript, nullptr,
32✔
93
      [this](const uint32_t& newValue)
16✔
94
      {
95
        if(auto obj = interface->getInput(channel, newValue, m_object))
×
96
        {
97
          setInput(obj);
×
98
          return true;
×
99
        }
×
100
        return false;
×
101
      }}
102
  , onDelay{&object, "on_delay", delayMin, PropertyFlags::ReadWrite | PropertyFlags::Store, nullptr, roundToDelayStep}
16✔
103
  , offDelay{&object, "off_delay", delayMin, PropertyFlags::ReadWrite | PropertyFlags::Store, nullptr, roundToDelayStep}
48✔
104
{
105
  const auto worldState = world.state.value();
16✔
106
  const bool editable = contains(worldState, WorldState::Edit);
16✔
107
  const bool editableAndStopped = editable && !contains(worldState, WorldState::Run);
16✔
108

109
  Attributes::addCategory(interface, Category::input);
16✔
110
  Attributes::addDisplayName(interface, DisplayName::Hardware::interface);
16✔
111
  Attributes::addEnabled(interface, editableAndStopped);
16✔
112
  Attributes::addObjectList(interface, world.inputControllers);
16✔
113

114
  Attributes::addCategory(channel, Category::input);
16✔
115
  Attributes::addDisplayName(channel, DisplayName::Hardware::channel);
16✔
116
  Attributes::addEnabled(channel, editableAndStopped);
16✔
117
  Attributes::addValues(channel, std::span<const InputChannel>());
16✔
118
  Attributes::addVisible(channel, false);
16✔
119

120
  Attributes::addCategory(address, Category::input);
16✔
121
  Attributes::addDisplayName(address, DisplayName::Hardware::address);
16✔
122
  Attributes::addEnabled(address, editableAndStopped);
16✔
123
  Attributes::addVisible(address, false);
16✔
124
  Attributes::addMinMax<uint32_t>(address, Input::addressMinDefault, Input::addressMaxDefault);
16✔
125

126
  Attributes::addCategory(onDelay, Category::input);
16✔
127
  Attributes::addDisplayName(onDelay, "input:on_delay");
16✔
128
  Attributes::addEnabled(onDelay, editable);
16✔
129
  Attributes::addMinMax(onDelay, delayMin, delayMax);
16✔
130
  Attributes::addStep(onDelay, delayStep);
16✔
131
  Attributes::addUnit(onDelay, delayUnit);
16✔
132
  Attributes::addVisible(onDelay, false);
16✔
133

134
  Attributes::addCategory(offDelay, Category::input);
16✔
135
  Attributes::addDisplayName(offDelay, "input:off_delay");
16✔
136
  Attributes::addEnabled(offDelay, editable);
16✔
137
  Attributes::addMinMax(offDelay, delayMin, delayMax);
16✔
138
  Attributes::addStep(offDelay, delayStep);
16✔
139
  Attributes::addUnit(offDelay, delayUnit);
16✔
140
  Attributes::addVisible(offDelay, false);
16✔
141
}
16✔
142

143
InputConsumer::~InputConsumer()
16✔
144
{
145
  releaseInput();
16✔
146
}
16✔
147

148
void InputConsumer::loaded()
×
149
{
150
  if(interface)
×
151
  {
152
    if(auto object = interface->getInput(channel, address, m_object))
×
153
    {
154
      setInput(object);
×
155
      interfaceChanged();
×
156
    }
157
    else
158
    {
159
      interface.setValueInternal(nullptr);
×
160
      //! \todo log warning
161
    }
×
162
  }
163
}
×
164

165
void InputConsumer::worldEvent(WorldState worldState, WorldEvent /*worldEvent*/)
14✔
166
{
167
  const bool editable = contains(worldState, WorldState::Edit);
14✔
168
  const bool editableAndStopped = editable && !contains(worldState, WorldState::Run);
14✔
169

170
  Attributes::setEnabled(interface, editableAndStopped);
14✔
171
  Attributes::setEnabled(channel, editableAndStopped);
14✔
172
  Attributes::setEnabled(address, editableAndStopped);
14✔
173
  Attributes::setEnabled(onDelay, editable);
14✔
174
  Attributes::setEnabled(offDelay, editable);
14✔
175
}
14✔
176

177
void InputConsumer::addInterfaceItems(InterfaceItems& items)
16✔
178
{
179
  items.add(interface);
16✔
180
  items.add(channel);
16✔
181
  items.add(address);
16✔
182
  items.add(onDelay);
16✔
183
  items.add(offDelay);
16✔
184
}
16✔
185

186
void InputConsumer::setInput(std::shared_ptr<Input> value)
×
187
{
188
  releaseInput();
×
189
  assert(!m_input);
×
190
  m_input = value;
×
191
  if(m_input)
×
192
  {
193
    m_inputDestroying = m_input->onDestroying.connect(
×
194
      [this](Object& object)
×
195
      {
196
        (void)object; // silence unused warning
197
        assert(m_input.get() == &object);
×
198
        interface.setValue(nullptr);
×
199
      });
×
200
    m_inputValueChanged = m_input->onValueChanged.connect(
×
201
      [this](bool inputValue, const std::shared_ptr<Input>& /*input*/)
×
202
      {
203
        try
204
        {
NEW
205
          m_inputFilterTimer.cancel();
×
206
        }
NEW
207
        catch(...)
×
208
        {
209
        }
×
210
        m_inputFilterTimer.expires_after(std::chrono::milliseconds(inputValue ? onDelay.value() : offDelay.value()));
×
211
        m_inputFilterTimer.async_wait(
×
212
          [this, inputValue](const boost::system::error_code& ec)
×
213
          {
214
            if(ec == boost::asio::error::operation_aborted)
×
215
              return;
×
216

217
            inputValueChanged(inputValue, m_input);
×
218
          });
219
      });
×
220

221
    if(m_input->value != TriState::Undefined)
×
222
    {
223
      inputValueChanged(m_input->value == TriState::True, m_input);
×
224
    }
225
  }
226
}
×
227

228
void InputConsumer::releaseInput()
16✔
229
{
230
  if(m_input)
16✔
231
  {
232
    try
233
    {
NEW
234
      m_inputFilterTimer.cancel();
×
235
    }
NEW
236
    catch(...)
×
237
    {
NEW
238
    }
×
239
    m_inputDestroying.disconnect();
×
240
    m_inputValueChanged.disconnect();
×
241
    if(m_input->interface)
×
242
    {
243
      m_input->interface->releaseInput(*m_input, m_object);
×
244
    }
245
    m_input.reset();
×
246
  }
247
}
16✔
248

249
void InputConsumer::interfaceChanged()
×
250
{
251
  Attributes::setValues(channel, interface ? interface->inputChannels() : std::span<const InputChannel>());
×
252
  Attributes::setVisible(channel, interface && interface->inputChannels().size() > 1);
×
253
  Attributes::setVisible({address, offDelay, onDelay}, interface);
×
254

255
  channelChanged();
×
256
}
×
257

258
void InputConsumer::channelChanged()
×
259
{
260
  if(interface)
×
261
  {
262
    const auto limits = interface->inputAddressMinMax(channel);
×
263
    Attributes::setMinMax(address, limits.first, limits.second);
×
264
  }
265
  else
266
  {
267
    Attributes::setMinMax(address, Input::addressMinDefault, Input::addressMaxDefault);
×
268
  }
269
}
×
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