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

traintastic / traintastic / 23669027368

27 Mar 2026 09:50PM UTC coverage: 26.198% (+0.02%) from 26.176%
23669027368

push

github

reinder
Merge remote-tracking branch 'origin/master' into cbus

11 of 144 new or added lines in 34 files covered. (7.64%)

1 existing line in 1 file now uncovered.

8256 of 31514 relevant lines covered (26.2%)

182.55 hits per line

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

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

23
#include "simulationiohandler.hpp"
24
#include "../kernel.hpp"
25
#include "../messages.hpp"
26
#include "../../../../utils/rtrim.hpp"
27

28
namespace ECoS {
29

30
static bool appendOption(std::string& response, const Simulation::ECoS& ecos, std::string_view option)
×
31
{
32
  if(option == Option::commandstationtype)
×
33
  {
34
    response.append("\"").append(ecos.commandStationType).append("\"");
×
35
  }
36
  else if(option == Option::protocolversion)
×
37
  {
38
    response.append(ecos.protocolVersion);
×
39
  }
40
  else if(option == Option::hardwareversion)
×
41
  {
42
    response.append(ecos.hardwareVersion);
×
43
  }
44
  else if(option == Option::applicationversion)
×
45
  {
46
    response.append(ecos.applicationVersion);
×
47
  }
48
  else if(option == Option::applicationversionsuffix)
×
49
  {
50
    response.append("\"").append(ecos.applicationVersionSuffix).append("\"");
×
51
  }
52
  else if(option == Option::railcom)
×
53
  {
54
    response.append(ecos.railcom ? "1" : "0");
×
55
  }
56
  else if(option == Option::railcomplus)
×
57
  {
58
    response.append(ecos.railcomPlus ? "1" : "0");
×
59
  }
60
  else
61
  {
62
    return false;
×
63
  }
64
  return true;
×
65
}
66

67
static bool appendOption(std::string& response, const Simulation::Switch& sw, std::string_view option)
×
68
{
69
  if(option == Option::name1)
×
70
  {
71
    response.append("\"").append(sw.name1).append("\"");
×
72
  }
73
  else if(option == Option::name2)
×
74
  {
75
    response.append("\"").append(sw.name2).append("\"");
×
76
  }
77
  else if(option == Option::name3)
×
78
  {
79
    response.append("\"").append(sw.name3).append("\"");
×
80
  }
81
  else if(option == Option::addr)
×
82
  {
83
    response.append(std::to_string(sw.address));
×
84
  }
85
  else if(option == Option::addrext)
×
86
  {
87
    response.append(sw.addrext);
×
88
  }
89
  else if(option == Option::symbol)
×
90
  {
91
    response.append(std::to_string(sw.symbol));
×
92
  }
93
  else if(option == Option::type)
×
94
  {
95
    response.append(sw.type);
×
96
  }
97
  else if(option == Option::protocol)
×
98
  {
99
    response.append(sw.protocol);
×
100
  }
101
  else if(option == Option::state)
×
102
  {
103
    response.append(std::to_string(sw.state));
×
104
  }
105
  else if(option == Option::mode)
×
106
  {
107
    response.append(sw.mode);
×
108
  }
109
  else if(option == Option::duration)
×
110
  {
111
    response.append(std::to_string(sw.duration));
×
112
  }
113
  else if(option == Option::variant)
×
114
  {
115
    response.append(std::to_string(sw.variant));
×
116
  }
117
  else
118
  {
119
    return false;
×
120
  }
121
  return true;
×
122
}
123

124

125
SimulationIOHandler::SimulationIOHandler(Kernel& kernel, const Simulation& simulation)
×
126
  : IOHandler(kernel)
127
  , m_simulation{simulation}
×
128
{
129
}
×
130

131
void SimulationIOHandler::start()
×
132
{
133
  m_kernel.started();
×
134
}
×
135

136
bool SimulationIOHandler::send(std::string_view message)
×
137
{
138
  Request request;
×
139
  if(!parseRequest(message, request))
×
140
    return false;
×
141

142
  if(request.command == Command::request && request.options.size() == 1 && request.options[0] == Option::view)
×
143
  {
144
    return replyOk(message); // notify view active
×
145
  }
146

147
  if(request.objectId == ObjectId::ecos)
×
148
  {
149
    if(request.command == Command::get)
×
150
    {
151
      std::string response{replyHeader(message)};
×
152
      for(auto option : request.options)
×
153
      {
154
        response.append(std::to_string(request.objectId)).append(" ").append(option).append("[");
×
155
        if(!appendOption(response, m_simulation.ecos, option))
×
156
        {
157
          return replyErrorUnknownOption(message, option);
×
158
        }
159
        response.append("]\r\n");
×
160
      }
161
      response.append("<END 0 (OK)>\r\n");
×
162

163
      return reply(response);
×
164
    }
×
165
    if(request.command == Command::set && request.options.size() == 1 && (request.options[0] == Option::stop || request.options[0] == Option::go))
×
166
    {
167
      return replyOk(message);
×
168
    }
169
  }
170
  else if(request.objectId == ObjectId::locomotiveManager)
×
171
  {
172
    if(request.command == Command::queryObjects)
×
173
    {
174
      std::string response{replyHeader(message)};
×
175
      for(const auto& locomotive : m_simulation.locomotives)
×
176
      {
177
        response.append(std::to_string(locomotive.id));
×
178
        for(auto option : request.options)
×
179
        {
180
          if(option == Option::protocol)
×
181
            response.append(" protocol[").append(toString(locomotive.protocol)).append("]");
×
182
          else if(option == Option::addr)
×
183
            response.append(" addr[").append(std::to_string(locomotive.address)).append("]");
×
184
        }
185
        response.append("\r\n");
×
186
      }
187
      response.append("<END 0 (OK)>\r\n");
×
188

189
      return reply(response);
×
190
    }
×
191
  }
192
  else if(request.objectId == ObjectId::switchManager)
×
193
  {
194
    if(request.command == Command::set && request.options.size() == 1)
×
195
    {
196
      std::string_view option;
×
197
      std::string_view value;
×
198
      if(parseOptionValue(request.options[0], option, value) && option == Option::switch_)
×
199
      {
200
        return replyOk(message); // notify executed
×
201
      }
202
    }
203
    else if(request.command == Command::queryObjects)
×
204
    {
205
      std::string response{replyHeader(message)};
×
206
      for(const auto& sw : m_simulation.switches)
×
207
      {
208
        response.append(std::to_string(sw.id));
×
209
        for(auto option : request.options)
×
210
        {
211
          response.append(" ").append(option).append("[");
×
212
          if(!appendOption(response, sw, option))
×
213
          {
214
            assert(false); // FIXME: add error response
×
215
          }
216
          response.append("]");
×
217
        }
218
        response.append("\r\n");
×
219
      }
220
      response.append("<END 0 (OK)>\r\n");
×
221
      return reply(response);
×
222
    }
×
223
  }
224
  else if(request.objectId == ObjectId::feedbackManager)
×
225
  {
226
    if(request.command == Command::queryObjects)
×
227
    {
228
      std::string response{replyHeader(message)};
×
229
      for(const auto& s88 : m_simulation.s88)
×
230
      {
231
        response.append(std::to_string(s88.id));
×
232
        for(auto option : request.options)
×
233
        {
234
          if(option == Option::ports)
×
235
            response.append(" ports[").append(std::to_string(s88.ports)).append("]");
×
236
        }
237
        response.append("\r\n");
×
238
      }
239
      response.append("<END 0 (OK)>\r\n");
×
240

241
      return reply(response);
×
242
    }
×
243
  }
244
  else if(auto itLocomotive = std::find_if(m_simulation.locomotives.begin(), m_simulation.locomotives.end(),
×
245
    [id=request.objectId](const auto& v)
×
246
    {
247
      return v.id == id;
×
248
    }); itLocomotive != m_simulation.locomotives.end())
×
249
  {
250
    if(request.command == Command::get)
×
251
    {
252
      std::string_view key;
×
253
      std::string_view value;
×
254
      std::string response{replyHeader(message)};
×
255
      for(auto option : request.options)
×
256
      {
257
        if(option == Option::dir)
×
258
          response.append(std::to_string(request.objectId)).append(" dir[0]\r\n");
×
259
        else if(option == Option::speedStep)
×
260
          response.append(std::to_string(request.objectId)).append(" speedstep[0]\r\n");
×
261
        else if(parseOptionValue(option, key, value) && key == Option::func)
×
262
          response.append(std::to_string(request.objectId)).append(" func[").append(value).append(",0]\r\n");
×
263
        else
264
          assert(false);
×
265
      }
266
      response.append("<END 0 (OK)>\r\n");
×
267

268
      return reply(response);
×
269
    }
×
270
  }
271
  else if(auto itSwitch = std::find_if(m_simulation.switches.begin(), m_simulation.switches.end(),
×
272
    [id=request.objectId](const auto& v)
×
273
    {
274
      return v.id == id;
×
275
    }); itSwitch != m_simulation.switches.end())
×
276
  {
277
    const auto& sw = *itSwitch;
×
278

279
    if(request.command == Command::get)
×
280
    {
281
      std::string response{replyHeader(message)};
×
282
      for(auto option : request.options)
×
283
      {
284
        response.append(std::to_string(request.objectId)).append(" ").append(option).append("[");
×
285
        if(!appendOption(response, sw, option))
×
286
        {
287
          return replyErrorUnknownOption(message, option);
×
288
        }
289
        response.append("]\r\n");
×
290
      }
291
      response.append("<END 0 (OK)>\r\n");
×
292

293
      return reply(response);
×
294
    }
×
295
  }
296
  else if(auto it = std::find_if(m_simulation.s88.begin(), m_simulation.s88.end(),
×
297
    [id=request.objectId](const auto& v)
×
298
    {
299
      return v.id == id;
×
300
    }); it != m_simulation.s88.end())
×
301
  {
302
    if(request.command == Command::get)
×
303
    {
304
      std::string response{replyHeader(message)};
×
305
      for(auto option : request.options)
×
306
      {
307
        if(option == Option::state)
×
308
          response.append(std::to_string(request.objectId)).append(" state[0x0]\r\n");
×
309
      }
310
      response.append("<END 0 (OK)>\r\n");
×
311

312
      return reply(response);
×
313
    }
×
314
  }
315

316
  return reply(std::string("<REPLY ").append(message).append(">\r\n<END 999 (Traintastic: no simulation support)>\r\n"));
×
317
}
×
318

319
bool SimulationIOHandler::reply(std::string_view message)
×
320
{
321
  // post the reply, so it has some delay
NEW
322
  boost::asio::post(m_kernel.ioContext(), 
×
323
    [this, data=std::string(message)]()
×
324
    {
325
      m_kernel.receive(data);
×
326
    });
×
327

328
  return true;
×
329
}
330

331
bool SimulationIOHandler::replyOk(std::string_view request)
×
332
{
333
  return reply(replyHeader(request).append("<END 0 (OK)>\r\n"));
×
334
}
335

336
std::string SimulationIOHandler::replyHeader(std::string_view request)
×
337
{
338
  return std::string("<REPLY ").append(rtrim(request, {'\r', '\n'})).append(">\r\n");
×
339
}
340

341
bool SimulationIOHandler::replyErrorUnknownOption(std::string_view request, std::string_view option)
×
342
{
343
  assert(option.data() >= request.data() && (option.data() + option.size()) <= (request.data() + request.size())); // option view must be within request view
×
344
  const auto pos = option.data() - request.data();
×
345
  return reply(replyHeader(request).append("<END 11 (unknown option at ").append(std::to_string(1 + pos)).append(")>\r\n"));
×
346
}
347

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