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

traintastic / traintastic / 22823086659

08 Mar 2026 02:30PM UTC coverage: 26.831% (+0.06%) from 26.774%
22823086659

push

github

reinder
[cbus] renamed CBUSAccessory(Short) to Long/Short event and added node setting for Long events.

0 of 15 new or added lines in 2 files covered. (0.0%)

1909 existing lines in 38 files now uncovered.

8230 of 30674 relevant lines covered (26.83%)

186.8 hits per line

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

0.0
/server/src/hardware/protocol/ecos/kernel.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 "kernel.hpp"
23
#include <algorithm>
24
#include <typeinfo>
25
#include "messages.hpp"
26
#include "simulation.hpp"
27
#include "object/ecos.hpp"
28
#include "object/locomotivemanager.hpp"
29
#include "object/locomotive.hpp"
30
#include "object/switchmanager.hpp"
31
#include "object/switch.hpp"
32
#include "object/feedbackmanager.hpp"
33
#include "object/feedback.hpp"
34
#include "../../protocol/dcc/dcc.hpp"
35
#include "../../decoder/decoder.hpp"
36
#include "../../decoder/decoderchangeflags.hpp"
37
#include "../../input/inputcontroller.hpp"
38
#include "../../output/outputcontroller.hpp"
39
#include "../../../utils/setthreadname.hpp"
40
#include "../../../utils/startswith.hpp"
41
#include "../../../utils/ltrim.hpp"
42
#include "../../../utils/rtrim.hpp"
43
#include "../../../utils/tohex.hpp"
44
#include "../../../core/eventloop.hpp"
45
#include "../../../log/log.hpp"
46
#include "../../../log/logmessageexception.hpp"
47

48
#define ASSERT_IS_KERNEL_THREAD assert(isKernelThread())
49

50
namespace ECoS {
51

UNCOV
52
static constexpr DecoderProtocol toDecoderProtocol(LocomotiveProtocol locomotiveProtocol, uint16_t address)
×
53
{
UNCOV
54
  switch(locomotiveProtocol)
×
55
  {
UNCOV
56
    case LocomotiveProtocol::DCC14:
×
57
    case LocomotiveProtocol::DCC28:
58
    case LocomotiveProtocol::DCC128:
UNCOV
59
      return DCC::getProtocol(address);
×
60

UNCOV
61
    case LocomotiveProtocol::MM14:
×
62
    case LocomotiveProtocol::MM27:
63
    case LocomotiveProtocol::MM28:
UNCOV
64
      return DecoderProtocol::Motorola;
×
65

UNCOV
66
    case LocomotiveProtocol::SX32:
×
67
      return DecoderProtocol::Selectrix;
×
68

UNCOV
69
    case LocomotiveProtocol::Unknown:
×
70
    case LocomotiveProtocol::MMFKT:
UNCOV
71
      break;
×
72
  }
UNCOV
73
  return DecoderProtocol::None;
×
74
}
75

UNCOV
76
Kernel::Kernel(std::string logId_, const Config& config, bool simulation)
×
77
  : KernelBase(std::move(logId_))
×
78
  , m_simulation{simulation}
×
79
  , m_decoderController{nullptr}
×
80
  , m_inputController{nullptr}
×
81
  , m_outputController{nullptr}
×
82
  , m_config{config}
×
83
{
UNCOV
84
}
×
85

UNCOV
86
void Kernel::setConfig(const Config& config)
×
87
{
UNCOV
88
  m_ioContext.post(
×
89
    [this, newConfig=config]()
×
90
    {
UNCOV
91
      m_config = newConfig;
×
92
    });
×
93
}
×
94

UNCOV
95
void Kernel::setOnEmergencyStop(std::function<void()> callback)
×
96
{
UNCOV
97
  assert(!m_started);
×
98
  m_onEmergencyStop = std::move(callback);
×
99
}
×
100

UNCOV
101
void Kernel::setOnGo(std::function<void()> callback)
×
102
{
UNCOV
103
  assert(!m_started);
×
104
  m_onGo = std::move(callback);
×
105
}
×
106

UNCOV
107
void Kernel::setOnObjectChanged(OnObjectChanged callback)
×
108
{
UNCOV
109
  assert(!m_started);
×
110
  m_onObjectChanged = std::move(callback);
×
111
}
×
112

UNCOV
113
void Kernel::setOnObjectRemoved(OnObjectRemoved callback)
×
114
{
UNCOV
115
  assert(!m_started);
×
116
  m_onObjectRemoved = std::move(callback);
×
117
}
×
118
void Kernel::setDecoderController(DecoderController* decoderController)
×
119
{
UNCOV
120
  assert(!m_started);
×
121
  m_decoderController = decoderController;
×
122
}
×
123

UNCOV
124
void Kernel::setInputController(InputController* inputController)
×
125
{
UNCOV
126
  assert(!m_started);
×
127
  m_inputController = inputController;
×
128
}
×
129

UNCOV
130
void Kernel::setOutputController(OutputController* outputController)
×
131
{
UNCOV
132
  assert(!m_started);
×
133
  m_outputController = outputController;
×
134
}
×
135

UNCOV
136
void Kernel::start()
×
137
{
UNCOV
138
  assert(m_ioHandler);
×
139
  assert(!m_started);
×
140
  assert(m_objects.empty());
×
141

UNCOV
142
  m_thread = std::thread(
×
143
    [this]()
×
144
    {
UNCOV
145
      setThreadName("ecos");
×
146
      auto work = std::make_shared<boost::asio::io_context::work>(m_ioContext);
×
147
      m_ioContext.run();
×
148
    });
×
149

UNCOV
150
  m_ioContext.post(
×
151
    [this]()
×
152
    {
153
      try
154
      {
UNCOV
155
        m_ioHandler->start();
×
156
      }
UNCOV
157
      catch(const LogMessageException& e)
×
158
      {
UNCOV
159
        EventLoop::call(
×
160
          [this, e]()
×
161
          {
UNCOV
162
            Log::log(logId, e.message(), e.args());
×
163
            error();
×
164
          });
×
165
        return;
×
166
      }
×
167
    });
168

169
#ifndef NDEBUG
UNCOV
170
  m_started = true;
×
171
#endif
UNCOV
172
}
×
173

UNCOV
174
void Kernel::stop(Simulation* simulation)
×
175
{
UNCOV
176
  m_ioContext.post(
×
177
    [this]()
×
178
    {
UNCOV
179
      m_ioHandler->stop();
×
180
    });
×
181

UNCOV
182
  m_ioContext.stop();
×
183

UNCOV
184
  m_thread.join();
×
185

UNCOV
186
  if(simulation && !m_objects.empty()) // get simulation data
×
187
  {
UNCOV
188
    simulation->clear();
×
189

190
    // ECoS:
191
    {
UNCOV
192
      simulation->ecos.commandStationType = toString(ecos().model());
×
193
      simulation->ecos.protocolVersion = ::toString(ecos().protocolVersion());
×
194
      simulation->ecos.hardwareVersion = ::toString(ecos().hardwareVersion());
×
195
      simulation->ecos.applicationVersion = ::toString(ecos().applicationVersion());
×
196
      simulation->ecos.applicationVersionSuffix = ecos().applicationVersionSuffix();
×
197
      simulation->ecos.railcom = ecos().railcom();
×
198
      simulation->ecos.railcomPlus = ecos().railcomPlus();
×
199
    }
200

201
    // Locomotives / switches:
UNCOV
202
    for(const auto& it : m_objects)
×
203
    {
UNCOV
204
      if(const auto* locomotive = dynamic_cast<const Locomotive*>(it.second.get()))
×
205
      {
UNCOV
206
        simulation->locomotives.emplace_back(
×
207
          Simulation::Locomotive{
×
208
            {locomotive->id()},
×
209
            locomotive->protocol(),
×
210
            locomotive->address()});
×
211
      }
UNCOV
212
      else if(const auto* sw = dynamic_cast<const Switch*>(it.second.get()))
×
213
      {
UNCOV
214
        simulation->switches.emplace_back(
×
215
          Simulation::Switch{
×
216
            {sw->id()},
×
217
            sw->name1(),
×
218
            sw->name2(),
×
219
            sw->name3(),
×
220
            sw->address(),
×
221
            toString(sw->addrext()),
UNCOV
222
            std::string{toString(sw->type())},
×
223
            static_cast<int>(sw->symbol()),
×
224
            std::string{toString(sw->protocol())},
×
225
            sw->state(),
×
226
            std::string{toString(sw->mode())},
×
227
            sw->duration(),
×
228
            sw->variant()
×
229
            });
230
      }
231
    }
232

233
    // S88:
234
    {
UNCOV
235
      uint16_t id = ObjectId::s88;
×
236
      auto it = m_objects.find(id);
×
237
      while(it != m_objects.end())
×
238
      {
UNCOV
239
        if(const auto* feedback = dynamic_cast<const Feedback*>(it->second.get()))
×
240
          simulation->s88.emplace_back(Simulation::S88{{feedback->id()}, feedback->ports()});
×
241
        else
UNCOV
242
          break;
×
243
        it = m_objects.find(++id);
×
244
      }
245
    }
246
  }
247

UNCOV
248
  m_objects.clear();
×
249

250
#ifndef NDEBUG
UNCOV
251
  m_started = false;
×
252
#endif
UNCOV
253
}
×
254

UNCOV
255
void Kernel::started()
×
256
{
UNCOV
257
  assert(isKernelThread());
×
258

UNCOV
259
  m_objects.add(std::make_unique<ECoS>(*this));
×
260
  m_objects.add(std::make_unique<LocomotiveManager>(*this));
×
261
  m_objects.add(std::make_unique<SwitchManager>(*this));
×
262
  m_objects.add(std::make_unique<FeedbackManager>(*this));
×
263

UNCOV
264
  KernelBase::started();
×
265
}
×
266

UNCOV
267
void Kernel::receive(std::string_view message)
×
268
{
UNCOV
269
  if(m_config.debugLogRXTX)
×
270
  {
UNCOV
271
    std::string msg{rtrim(message, {'\r', '\n'})};
×
272
    std::replace_if(msg.begin(), msg.end(), [](char c){ return c == '\r' || c == '\n'; }, ';');
×
273
    EventLoop::call([this, msg](){ Log::log(logId, LogMessage::D2002_RX_X, msg); });
×
274
  }
×
275

UNCOV
276
  if(Reply reply; parseReply(message, reply))
×
277
  {
UNCOV
278
    auto it = m_objects.find(reply.objectId);
×
279
    if(it != m_objects.end())
×
280
      it->second->receiveReply(reply);
×
281
  }
UNCOV
282
  else if(Event event; parseEvent(message, event))
×
283
  {
UNCOV
284
    auto it = m_objects.find(event.objectId);
×
285
    if(it != m_objects.end())
×
286
      it->second->receiveEvent(event);
×
287
  }
288
  else
UNCOV
289
  {}//  EventLoop::call([this]() { Log::log(logId, LogMessage::E2018_ParseError); });
×
290
}
×
291

UNCOV
292
ECoS& Kernel::ecos()
×
293
{
UNCOV
294
  assert(isKernelThread() || m_ioContext.stopped());
×
295

UNCOV
296
  return static_cast<ECoS&>(*m_objects[ObjectId::ecos]);
×
297
}
298

UNCOV
299
void Kernel::ecosGoChanged(TriState value)
×
300
{
UNCOV
301
  ASSERT_IS_KERNEL_THREAD;
×
302

UNCOV
303
  if(value == TriState::False && m_onEmergencyStop)
×
304
    EventLoop::call([this]() { m_onEmergencyStop(); });
×
305
  else if(value == TriState::True && m_onGo)
×
306
    EventLoop::call([this]() { m_onGo(); });
×
307
}
×
308

UNCOV
309
Locomotive* Kernel::getLocomotive(DecoderProtocol protocol, uint16_t address, uint8_t speedSteps)
×
310
{
UNCOV
311
  ASSERT_IS_KERNEL_THREAD;
×
312

313
  //! \todo optimize this
314

UNCOV
315
  auto it = std::find_if(m_objects.begin(), m_objects.end(),
×
316
    [protocol, address, speedSteps](const auto& item)
×
317
    {
UNCOV
318
      auto* l = dynamic_cast<Locomotive*>(item.second.get());
×
319
      return
UNCOV
320
        l &&
×
321
        protocol == toDecoderProtocol(l->protocol(), l->address()) &&
×
322
        address == l->address()  &&
×
323
        speedSteps == l->speedSteps();
×
324
    });
325

UNCOV
326
  if(it != m_objects.end())
×
327
    return static_cast<Locomotive*>(it->second.get());
×
328

UNCOV
329
  return nullptr;
×
330
}
331

UNCOV
332
SwitchManager& Kernel::switchManager()
×
333
{
UNCOV
334
  ASSERT_IS_KERNEL_THREAD;
×
335

UNCOV
336
  return static_cast<SwitchManager&>(*m_objects[ObjectId::switchManager]);
×
337
}
338

UNCOV
339
void Kernel::emergencyStop()
×
340
{
UNCOV
341
  m_ioContext.post([this]() { ecos().stop(); });
×
342
}
×
343

UNCOV
344
void Kernel::go()
×
345
{
UNCOV
346
  m_ioContext.post([this]() { ecos().go(); });
×
347
}
×
348

UNCOV
349
void Kernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
×
350
{
UNCOV
351
  if(has(changes, DecoderChangeFlags::Direction))
×
352
  {
UNCOV
353
    m_ioContext.post(
×
354
      [this,
×
355
        protocol=decoder.protocol.value(),
×
356
        address=decoder.address.value(),
×
357
        speedSteps=decoder.speedSteps.value(),
×
358
        direction=decoder.direction.value()]()
×
359
      {
UNCOV
360
        if(auto* locomotive = getLocomotive(protocol, address, speedSteps))
×
361
          locomotive->setDirection(direction);
×
362
      });
×
363
  }
UNCOV
364
  else if(has(changes, DecoderChangeFlags::EmergencyStop | DecoderChangeFlags::Throttle))
×
365
  {
UNCOV
366
    m_ioContext.post(
×
367
      [this,
×
368
        protocol=decoder.protocol.value(),
×
369
        address=decoder.address.value(),
×
370
        speedSteps=decoder.speedSteps.value(),
×
371
        throttle=decoder.throttle.value(),
×
372
        emergencyStop=decoder.emergencyStop.value()]()
×
373
      {
UNCOV
374
        if(auto* locomotive = getLocomotive(protocol, address, speedSteps))
×
375
        {
UNCOV
376
          if(emergencyStop)
×
377
            locomotive->stop();
×
378
          else
UNCOV
379
            locomotive->setSpeedStep(Decoder::throttleToSpeedStep(throttle, locomotive->speedSteps()));
×
380
        }
UNCOV
381
      });
×
382
  }
UNCOV
383
  else if(has(changes, DecoderChangeFlags::FunctionValue) && functionNumber <= std::numeric_limits<uint8_t>::max())
×
384
  {
UNCOV
385
    m_ioContext.post(
×
386
      [this,
×
387
        protocol=decoder.protocol.value(),
×
388
        address=decoder.address.value(),
×
389
        speedSteps=decoder.speedSteps.value(),
×
390
        index=static_cast<uint8_t>(functionNumber),
UNCOV
391
        value=decoder.getFunctionValue(functionNumber)]()
×
392
      {
UNCOV
393
        if(auto* locomotive = getLocomotive(protocol, address, speedSteps))
×
394
          locomotive->setFunctionValue(index, value);
×
395
      });
×
396
  }
UNCOV
397
}
×
398

UNCOV
399
bool Kernel::setOutput(OutputChannel channel, const OutputLocation& location, OutputValue value)
×
400
{
UNCOV
401
  assert(isEventLoopThread());
×
402

UNCOV
403
  switch(channel)
×
404
  {
UNCOV
405
    case OutputChannel::AccessoryDCC:
×
406
    case OutputChannel::AccessoryMotorola:
407
    {
UNCOV
408
      const auto switchProtocol = (channel == OutputChannel::AccessoryDCC) ? SwitchProtocol::DCC : SwitchProtocol::Motorola;
×
409
      m_ioContext.post(
×
410
        [this, switchProtocol, address=std::get<OutputAddress>(location).address, port=(std::get<OutputPairValue>(value) == OutputPairValue::Second)]()
×
411
        {
UNCOV
412
          switchManager().setSwitch(switchProtocol, address, port);
×
413
        });
×
414
      return true;
×
415
    }
UNCOV
416
    case OutputChannel::ECoSObject:
×
417
    {
UNCOV
418
      m_ioContext.post(
×
419
        [this, object=std::get<OutputECoSObject>(location).object, state=std::get<uint8_t>(value)]()
×
420
        {
UNCOV
421
          if(auto it = m_objects.find(object); it != m_objects.end())
×
422
          {
UNCOV
423
            if(auto* sw = dynamic_cast<Switch*>(it->second.get()))
×
424
            {
UNCOV
425
              sw->setState(state);
×
426
            }
427
          }
UNCOV
428
        });
×
429
      return true;
×
430
    }
UNCOV
431
    default: /*[[unlikely]]*/
×
432
      assert(false);
×
433
      break;
434
  }
435

436
  return false;
437
}
438

UNCOV
439
void Kernel::simulateInputChange(InputChannel channel, uint32_t address, SimulateInputAction action)
×
440
{
UNCOV
441
  if(!m_simulation)
×
442
    return;
×
443

UNCOV
444
  m_ioContext.post(
×
445
    [this, channel, address, action]()
×
446
    {
UNCOV
447
      switch(channel)
×
448
      {
UNCOV
449
        case InputChannel::S88:
×
450
        {
UNCOV
451
          uint16_t id = ObjectId::s88;
×
452
          uint32_t port = address - 1;
×
453
          auto it = m_objects.find(id);
×
454
          while(it != m_objects.end())
×
455
          {
UNCOV
456
            if(const auto* feedback = dynamic_cast<const Feedback*>(it->second.get()))
×
457
            {
UNCOV
458
              const auto ports = feedback->ports();
×
459
              if(port < ports)
×
460
              {
UNCOV
461
                uint16_t mask = 0;
×
462
                for(uint8_t i = 0; i < ports; i++)
×
463
                {
UNCOV
464
                  TriState value = feedback->operator[](i);
×
465
                  if(port == i)
×
466
                  {
UNCOV
467
                    switch(action)
×
468
                    {
UNCOV
469
                      case SimulateInputAction::SetFalse:
×
470
                        if(value == TriState::False)
×
471
                          return; // no change
×
472
                        value = TriState::False;
×
473
                        break;
×
474

UNCOV
475
                      case SimulateInputAction::SetTrue:
×
476
                        if(value == TriState::True)
×
477
                          return; // no change
×
478
                        value = TriState::True;
×
479
                        break;
×
480

UNCOV
481
                      case SimulateInputAction::Toggle:
×
482
                        value = (value == TriState::True) ? TriState::False : TriState::True;
×
483
                        break;
×
484
                    }
485
                  }
UNCOV
486
                  if(value == TriState::True)
×
487
                    mask |= 1 << i;
×
488
                }
489

UNCOV
490
                receive(
×
491
                  std::string("<EVENT ").append(std::to_string(feedback->id())).append(">\r\n")
×
492
                    .append(std::to_string(feedback->id())).append(" state[0x").append(mask != 0 ? ltrim(toHex(mask), '0') : std::string_view{"0"}).append("]>\r\n")
×
493
                    .append("<END 0 (OK)>\r\n"));
×
494

UNCOV
495
                break;
×
496
              }
UNCOV
497
              port -= ports;
×
498
            }
UNCOV
499
            it = m_objects.find(++id);
×
500
          }
UNCOV
501
          break;
×
502
        }
UNCOV
503
        case InputChannel::ECoSDetector:
×
504
          //! \todo Implement ECoS detector simulation
UNCOV
505
          break;
×
506

UNCOV
507
        default: [[unlikely]]
×
508
          assert(false);
×
509
          break;
510
      }
511
    });
512
}
513

UNCOV
514
void Kernel::switchManagerSwitched(SwitchProtocol protocol, uint16_t address, OutputPairValue value)
×
515
{
UNCOV
516
  ASSERT_IS_KERNEL_THREAD;
×
517

UNCOV
518
  if(!m_outputController)
×
519
    return;
×
520

UNCOV
521
  switch(protocol)
×
522
  {
UNCOV
523
    case SwitchProtocol::DCC:
×
524
      EventLoop::call(
×
525
        [this, address, value]()
×
526
        {
UNCOV
527
          m_outputController->updateOutputValue(OutputChannel::AccessoryDCC, OutputAddress(address), value);
×
528
        });
×
529
      break;
×
530

UNCOV
531
    case SwitchProtocol::Motorola:
×
532
      EventLoop::call(
×
533
        [this, address, value]()
×
534
        {
UNCOV
535
          m_outputController->updateOutputValue(OutputChannel::AccessoryMotorola, OutputAddress(address), value);
×
536
        });
×
537
      break;
×
538

UNCOV
539
    case SwitchProtocol::Unknown:
×
540
      assert(false);
×
541
      break;
542
  }
543
}
544

UNCOV
545
void Kernel::switchStateChanged(uint16_t objectId, uint8_t state)
×
546
{
UNCOV
547
  ASSERT_IS_KERNEL_THREAD;
×
548

UNCOV
549
  if(!m_outputController)
×
550
    return;
×
551

UNCOV
552
  EventLoop::call(
×
553
    [this, objectId, state]()
×
554
    {
UNCOV
555
      m_outputController->updateOutputValue(OutputChannel::ECoSObject, OutputECoSObject(objectId), state);
×
556
    });
×
557
}
558

UNCOV
559
void Kernel::feedbackStateChanged(Feedback& object, uint8_t port, TriState value)
×
560
{
UNCOV
561
  if(!m_inputController)
×
562
    return;
×
563

UNCOV
564
  if(isS88FeedbackId(object.id()))
×
565
  {
UNCOV
566
    uint32_t offset = 1;
×
567
    for(uint16_t id = ObjectId::s88; id < object.id(); id++)
×
568
    {
UNCOV
569
      auto it = m_objects.find(id);
×
570
      if(it == m_objects.end())
×
571
      {
UNCOV
572
        assert(false);
×
573
        return;
574
      }
575

UNCOV
576
      const auto* feedback = dynamic_cast<const Feedback*>(it->second.get());
×
577
      if(!feedback)
×
578
      {
UNCOV
579
        assert(false);
×
580
        return;
581
      }
582

UNCOV
583
      offset += feedback->ports();
×
584
    }
585

UNCOV
586
    EventLoop::call(
×
587
      [this, address=offset + port, value]()
×
588
      {
UNCOV
589
        m_inputController->updateInputValue(InputChannel::S88, address, value);
×
590
      });
×
591
  }
592
  else // ECoS Detector
593
  {
UNCOV
594
    const uint16_t portsPerObject = 16;
×
595
    const uint16_t address = 1 + port + portsPerObject * (object.id() - ObjectId::ecosDetector);
×
596

UNCOV
597
    EventLoop::call(
×
598
      [this, address, value]()
×
599
      {
UNCOV
600
        m_inputController->updateInputValue(InputChannel::ECoSDetector, address, value);
×
601
      });
×
602
  }
603
}
604

UNCOV
605
void Kernel::setIOHandler(std::unique_ptr<IOHandler> handler)
×
606
{
UNCOV
607
  assert(handler);
×
608
  assert(!m_ioHandler);
×
609
  m_ioHandler = std::move(handler);
×
610
}
×
611

UNCOV
612
bool Kernel::objectExists(uint16_t objectId) const
×
613
{
UNCOV
614
  return m_objects.find(objectId) != m_objects.end();
×
615
}
616

UNCOV
617
void Kernel::addObject(std::unique_ptr<Object> object)
×
618
{
UNCOV
619
  objectChanged(*object);
×
620
  m_objects.add(std::move(object));
×
621
}
×
622

UNCOV
623
void Kernel::objectChanged(Object& object)
×
624
{
UNCOV
625
  if(!m_onObjectChanged) /*[[unlikely]]*/
×
626
  {
UNCOV
627
    return;
×
628
  }
629

UNCOV
630
  std::string objectName;
×
631
  if(const auto* sw = dynamic_cast<const Switch*>(&object))
×
632
  {
UNCOV
633
    objectName = sw->nameUI();
×
634
  }
635

UNCOV
636
  EventLoop::call(
×
637
    [this, typeHash=typeid(object).hash_code(), objectId=object.id(), objectName]()
×
638
    {
UNCOV
639
      m_onObjectChanged(typeHash, objectId, objectName);
×
640
    });
×
641
}
×
642

UNCOV
643
void Kernel::removeObject(uint16_t objectId)
×
644
{
UNCOV
645
  m_objects.erase(objectId);
×
646
  if(m_onObjectRemoved) /*[[likely]]*/
×
647
  {
UNCOV
648
    m_onObjectRemoved(objectId);
×
649
  }
UNCOV
650
}
×
651

UNCOV
652
void Kernel::send(std::string_view message)
×
653
{
UNCOV
654
  if(m_ioHandler->send(message))
×
655
  {
UNCOV
656
    if(m_config.debugLogRXTX)
×
657
      EventLoop::call(
×
658
        [this, msg=std::string(rtrim(message, '\n'))]()
×
659
        {
UNCOV
660
          Log::log(logId, LogMessage::D2001_TX_X, msg);
×
661
        });
×
662
  }
663
  else
664
  {} // log message and go to error state
UNCOV
665
}
×
666

667
}
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