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

OpenLightingProject / ola / 16900099157

12 Aug 2025 05:43AM UTC coverage: 45.72% (-0.02%) from 45.742%
16900099157

Pull #2016

github

web-flow
Merge c368ef6ae into eaf937e80
Pull Request #2016: Bump actions/checkout from 4 to 5

7586 of 17462 branches covered (43.44%)

22424 of 49046 relevant lines covered (45.72%)

51.77 hits per line

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

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

21
#include <ola/Callback.h>
22
#include <ola/Constants.h>
23
#include <ola/Logging.h>
24
#include <ola/base/Array.h>
25
#include <ola/base/Flags.h>
26
#include <ola/base/Init.h>
27
#include <ola/base/SysExits.h>
28
#include <ola/io/SelectServer.h>
29
#include <ola/io/StdinHandler.h>
30
#include <ola/network/MACAddress.h>
31
#include <ola/rdm/RDMCommand.h>
32
#include <ola/rdm/RDMCommandSerializer.h>
33
#include <ola/rdm/RDMPacket.h>
34
#include <ola/rdm/UID.h>
35
#include <ola/strings/Format.h>
36
#include <ola/util/Utils.h>
37
#include <ola/stl/STLUtils.h>
38

39
#include <algorithm>
40
#include <cctype>
41
#include <iostream>
42
#include <map>
43
#include <memory>
44
#include <string>
45
#include <vector>
46

47
#include "libs/usb/JaRuleWidget.h"
48
#include "tools/ja-rule/USBDeviceManager.h"
49

50
using ola::NewCallback;
51
using ola::NewSingleCallback;
52
using ola::STLFind;
53
using ola::STLReplace;
54
using ola::io::ByteString;
55
using ola::io::SelectServer;
56
using ola::io::StdinHandler;
57
using ola::rdm::RDMCommand;
58
using ola::rdm::RDMCommandSerializer;
59
using ola::rdm::RDMDiscoveryResponse;
60
using ola::rdm::RDMRequest;
61
using ola::rdm::RDMResponse;
62
using ola::rdm::RDMSetRequest;
63
using ola::rdm::RDMStatusCode;
64
using ola::rdm::UID;
65
using ola::strings::ToHex;
66
using ola::usb::CommandClass;
67
using ola::usb::JaRuleReturnCode;
68
using ola::usb::JaRuleWidget;
69
using ola::utils::JoinUInt8;
70
using ola::utils::SplitUInt16;
71
using std::auto_ptr;
72
using std::cout;
73
using std::endl;
74
using std::string;
75
using std::vector;
76

77
DEFINE_string(target_uid, "7a70:00000001",
78
              "The UID of the responder to control.");
79
DEFINE_string(lower_uid, "0000:00000000", "The lower UID for the DUB.");
80
DEFINE_string(upper_uid, "ffff:ffffffff", "The upper UID for the DUB.");
81
DEFINE_uint8(port, 0, "The port to control");
82

83
/**
84
 * @brief Wait on input from the keyboard, and based on the input, send
85
 * messages to the device.
86
 */
87
class Controller {
88
 public:
89
  enum TimingOption {
90
    TIMING_BREAK,
91
    TIMING_MARK,
92
    TIMING_RDM_RESPONSE_TIMEOUT,
93
    TIMING_RDM_BROADCAST_TIMEOUT,
94
    TIMING_DUB_RESPONSE_LIMIT,
95
    TIMING_RESPONDER_DELAY,
96
    TIMING_RESPONDER_JITTER,
97
  };
98

99
  Controller(SelectServer* ss, const UID &target_uid,
×
100
               const UID &lower_uid, const UID &upper_uid)
101
      : m_ss(ss),
×
102
        m_our_uid(0, 0),
×
103
        m_target_uid(target_uid),
×
104
        m_lower_uid(lower_uid),
×
105
        m_upper_uid(upper_uid),
×
106
        m_stdin_handler(
×
107
            new StdinHandler(ss, ola::NewCallback(this, &Controller::Input))),
×
108
        m_widget(NULL),
×
109
        m_log_count(0),
×
110
        m_dmx_slot_data(0),
×
111
        m_mode(MODE_DEFAULT),
×
112
        m_current_timing_option(TIMING_BREAK) {
×
113
    m_actions['\n'] = Action(NewCallback(this, &Controller::Commit));
×
114
    m_actions[27] = Action(NewCallback(this, &Controller::ExitEditMode));
×
115
    m_actions['+'] = Action(NewCallback(this, &Controller::Adjust, true));
×
116
    m_actions['-'] = Action(NewCallback(this, &Controller::Adjust, false));
×
117
    m_actions['0'] = Action("Send a 0 length DMX frame",
×
118
                            NewCallback(this, &Controller::SendZeroDMX));
×
119
    m_actions['2'] = Action("Send 2 DMX frames back to back",
×
120
                            NewCallback(this, &Controller::SendDoubleDMX));
×
121
    m_actions['d'] = Action(
×
122
        "Send a DUB frame from 0000:00000000 to ffff:ffffffff",
123
        NewCallback(this, &Controller::SendDUB, UID(0, 0),
×
124
                    UID::AllDevices()));
×
125
    m_actions['D'] = Action(
×
126
        "Send a DUB frame from --lower-uid to --upper-uid",
127
        NewCallback(this, &Controller::SendDUB, m_lower_uid, m_upper_uid));
×
128
    m_actions['e'] = Action("Send an echo command",
×
129
                            NewCallback(this, &Controller::SendEcho));
×
130
    m_actions['f'] = Action("Fetch the flags state",
×
131
                            NewCallback(this, &Controller::GetFlags));
×
132
    m_actions['h'] = Action("Display help",
×
133
                            NewCallback(this, &Controller::PrintCommands));
×
134
    m_actions['i'] = Action(
×
135
        "Identify on",
136
        NewCallback(this, &Controller::SendIdentify, true));
×
137
    m_actions['I'] = Action(
×
138
        "Identify off",
139
        NewCallback(this, &Controller::SendIdentify, false));
×
140
    m_actions['m'] = Action(
×
141
        "Send a broadcast mute",
142
        NewCallback(this, &Controller::SendMute, UID::AllDevices()));
×
143
    m_actions['M'] = Action(
×
144
        "Send a mute to the target UID",
145
        NewCallback(this, &Controller::SendMute, m_target_uid));
×
146
    m_actions['q'] = Action(
×
147
        "Quit",
148
        NewCallback(m_ss, &SelectServer::Terminate));
×
149
    m_actions['r'] = Action(
×
150
        "Reset the device",
151
        NewCallback(this, &Controller::ResetDevice));
×
152
    m_actions['t'] = Action(
×
153
        "Send a DMX frame",
154
        NewCallback(this, &Controller::SendDMX));
×
155
    m_actions['u'] = Action(
×
156
        "Send a broadcast unmute",
157
        NewCallback(this, &Controller::SendUnMute, UID::AllDevices()));
×
158
    m_actions['U'] = Action(
×
159
        "Send an unmute to the target UID",
160
        NewCallback(this, &Controller::SendUnMute, m_target_uid));
×
161
    m_actions['.'] = Action(
×
162
        "Get the hardware info",
163
        NewCallback(this, &Controller::GetHardwareInfo));
×
164
    m_actions[','] = Action(
×
165
        "Run the self test",
166
        NewCallback(this, &Controller::RunSelfTest));
×
167

168
    // Timing Options
169
    // For each of the options below, we allow a bigger range than the device
170
    // itself so we can test out-of-range errors.
171
    STLReplace(&m_timing_settings, TIMING_BREAK,
×
172
               TimingSetting('b', "break time", 176,
×
173
                             40,  // actual min is 44
174
                             1000,  // actual max is 800
175
                             TIMING_UNITS_MICROSECONDS,
176
                             ola::usb::JARULE_CMD_GET_BREAK_TIME,
177
                             ola::usb::JARULE_CMD_SET_BREAK_TIME));
×
178

179
    STLReplace(&m_timing_settings, TIMING_MARK,
×
180
               TimingSetting('x', "mark time", 12,
×
181
                             2,  // actual min is 4
182
                             850,  // actual max is 800
183
                             TIMING_UNITS_MICROSECONDS,
184
                             ola::usb::JARULE_CMD_GET_MARK_TIME,
185
                             ola::usb::JARULE_CMD_SET_MARK_TIME));
×
186

187
    STLReplace(&m_timing_settings, TIMING_RDM_RESPONSE_TIMEOUT,
×
188
               TimingSetting('y', "RDM response timeout", 28,
×
189
                             5,  // actual min 10
190
                             55,  // action max is 50
191
                             TIMING_UNITS_TENTHS_OF_MILLI_SECONDS,
192
                             ola::usb::JARULE_CMD_GET_RDM_RESPONSE_TIMEOUT,
193
                             ola::usb::JARULE_CMD_GET_RDM_RESPONSE_TIMEOUT));
×
194

195
    STLReplace(&m_timing_settings, TIMING_RDM_BROADCAST_TIMEOUT,
×
196
               TimingSetting('z', "RDM broadcast response timeout", 28,
×
197
                             5,  // actual min 10
198
                             55,  // action max is 50
199
                             TIMING_UNITS_TENTHS_OF_MILLI_SECONDS,
200
                             ola::usb::JARULE_CMD_GET_RDM_BROADCAST_TIMEOUT,
201
                             ola::usb::JARULE_CMD_SET_RDM_BROADCAST_TIMEOUT));
×
202

203
    TimingSettingMap::const_iterator iter = m_timing_settings.begin();
×
204
    for (; iter != m_timing_settings.end(); ++iter) {
×
205
      const TimingSetting &setting = iter->second;
×
206
      m_actions[setting.character_code] = Action(
×
207
          "Get " + setting.description,
×
208
          NewCallback(this, &Controller::GetTime, iter->first));
×
209
      m_actions[toupper(setting.character_code)] = Action(
×
210
          "Set " + setting.description,
×
211
          NewCallback(this, &Controller::EditTiming, iter->first));
×
212
    }
213
  }
×
214

215
  ~Controller() {
×
216
    m_stdin_handler.reset();
×
217

218
    ActionMap::iterator iter = m_actions.begin();
×
219
    for (; iter != m_actions.end(); ++iter) {
×
220
      delete iter->second.action;
×
221
    }
222
  }
×
223

224
  void WidgetEvent(USBDeviceManager::EventType event,
×
225
                   JaRuleWidget* widget) {
226
    if (event == USBDeviceManager::WIDGET_ADDED) {
×
227
      OLA_INFO << "Open Lighting Device added";
×
228
      if (!m_widget) {
×
229
        m_widget = widget;
×
230
        m_our_uid = widget->GetUID();
×
231

232
        // Switch to controller mode.
233
        uint8_t mode = ola::usb::CONTROLLER_MODE;
×
234
        m_widget->SendCommand(
×
235
            FLAGS_port,
236
            ola::usb::JARULE_CMD_SET_MODE,
237
            &mode, sizeof(mode), NULL);
238
      } else {
239
        OLA_WARN << "Only a single device is supported";
×
240
      }
241
    } else {
242
      OLA_INFO << "Open Lighting Device removed";
×
243
      if (widget == m_widget) {
×
244
        m_widget = NULL;
×
245
      }
246
    }
247
  }
×
248

249
  void Input(int c) {
×
250
    Action *action = STLFind(&m_actions, c);
×
251
    if (action) {
×
252
      action->action->Run();
×
253
    } else {
254
      switch (c) {
×
255
        case 27:  // Escape
×
256
          if (m_mode != MODE_DEFAULT) {
×
257
            cout << "Edit Aborted" << endl;
×
258
            m_mode = MODE_DEFAULT;
×
259
          }
260
          break;
261
        default:
×
262
          {}
×
263
      }
264
    }
265
  }
×
266

267
  void PrintCommands() {
×
268
    vector<string> lines;
×
269
    for (ActionMap::iterator iter = m_actions.begin();
×
270
         iter != m_actions.end(); ++iter) {
×
271
      if (!iter->second.description.empty() &&
×
272
          std::isprint(iter->first)) {
×
273
        std::ostringstream str;
×
274
        str << " " << iter->first << " - " << iter->second.description << endl;
×
275
        lines.push_back(str.str());
×
276
      }
×
277
    }
278

279
    std::sort(lines.begin(), lines.end());
×
280

281
    cout << "Commands:" << endl;
×
282
    for (vector<string>::iterator iter = lines.begin();
×
283
         iter != lines.end(); ++iter) {
×
284
      cout << *iter;
×
285
    }
286
  }
×
287

288
  void EditTiming(TimingOption option) {
×
289
    m_mode = MODE_EDIT_TIMING;
×
290
    m_current_timing_option = option;
×
291

292
    const TimingSetting* setting = STLFind(&m_timing_settings, option);
×
293
    if (!setting) {
×
294
      OLA_WARN << "Missing timing setting " << option;
×
295
      return;
×
296
    }
297
    cout << "Editing " << setting->description << ", currently "
×
298
         << FormatTime(setting->units, setting->current_value) << "." << endl
×
299
         << "Use +/- to adjust, Enter commits, Esc to abort" << endl;
×
300
  }
301

302
  void EchoCommandComplete(ola::usb::USBCommandResult result,
×
303
                           JaRuleReturnCode return_code, uint8_t status_flags,
304
                           const ByteString &payload) {
305
    if (!CheckResult(result, status_flags)) {
×
306
      return;
×
307
    }
308

309
    string response;
×
310
    if (!payload.empty()) {
×
311
       response.append(reinterpret_cast<const char*>(payload.data()),
×
312
                       payload.size());
313
    }
314
    cout << "Echo Reply: RC " << return_code << ": " << response << endl;
×
315
  }
×
316

317
  void AckCommandComplete(
×
318
      ola::usb::USBCommandResult result,
319
      JaRuleReturnCode return_code, uint8_t status_flags,
320
      const ByteString &payload) {
321
    if (!CheckResult(result, status_flags)) {
×
322
      return;
323
    }
324

325
    OLA_INFO << "RC: " << return_code << ", payload_size: " << payload.size();
×
326
  }
327

328
  void GetFlagsCommandComplete(
×
329
      ola::usb::USBCommandResult result,
330
      JaRuleReturnCode return_code, uint8_t status_flags,
331
      const ByteString &payload) {
332
    if (!CheckResult(result, status_flags)) {
×
333
      return;
334
    }
335

336
    OLA_INFO << "RC: " << return_code << ", payload_size: " << payload.size();
×
337
    if (!payload.empty()) {
×
338
      ola::strings::FormatData(&std::cout, payload.data(),
×
339
                               payload.size());
×
340
    }
341
  }
342

343
  void DUBCommandComplete(ola::usb::USBCommandResult result,
×
344
                          JaRuleReturnCode return_code, uint8_t status_flags,
345
                          const ByteString &payload) {
346
    if (!CheckResult(result, status_flags)) {
×
347
      return;
348
    }
349

350
    cout << "DUB Response: RC: " << return_code << ", size: "
×
351
         << payload.size() << endl;
×
352
  }
353

354
  void DisplayTime(TimingOption option,
×
355
                   ola::usb::USBCommandResult result,
356
                   JaRuleReturnCode return_code, uint8_t status_flags,
357
                   const ByteString &payload) {
358
    if (!CheckResult(result, status_flags)) {
×
359
      return;
×
360
    }
361

362
    if (return_code != ola::usb::RC_OK) {
×
363
      OLA_INFO << "RC: " << return_code << ", payload_size: "
×
364
               << payload.size();
×
365
      return;
×
366
    }
367

368
    const TimingSetting* setting = STLFind(&m_timing_settings, option);
×
369
    if (!setting) {
×
370
      OLA_WARN << "Missing timing setting " << option;
×
371
      return;
×
372
    }
373

374
    uint16_t value = 0;
×
375
    if (payload.size() != sizeof(value)) {
×
376
      OLA_WARN << "Payload size mismatch";
×
377
      return;
×
378
    }
379

380
    value = JoinUInt8(payload[1], payload[0]);
×
381

382
    string description = setting->description;
×
383
    ola::CapitalizeFirst(&description);
×
384
    cout << description << ": " << FormatTime(setting->units, value) << endl;
×
385
  }
×
386

387
  void CommandComplete(ola::usb::USBCommandResult result,
×
388
                       JaRuleReturnCode return_code, uint8_t status_flags,
389
                       const ByteString &payload) {
390
    if (!CheckResult(result, status_flags)) {
×
391
      return;
392
    }
393

394
    OLA_INFO << "RC: " << return_code << ", payload_size: " << payload.size();
×
395

396
    if (!payload.empty()) {
×
397
      return;
398
    }
399

400
    if (payload[0] == ola::rdm::START_CODE) {
×
401
      RDMStatusCode status_code;
×
402
      // Skip over the start code.
403
      auto_ptr<RDMResponse> response(RDMResponse::InflateFromData(
×
404
          payload.data() + 1, payload.size() - 1, &status_code));
×
405

406
      if (!response.get()) {
×
407
        OLA_WARN << "Failed to inflate RDM response";
×
408
        if (!payload.empty()) {
×
409
          ola::strings::FormatData(&std::cout, payload.data(), payload.size());
×
410
        }
411
      }
412
      OLA_INFO << *response;
×
413
    }
×
414
  }
415

416
  void HardwareInfoComplete(
×
417
      ola::usb::USBCommandResult result,
418
      JaRuleReturnCode return_code, uint8_t status_flags,
419
      const ByteString &payload) {
420
    if (!CheckResult(result, status_flags)) {
×
421
      return;
422
    }
423

424
    OLA_INFO << "RC: " << return_code << ", payload_size: " << payload.size();
×
425
    if (payload.size() >= 14) {
×
426
      uint16_t model_id = JoinUInt8(payload[1], payload[0]);
×
427
      UID uid(payload.data() + sizeof(uint16_t));
×
428
      ola::network::MACAddress mac_address(
×
429
          payload.data() + sizeof(uint16_t) + UID::LENGTH);
×
430
      cout << "Model: " << model_id << ", UID: " << uid << ", MAC: "
×
431
           << mac_address << endl;
×
432
    } else {
433
      OLA_WARN << "Received " << payload.size() << " bytes, expecting 14";
×
434
    }
435
  }
436

437
  void SelfTestPart2Complete(ola::usb::USBCommandResult result,
×
438
                             JaRuleReturnCode return_code, uint8_t status_flags,
439
                             OLA_UNUSED const ByteString &payload) {
440
    if (!CheckResult(result, status_flags)) {
×
441
      return;
442
    }
443

444
    cout << "Test result: " << return_code << endl;
×
445
  }
446

447
  void SelfTestPart1Complete(ola::usb::USBCommandResult result,
×
448
                             JaRuleReturnCode return_code, uint8_t status_flags,
449
                             OLA_UNUSED const ByteString &payload) {
450
    if (!CheckResult(result, status_flags)) {
×
451
      return;
452
    }
453
    if (return_code == ola::usb::RC_OK) {
×
454
      m_widget->SendCommand(
×
455
          FLAGS_port, ola::usb::JARULE_CMD_RUN_SELF_TEST,
456
          NULL, 0,
457
          NewSingleCallback(this, &Controller::SelfTestPart2Complete));
×
458
    } else {
459
      OLA_WARN << "Unable to change to self test mode";
×
460
    }
461
  }
462

463
 private:
464
  enum Mode {
465
    MODE_DEFAULT,
466
    MODE_EDIT_TIMING,
467
  };
468

469
  typedef enum {
470
    TIMING_UNITS_MICROSECONDS,
471
    TIMING_UNITS_TENTHS_OF_MILLI_SECONDS,
472
  } TimingUnit;
473

474
  struct Action {
×
475
   public:
476
    typedef ola::Callback0<void> ActionCallback;
477

478
    Action() : action(NULL) {}
×
479
    explicit Action(ActionCallback *cb) : action(cb) {}
×
480
    Action(const string &description, ActionCallback *cb)
×
481
       : description(description), action(cb) {}
×
482

483
    string description;
484
    ActionCallback *action;
485
  };
486

487
  typedef std::map<char, Action> ActionMap;
488

489
  struct TimingSetting {
×
490
   public:
491
     TimingSetting(char character_code,
×
492
                   string description,
493
                   uint16_t initial_value,
494
                   uint16_t min_value,
495
                   uint16_t max_value,
496
                   TimingUnit units,
497
                   CommandClass get_command,
498
                   CommandClass set_command)
499
       : character_code(character_code),
×
500
         description(description),
×
501
         current_value(initial_value),
×
502
         min_value(min_value),
×
503
         max_value(max_value),
×
504
         units(units),
×
505
         get_command(get_command),
×
506
         set_command(set_command) {
×
507
    }
508

509
    char character_code;
510
    string description;
511
    uint16_t current_value;
512
    uint16_t min_value;
513
    uint16_t max_value;
514
    TimingUnit units;
515
    CommandClass get_command;
516
    CommandClass set_command;
517
  };
518

519
  typedef std::map<TimingOption, TimingSetting> TimingSettingMap;
520

521
  ActionMap m_actions;
522
  TimingSettingMap m_timing_settings;
523

524
  SelectServer* m_ss;
525
  UID m_our_uid, m_target_uid, m_lower_uid, m_upper_uid;
526
  auto_ptr<StdinHandler> m_stdin_handler;
527
  JaRuleWidget* m_widget;
528
  unsigned int m_log_count;
529
  uint8_t m_dmx_slot_data;
530
  Mode m_mode;
531
  TimingOption m_current_timing_option;
532

533
  bool CheckForWidget() const {
×
534
    if (!m_widget) {
×
535
      cout << "Device not present or device unavailable" << endl;
×
536
      return false;
×
537
    }
538
    return true;
539
  }
540

541
  void ResetDevice() {
×
542
    if (!CheckForWidget()) {
×
543
      return;
544
    }
545

546
    m_widget->SendCommand(
×
547
        FLAGS_port,
548
        ola::usb::JARULE_CMD_RESET_DEVICE,
549
        NULL, 0,
550
        NewSingleCallback(this, &Controller::AckCommandComplete));
×
551
  }
552

553
  void GetFlags() {
×
554
    if (!CheckForWidget()) {
×
555
      return;
556
    }
557

558
    m_widget->SendCommand(
×
559
        FLAGS_port,
560
        ola::usb::JARULE_CMD_GET_FLAGS,
561
        NULL, 0,
562
        NewSingleCallback(this, &Controller::GetFlagsCommandComplete));
×
563
  }
564

565
  void _SendDMX(const uint8_t *data, unsigned int size) {
×
566
    if (!CheckForWidget()) {
×
567
      return;
568
    }
569
    m_widget->SendCommand(
×
570
        FLAGS_port,
571
        ola::usb::JARULE_CMD_TX_DMX,
572
        data, size,
573
        NewSingleCallback(this, &Controller::CommandComplete));
×
574
  }
575

576
  void Adjust(bool increase) {
×
577
    TimingSetting* setting = STLFind(&m_timing_settings,
×
578
                                     m_current_timing_option);
×
579
    if (!setting) {
×
580
      OLA_WARN << "Missing timing setting " << m_current_timing_option;
×
581
      return;
×
582
    }
583

584
    if (increase) {
×
585
      setting->current_value++;
×
586
      if (setting->current_value > setting->max_value) {
×
587
        setting->current_value = setting->max_value;
×
588
      }
589
    } else {
590
      setting->current_value--;
×
591
      if (setting->current_value < setting->min_value) {
×
592
        setting->current_value = setting->min_value;
×
593
      }
594
    }
595

596
    string description = setting->description;
×
597
    ola::CapitalizeFirst(&description);
×
598
    cout << description << " is now "
×
599
         << FormatTime(setting->units, setting->current_value) << endl;
×
600
  }
×
601

602
  void Commit() {
×
603
    if (!CheckForWidget()) {
×
604
      return;
×
605
    }
606

607
    if (m_mode == MODE_DEFAULT) {
×
608
      return;
609
    }
610

611
    uint8_t payload[2];
×
612
    const TimingSetting* setting = STLFind(
×
613
        &m_timing_settings, m_current_timing_option);
×
614
    if (!setting) {
×
615
      OLA_WARN << "Missing timing setting " << m_current_timing_option;
×
616
      return;
×
617
    }
618
    SplitUInt16(setting->current_value, &payload[1], &payload[0]);
×
619

620
    m_widget->SendCommand(
×
621
        FLAGS_port, setting->set_command,
×
622
        payload, arraysize(payload),
623
        NewSingleCallback(this, &Controller::AckCommandComplete));
×
624
    m_mode = MODE_DEFAULT;
×
625
  }
626

627
  void ExitEditMode() {
×
628
    if (m_mode != MODE_DEFAULT) {
×
629
      cout << "Edit aborted" << endl;
×
630
      m_mode = MODE_DEFAULT;
×
631
    }
632
  }
×
633

634
  void SendZeroDMX() {
×
635
    _SendDMX(NULL, 0);
×
636
  }
×
637

638
  void SendDoubleDMX() {
×
639
    uint8_t payload[512];
×
640
    memset(payload, 0, 512);
×
641
    payload[0] = m_dmx_slot_data;
×
642
    _SendDMX(payload, arraysize(payload));
×
643
    m_dmx_slot_data += 16;
×
644
    payload[0] = m_dmx_slot_data;
×
645
    _SendDMX(payload, arraysize(payload));
×
646
    m_dmx_slot_data += 16;
×
647
  }
×
648

649
  void SendDMX() {
×
650
    if (!CheckForWidget()) {
×
651
      return;
×
652
    }
653

654
    uint8_t payload[512];
×
655
    memset(payload, 0, 512);
×
656
    payload[0] = m_dmx_slot_data;
×
657
    _SendDMX(payload, arraysize(payload));
×
658
    m_dmx_slot_data += 16;
×
659
  }
660

661
  void SendEcho() {
×
662
    if (!CheckForWidget()) {
×
663
      return;
×
664
    }
665

666
    const char payload[] = "echo test";
×
667
    m_widget->SendCommand(
×
668
        FLAGS_port,
669
        ola::usb::JARULE_CMD_ECHO,
670
        reinterpret_cast<const uint8_t*>(payload),
671
        arraysize(payload),
672
        NewSingleCallback(this, &Controller::EchoCommandComplete));
×
673
  }
674

675
  void GetTime(TimingOption option) {
×
676
    if (!CheckForWidget()) {
×
677
      return;
678
    }
679

680
    const TimingSetting* setting = STLFind(&m_timing_settings, option);
×
681
    if (!setting) {
×
682
      OLA_WARN << "Missing timing setting " << option;
×
683
      return;
×
684
    }
685
    m_widget->SendCommand(
×
686
        FLAGS_port,
687
        setting->get_command, NULL, 0,
×
688
        NewSingleCallback(this, &Controller::DisplayTime, option));
×
689
  }
690

691
  void SendDUB(UID lower, UID upper) {
×
692
    if (!CheckForWidget()) {
×
693
      return;
×
694
    }
695

696
    auto_ptr<RDMRequest> request(
×
697
        ola::rdm::NewDiscoveryUniqueBranchRequest(m_our_uid, lower, upper, 0));
×
698
    unsigned int rdm_length = RDMCommandSerializer::RequiredSize(*request);
×
699
    uint8_t data[rdm_length];
×
700
    RDMCommandSerializer::Pack(*request, data, &rdm_length);
×
701
    OLA_INFO << "Sending " << rdm_length << " byte RDM command.";
×
702
    m_widget->SendCommand(
×
703
        FLAGS_port,
704
        ola::usb::JARULE_CMD_RDM_DUB_REQUEST,
705
        data, rdm_length,
706
        NewSingleCallback(this, &Controller::DUBCommandComplete));
×
707
  }
×
708

709
  void SendIdentify(bool identify_on) {
×
710
    if (!CheckForWidget()) {
×
711
      return;
×
712
    }
713

714
    uint8_t param_data = identify_on;
×
715
    RDMSetRequest request(m_our_uid, m_target_uid, 0, 0, 0,
×
716
                          ola::rdm::PID_IDENTIFY_DEVICE, &param_data,
717
                          sizeof(param_data));
×
718

719
    unsigned int rdm_length = RDMCommandSerializer::RequiredSize(request);
×
720
    uint8_t data[rdm_length];
×
721
    RDMCommandSerializer::Pack(request, data, &rdm_length);
×
722
    m_widget->SendCommand(
×
723
        FLAGS_port,
724
        ola::usb::JARULE_CMD_RDM_REQUEST,
725
        data, rdm_length,
726
        NewSingleCallback(this, &Controller::CommandComplete));
×
727
  }
×
728

729
  void SendMute(UID target) {
×
730
    if (!CheckForWidget()) {
×
731
      return;
×
732
    }
733

734
    auto_ptr<RDMRequest> request(
×
735
        ola::rdm::NewMuteRequest(m_our_uid, target, 0));
×
736

737
    CommandClass command_class = target.IsBroadcast() ?
×
738
        ola::usb::JARULE_CMD_RDM_BROADCAST_REQUEST :
739
        ola::usb::JARULE_CMD_RDM_REQUEST;
×
740
    unsigned int rdm_length = RDMCommandSerializer::RequiredSize(*request);
×
741
    uint8_t data[rdm_length];
×
742
    RDMCommandSerializer::Pack(*request, data, &rdm_length);
×
743
    m_widget->SendCommand(
×
744
        FLAGS_port, command_class,
745
        data, rdm_length,
746
        NewSingleCallback(this, &Controller::CommandComplete));
×
747
  }
×
748

749
  void SendUnMute(UID target) {
×
750
    if (!CheckForWidget()) {
×
751
      return;
×
752
    }
753

754
    auto_ptr<RDMRequest> request(
×
755
        ola::rdm::NewUnMuteRequest(m_our_uid, target, 0));
×
756

757
    CommandClass command_class = target.IsBroadcast() ?
×
758
        ola::usb::JARULE_CMD_RDM_BROADCAST_REQUEST :
759
        ola::usb::JARULE_CMD_RDM_REQUEST;
×
760

761
    unsigned int rdm_length = RDMCommandSerializer::RequiredSize(*request);
×
762
    uint8_t data[rdm_length];
×
763
    RDMCommandSerializer::Pack(*request, data, &rdm_length);
×
764
    m_widget->SendCommand(
×
765
        FLAGS_port, command_class,
766
        data, rdm_length,
767
        NewSingleCallback(this, &Controller::CommandComplete));
×
768
  }
×
769

770
  void GetHardwareInfo() {
×
771
    if (!CheckForWidget()) {
×
772
      return;
773
    }
774
    m_widget->SendCommand(
×
775
        FLAGS_port, ola::usb::JARULE_CMD_GET_HARDWARE_INFO,
776
        NULL, 0,
777
        NewSingleCallback(this, &Controller::HardwareInfoComplete));
×
778
  }
779

780
  void RunSelfTest() {
×
781
    if (!CheckForWidget()) {
×
782
      return;
×
783
    }
784
    uint8_t mode = ola::usb::SELF_TEST_MODE;
×
785
    m_widget->SendCommand(
×
786
        FLAGS_port,
787
        ola::usb::JARULE_CMD_SET_MODE,
788
        &mode, sizeof(mode),
789
        NewSingleCallback(this, &Controller::SelfTestPart1Complete));
×
790
  }
791

792
  bool CheckResult(ola::usb::USBCommandResult result,
×
793
                   uint8_t status_flags) {
794
    if (result != ola::usb::COMMAND_RESULT_OK) {
×
795
      OLA_WARN << "Error: " << result;
×
796
      return false;
×
797
    }
798
    if (status_flags & ola::usb::FLAGS_CHANGED_FLAG) {
×
799
      OLA_INFO << "Flags changed!";
×
800
    }
801
    if (status_flags & ola::usb::MSG_TRUNCATED_FLAG) {
×
802
      OLA_INFO << "Message truncated";
×
803
    }
804
    return true;
805
  }
806

807
  string FormatTime(TimingUnit units, uint16_t value) {
×
808
    std::stringstream str;
×
809
    if (units == TIMING_UNITS_MICROSECONDS) {
×
810
      str << value << " us";
×
811
    } else if (units == TIMING_UNITS_TENTHS_OF_MILLI_SECONDS) {
×
812
      float adjusted_time = value / 10.0;
×
813
      str << adjusted_time << " ms";
×
814
    }
815
    return str.str();
×
816
  }
×
817

818
  DISALLOW_COPY_AND_ASSIGN(Controller);
819
};
820

821
void ParseUID(const string &uid_str, UID *uid) {
×
822
  auto_ptr<UID> target_uid(UID::FromString(uid_str));
×
823
  if (!target_uid.get()) {
×
824
    OLA_WARN << "Invalid UID: '" << uid_str << "'";
×
825
    exit(ola::EXIT_USAGE);
×
826
  }
827
  *uid = *target_uid;
×
828
}
×
829

830
/*
831
 * Main.
832
 */
833
int main(int argc, char **argv) {
×
834
  ola::AppInit(&argc, argv, "[ options ]", "Ja Rule Admin Tool");
×
835

836
  UID target_uid(0, 0), lower_uid(0, 0), upper_uid(0, 0);
×
837
  ParseUID(FLAGS_target_uid.str(), &target_uid);
×
838
  ParseUID(FLAGS_lower_uid.str(), &lower_uid);
×
839
  ParseUID(FLAGS_upper_uid.str(), &upper_uid);
×
840

841
  SelectServer ss;
×
842
  Controller controller(&ss, target_uid, lower_uid, upper_uid);
×
843

844
  USBDeviceManager manager(
×
845
    &ss, NewCallback(&controller, &Controller::WidgetEvent));
×
846

847
  if (!manager.Start()) {
×
848
    exit(ola::EXIT_UNAVAILABLE);
×
849
  }
850

851
  // Print this via cout to ensure we actually get some output by default
852
  cout << "Press h to print a help message" << endl;
×
853

854
  ss.Run();
×
855
  return ola::EXIT_OK;
×
856
}
×
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