PageRenderTime 45ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/common/rdm/RDMCommand.cpp

https://code.google.com/
C++ | 818 lines | 591 code | 95 blank | 132 comment | 93 complexity | cc618bb334ddefe13564bcdece4595cb MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0
  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  15. *
  16. * RDMCommand.cpp
  17. * The RDMCommand class
  18. * Copyright (C) 2010 Simon Newton
  19. */
  20. #include <string.h>
  21. #include <string>
  22. #include "ola/Logging.h"
  23. #include "ola/network/NetworkUtils.h"
  24. #include "ola/rdm/RDMCommand.h"
  25. #include "ola/rdm/UID.h"
  26. namespace ola {
  27. namespace rdm {
  28. /*
  29. * Constructor
  30. */
  31. RDMCommand::RDMCommand(const UID &source,
  32. const UID &destination,
  33. uint8_t transaction_number,
  34. uint8_t port_id,
  35. uint8_t message_count,
  36. uint16_t sub_device,
  37. uint16_t param_id,
  38. const uint8_t *data,
  39. unsigned int length):
  40. m_port_id(port_id),
  41. m_source(source),
  42. m_destination(destination),
  43. m_transaction_number(transaction_number),
  44. m_message_count(message_count),
  45. m_sub_device(sub_device),
  46. m_param_id(param_id),
  47. m_data(NULL),
  48. m_data_length(length) {
  49. SetParamData(data, length);
  50. }
  51. /*
  52. * Destructor
  53. */
  54. RDMCommand::~RDMCommand() {
  55. if (m_data)
  56. delete[] m_data;
  57. }
  58. /*
  59. * Equality test
  60. */
  61. bool RDMCommand::operator==(const RDMCommand &other) const {
  62. if (m_source == other.m_source &&
  63. m_destination == other.m_destination &&
  64. m_transaction_number == other.m_transaction_number &&
  65. m_message_count == other.m_message_count &&
  66. m_sub_device == other.m_sub_device &&
  67. CommandClass() == other.CommandClass() &&
  68. m_param_id == other.m_param_id &&
  69. m_data_length == other.m_data_length) {
  70. return 0 == memcmp(m_data, other.m_data, m_data_length);
  71. }
  72. return false;
  73. }
  74. std::string RDMCommand::ToString() const {
  75. std::stringstream str;
  76. str << m_source << " -> " << m_destination << ", Trans # " <<
  77. static_cast<int>(m_transaction_number) << ", Port ID " <<
  78. static_cast<int>(m_port_id) << ", Msg Cnt " <<
  79. static_cast<int>(m_message_count) << ", SubDevice " << m_sub_device
  80. << ", Cmd Class " << CommandClass() << ", Param ID " << m_param_id
  81. << ", Data Len " << m_data_length;
  82. str << ", Data ";
  83. for (unsigned int i = 0 ; i < m_data_length; i++)
  84. str << std::hex << std::setw(2) << static_cast<int>(m_data[i]) << " ";
  85. return str.str();
  86. }
  87. /*
  88. * Get the size of the raw data required to pack this RDM command
  89. */
  90. unsigned int RDMCommand::Size() const {
  91. return sizeof(rdm_command_message) + m_data_length + CHECKSUM_LENGTH;
  92. }
  93. /*
  94. * Pack this command into an RDM message structure.
  95. * The packed data structure does not include the RDM start code (0xCC) because
  96. * sometimes devices / protocols keep this separate.
  97. */
  98. bool RDMCommand::Pack(uint8_t *buffer, unsigned int *size) const {
  99. return Pack(buffer, size, m_source, m_transaction_number, m_port_id);
  100. }
  101. /*
  102. * Pack this command into an string.
  103. * The packed data does not include the RDM start code (0xCC) because
  104. * sometimes devices / protocols keep this separate.
  105. */
  106. bool RDMCommand::Pack(string *data) const {
  107. return Pack(data, m_source, m_transaction_number, m_port_id);
  108. }
  109. /*
  110. * Pack this command into an RDM message structure with additional fields
  111. * The packed data structure does not include the RDM start code (0xCC) because
  112. * sometimes devices / protocols keep this separate.
  113. */
  114. bool RDMCommand::Pack(uint8_t *buffer, unsigned int *size, const UID &source,
  115. uint8_t transaction_number,
  116. uint8_t port_id) const {
  117. if (*size < Size())
  118. return false;
  119. unsigned int packet_length = (sizeof(rdm_command_message) +
  120. m_data_length); // size of packet excluding start code + checksum
  121. rdm_command_message message;
  122. message.sub_start_code = SUB_START_CODE;
  123. message.message_length = packet_length + 1; // add in start code as well
  124. m_destination.Pack(message.destination_uid, UID::UID_SIZE);
  125. source.Pack(message.source_uid, UID::UID_SIZE);
  126. message.transaction_number = transaction_number;
  127. message.port_id = port_id;
  128. message.message_count = m_message_count;
  129. message.sub_device[0] = m_sub_device >> 8;
  130. message.sub_device[1] = m_sub_device & 0xff;
  131. message.command_class = CommandClass();
  132. message.param_id[0] = m_param_id >> 8;
  133. message.param_id[1] = m_param_id & 0xff;
  134. message.param_data_length = m_data_length;
  135. memcpy(buffer, &message, sizeof(message));
  136. memcpy(buffer + sizeof(rdm_command_message), m_data, m_data_length);
  137. uint16_t checksum = CalculateChecksum(buffer, packet_length);
  138. buffer[packet_length] = checksum >> 8;
  139. buffer[packet_length+1] = checksum & 0xff;
  140. *size = packet_length + CHECKSUM_LENGTH;
  141. return true;
  142. }
  143. /**
  144. * Write this RDMCommand to an output stream
  145. */
  146. void RDMCommand::Write(ola::io::OutputStream *stream) const {
  147. unsigned int packet_length = (sizeof(rdm_command_message) +
  148. m_data_length); // size of packet excluding start code + checksum
  149. rdm_command_message message;
  150. message.sub_start_code = SUB_START_CODE;
  151. message.message_length = packet_length + 1; // add in start code as well
  152. m_destination.Pack(message.destination_uid, UID::UID_SIZE);
  153. m_source.Pack(message.source_uid, UID::UID_SIZE);
  154. message.transaction_number = m_transaction_number;
  155. message.port_id = m_port_id;
  156. message.message_count = m_message_count;
  157. message.sub_device[0] = m_sub_device >> 8;
  158. message.sub_device[1] = m_sub_device & 0xff;
  159. message.command_class = CommandClass();
  160. message.param_id[0] = m_param_id >> 8;
  161. message.param_id[1] = m_param_id & 0xff;
  162. message.param_data_length = m_data_length;
  163. unsigned int checksum_value = START_CODE;
  164. // checksum & write out the header
  165. const uint8_t *ptr = reinterpret_cast<uint8_t*>(&message);
  166. for (unsigned int i = 0; i != sizeof(message); i++)
  167. checksum_value += ptr[i];
  168. stream->Write(reinterpret_cast<uint8_t*>(&message), sizeof(message));
  169. // checksum & write out the data
  170. for (unsigned int i = 0; i != m_data_length; i++)
  171. checksum_value += m_data[i];
  172. stream->Write(m_data, m_data_length);
  173. uint16_t checksum = static_cast<uint16_t>(checksum_value);
  174. *stream << ola::network::HostToNetwork(checksum);
  175. }
  176. bool RDMCommand::Pack(string *buffer,
  177. const UID &source,
  178. uint8_t transaction_number,
  179. uint8_t port_id) const {
  180. if (!buffer)
  181. return false;
  182. uint8_t data[
  183. sizeof(rdm_command_message) + MAX_PARAM_DATA_LENGTH + CHECKSUM_LENGTH];
  184. unsigned int size = sizeof(data);
  185. bool r = Pack(data, &size, source, transaction_number, port_id);
  186. if (r)
  187. buffer->assign(reinterpret_cast<char*>(data), size);
  188. return r;
  189. }
  190. void RDMCommand::SetParamData(const uint8_t *data, unsigned int length) {
  191. if (length > MAX_PARAM_DATA_LENGTH) {
  192. OLA_WARN << "Attempt to create RDM message with a length > " <<
  193. MAX_PARAM_DATA_LENGTH << ", was; " << length;
  194. m_data_length = MAX_PARAM_DATA_LENGTH;
  195. } else {
  196. m_data_length = length;
  197. }
  198. if (m_data_length > 0 && data != NULL) {
  199. if (m_data)
  200. delete[] m_data;
  201. m_data = new uint8_t[m_data_length];
  202. memcpy(m_data, data, m_data_length);
  203. }
  204. }
  205. /*
  206. * Convert a block of RDM data to an RDMCommand object.
  207. * The data must not include the RDM start code.
  208. * @param data the raw RDM data, starting from the sub-start-code
  209. * @param length the length of the data
  210. * @param command_message the command_message struct to copy the data to
  211. * @return a rdm_response_code
  212. */
  213. rdm_response_code RDMCommand::VerifyData(
  214. const uint8_t *data,
  215. unsigned int length,
  216. RDMCommand::rdm_command_message *command_message) {
  217. if (!data) {
  218. OLA_WARN << "RDM data was null";
  219. return RDM_INVALID_RESPONSE;
  220. }
  221. if (length < sizeof(rdm_command_message)) {
  222. OLA_WARN << "RDM message is too small, needs to be at least " <<
  223. sizeof(rdm_command_message) << ", was " << length;
  224. return RDM_PACKET_TOO_SHORT;
  225. }
  226. memcpy(reinterpret_cast<uint8_t*>(command_message),
  227. data,
  228. sizeof(*command_message));
  229. if (command_message->sub_start_code != SUB_START_CODE) {
  230. OLA_WARN << "Sub start code mis match, was 0x" << std::hex <<
  231. static_cast<int>(command_message->sub_start_code) << ", required 0x"
  232. << static_cast<int>(SUB_START_CODE);
  233. return RDM_WRONG_SUB_START_CODE;
  234. }
  235. unsigned int message_length = command_message->message_length;
  236. if (length < message_length + 1) {
  237. OLA_WARN << "RDM message is too small, needs to be " <<
  238. message_length + 1 << ", was " << length;
  239. return RDM_PACKET_LENGTH_MISMATCH;
  240. }
  241. uint16_t checksum = CalculateChecksum(data, message_length - 1);
  242. uint16_t actual_checksum = (data[message_length - 1] << 8) +
  243. data[message_length];
  244. if (actual_checksum != checksum) {
  245. OLA_WARN << "RDM checksum mismatch, was " << actual_checksum <<
  246. " but was supposed to be " << checksum;
  247. return RDM_CHECKSUM_INCORRECT;
  248. }
  249. // check param length is valid here
  250. unsigned int block_size = length - sizeof(rdm_command_message) - 2;
  251. if (command_message->param_data_length > block_size) {
  252. OLA_WARN << "Param length " <<
  253. static_cast<int>(command_message->param_data_length) <<
  254. " exceeds remaining RDM message size of " << block_size;
  255. return RDM_PARAM_LENGTH_MISMATCH;
  256. }
  257. return RDM_COMPLETED_OK;
  258. }
  259. /*
  260. * Calculate the checksum of this packet
  261. */
  262. uint16_t RDMCommand::CalculateChecksum(const uint8_t *data,
  263. unsigned int packet_length) {
  264. unsigned int checksum_value = START_CODE;
  265. for (unsigned int i = 0; i < packet_length; i++)
  266. checksum_value += data[i];
  267. return static_cast<uint16_t>(checksum_value);
  268. }
  269. /*
  270. * Convert the Command Class int to an enum
  271. */
  272. RDMCommand::RDMCommandClass RDMCommand::ConvertCommandClass(
  273. uint8_t command_class) {
  274. switch (command_class) {
  275. case DISCOVER_COMMAND:
  276. return DISCOVER_COMMAND;
  277. case DISCOVER_COMMAND_RESPONSE:
  278. return DISCOVER_COMMAND_RESPONSE;
  279. case GET_COMMAND:
  280. return GET_COMMAND;
  281. case GET_COMMAND_RESPONSE:
  282. return GET_COMMAND_RESPONSE;
  283. case SET_COMMAND:
  284. return SET_COMMAND;
  285. case SET_COMMAND_RESPONSE:
  286. return SET_COMMAND_RESPONSE;
  287. default:
  288. return INVALID_COMMAND;
  289. }
  290. }
  291. /*
  292. * Inflate a request from some data
  293. */
  294. RDMRequest* RDMRequest::InflateFromData(const uint8_t *data,
  295. unsigned int length) {
  296. rdm_command_message command_message;
  297. rdm_response_code code = VerifyData(data, length, &command_message);
  298. if (code != RDM_COMPLETED_OK)
  299. return NULL;
  300. uint16_t sub_device = ((command_message.sub_device[0] << 8) +
  301. command_message.sub_device[1]);
  302. uint16_t param_id = ((command_message.param_id[0] << 8) +
  303. command_message.param_id[1]);
  304. RDMCommandClass command_class = ConvertCommandClass(
  305. command_message.command_class);
  306. switch (command_class) {
  307. case GET_COMMAND:
  308. return new RDMGetRequest(
  309. UID(command_message.source_uid),
  310. UID(command_message.destination_uid),
  311. command_message.transaction_number, // transaction #
  312. command_message.port_id, // port id
  313. command_message.message_count, // message count
  314. sub_device,
  315. param_id,
  316. data + sizeof(rdm_command_message),
  317. command_message.param_data_length); // data length
  318. case SET_COMMAND:
  319. return new RDMSetRequest(
  320. UID(command_message.source_uid),
  321. UID(command_message.destination_uid),
  322. command_message.transaction_number, // transaction #
  323. command_message.port_id, // port id
  324. command_message.message_count, // message count
  325. sub_device,
  326. param_id,
  327. data + sizeof(rdm_command_message),
  328. command_message.param_data_length); // data length
  329. default:
  330. OLA_WARN << "Expected a RDM request command but got " << command_class;
  331. return NULL;
  332. }
  333. }
  334. /**
  335. * Inflate from some data
  336. */
  337. RDMRequest* RDMRequest::InflateFromData(const string &data) {
  338. return InflateFromData(reinterpret_cast<const uint8_t*>(data.data()),
  339. data.size());
  340. }
  341. /**
  342. * Inflate a request from some data
  343. * @param the request data
  344. * @param length the length of the request data
  345. * @param request an optional RDMRequest object that this response is for
  346. * @param a pointer to a rdm_response_code to set
  347. * @returns a new RDMResponse object, or NULL is this response is invalid
  348. */
  349. RDMResponse* RDMResponse::InflateFromData(const uint8_t *data,
  350. unsigned int length,
  351. rdm_response_code *response_code,
  352. const RDMRequest *request) {
  353. if (request)
  354. return InflateFromData(data,
  355. length,
  356. response_code,
  357. request,
  358. request->TransactionNumber());
  359. else
  360. return InflateFromData(data, length, response_code, request, 0);
  361. }
  362. RDMResponse* RDMResponse::InflateFromData(const uint8_t *data,
  363. unsigned int length,
  364. rdm_response_code *response_code,
  365. const RDMRequest *request,
  366. uint8_t transaction_number) {
  367. rdm_command_message command_message;
  368. *response_code = VerifyData(data, length, &command_message);
  369. if (*response_code != RDM_COMPLETED_OK)
  370. return NULL;
  371. UID source_uid(command_message.source_uid);
  372. UID destination_uid(command_message.destination_uid);
  373. uint16_t sub_device = ((command_message.sub_device[0] << 8) +
  374. command_message.sub_device[1]);
  375. RDMCommandClass command_class = ConvertCommandClass(
  376. command_message.command_class);
  377. if (request) {
  378. // check dest uid
  379. if (request->SourceUID() != destination_uid) {
  380. OLA_WARN << "The destination UID in the response doesn't match, got " <<
  381. destination_uid << ", expected " << request->SourceUID();
  382. *response_code = RDM_DEST_UID_MISMATCH;
  383. return NULL;
  384. }
  385. // check src uid
  386. if (request->DestinationUID() != source_uid) {
  387. OLA_WARN << "The source UID in the response doesn't match, got " <<
  388. source_uid << ", expected " << request->DestinationUID();
  389. *response_code = RDM_SRC_UID_MISMATCH;
  390. return NULL;
  391. }
  392. // check transaction #
  393. if (command_message.transaction_number != transaction_number) {
  394. OLA_WARN << "Transaction numbers don't match, got " <<
  395. static_cast<int>(command_message.transaction_number) << ", expected "
  396. << static_cast<int>(transaction_number);
  397. *response_code = RDM_TRANSACTION_MISMATCH;
  398. return NULL;
  399. }
  400. // check subdevice, but ignore if request was for all sub devices or
  401. // QUEUED_MESSAGE
  402. if (sub_device != request->SubDevice() &&
  403. request->SubDevice() != ALL_RDM_SUBDEVICES &&
  404. request->ParamId() != PID_QUEUED_MESSAGE) {
  405. OLA_WARN << "Sub device didn't match, got " << sub_device <<
  406. ", expected " << request->SubDevice();
  407. *response_code = RDM_SUB_DEVICE_MISMATCH;
  408. return NULL;
  409. }
  410. // check command class
  411. if (request->CommandClass() == GET_COMMAND &&
  412. command_class != GET_COMMAND_RESPONSE &&
  413. request->ParamId() != PID_QUEUED_MESSAGE) {
  414. OLA_WARN << "Expected GET_COMMAND_RESPONSE, got 0x" << std::hex <<
  415. command_class;
  416. *response_code = RDM_COMMAND_CLASS_MISMATCH;
  417. return NULL;
  418. }
  419. if (request->CommandClass() == SET_COMMAND &&
  420. command_class != SET_COMMAND_RESPONSE) {
  421. OLA_WARN << "Expected SET_COMMAND_RESPONSE, got 0x" << std::hex <<
  422. command_class;
  423. *response_code = RDM_COMMAND_CLASS_MISMATCH;
  424. return NULL;
  425. }
  426. }
  427. // check response type
  428. if (command_message.port_id > ACK_OVERFLOW) {
  429. OLA_WARN << "Response type isn't valid, got " << command_message.port_id;
  430. *response_code = RDM_INVALID_RESPONSE_TYPE;
  431. return NULL;
  432. }
  433. uint16_t param_id = ((command_message.param_id[0] << 8) +
  434. command_message.param_id[1]);
  435. uint8_t return_transaction_number = (request ? transaction_number :
  436. command_message.transaction_number);
  437. switch (command_class) {
  438. case GET_COMMAND_RESPONSE:
  439. *response_code = RDM_COMPLETED_OK;
  440. return new RDMGetResponse(
  441. source_uid,
  442. destination_uid,
  443. return_transaction_number, // transaction #
  444. command_message.port_id, // port id
  445. command_message.message_count, // message count
  446. sub_device,
  447. param_id,
  448. data + sizeof(rdm_command_message),
  449. command_message.param_data_length); // data length
  450. case SET_COMMAND_RESPONSE:
  451. *response_code = RDM_COMPLETED_OK;
  452. return new RDMSetResponse(
  453. source_uid,
  454. destination_uid,
  455. return_transaction_number, // transaction #
  456. command_message.port_id, // port id
  457. command_message.message_count, // message count
  458. sub_device,
  459. param_id,
  460. data + sizeof(rdm_command_message),
  461. command_message.param_data_length); // data length
  462. default:
  463. OLA_WARN << "Command class isn't valid, got 0x" << std::hex <<
  464. command_class;
  465. *response_code = RDM_INVALID_COMMAND_CLASS;
  466. return NULL;
  467. }
  468. }
  469. /**
  470. * Inflate from some data
  471. */
  472. RDMResponse* RDMResponse::InflateFromData(const string &data,
  473. rdm_response_code *response_code,
  474. const RDMRequest *request) {
  475. return InflateFromData(reinterpret_cast<const uint8_t*>(data.data()),
  476. data.size(),
  477. response_code,
  478. request);
  479. }
  480. /**
  481. * Inflate from some data
  482. */
  483. RDMResponse* RDMResponse::InflateFromData(const string &data,
  484. rdm_response_code *response_code,
  485. const RDMRequest *request,
  486. uint8_t transaction_number) {
  487. return InflateFromData(reinterpret_cast<const uint8_t*>(data.data()),
  488. data.size(),
  489. response_code,
  490. request,
  491. transaction_number);
  492. }
  493. /**
  494. * This combines two RDMResponses into one. It's used to combine the data from
  495. * two responses in an ACK_OVERFLOW session together.
  496. * @param response1 the first response.
  497. * @param response1 the second response.
  498. * @return A new response with the data from the first and second combined or
  499. * NULL if the size limit is reached.
  500. */
  501. RDMResponse* RDMResponse::CombineResponses(const RDMResponse *response1,
  502. const RDMResponse *response2) {
  503. unsigned int combined_length = response1->ParamDataSize() +
  504. response2->ParamDataSize();
  505. // do some sort of checking
  506. if (combined_length > MAX_OVERFLOW_SIZE) {
  507. OLA_WARN << "ACK_OVERFLOW buffer size hit! Limit is " << MAX_OVERFLOW_SIZE
  508. << ", request size is " << combined_length;
  509. return NULL;
  510. } else if (response1->SourceUID() != response2->SourceUID()) {
  511. OLA_WARN << "Source UIDs don't match";
  512. return NULL;
  513. }
  514. uint8_t *combined_data = new uint8_t[combined_length];
  515. memcpy(combined_data, response1->ParamData(), response1->ParamDataSize());
  516. memcpy(combined_data + response1->ParamDataSize(),
  517. response2->ParamData(),
  518. response2->ParamDataSize());
  519. RDMResponse *response = NULL;
  520. if (response1->CommandClass() == GET_COMMAND_RESPONSE &&
  521. response2->CommandClass() == GET_COMMAND_RESPONSE) {
  522. response = new RDMGetResponse(
  523. response1->SourceUID(),
  524. response1->DestinationUID(),
  525. response1->TransactionNumber(),
  526. RDM_ACK,
  527. response2->MessageCount(),
  528. response1->SubDevice(),
  529. response1->ParamId(),
  530. combined_data,
  531. combined_length);
  532. } else if (response1->CommandClass() == SET_COMMAND_RESPONSE &&
  533. response2->CommandClass() == SET_COMMAND_RESPONSE) {
  534. response = new RDMSetResponse(
  535. response1->SourceUID(),
  536. response1->DestinationUID(),
  537. response1->TransactionNumber(),
  538. RDM_ACK,
  539. response2->MessageCount(),
  540. response1->SubDevice(),
  541. response1->ParamId(),
  542. combined_data,
  543. combined_length);
  544. } else {
  545. OLA_WARN << "Expected a RDM request command but got " <<
  546. std::hex << response1->CommandClass();
  547. }
  548. delete[] combined_data;
  549. return response;
  550. }
  551. // Helper functions follow
  552. /**
  553. * Guess the type of an RDM message, so we know whether we should unpack it as
  554. * a request or response. This doesn't perform any data checking (that's left
  555. * to the Inflate* methods).
  556. * @param type a pointer to a rdm_message_type variable which is set to
  557. * RDM_REQUEST or RDM_RESPONSE.
  558. * @param data a pointer to the rdm message (excluding the start code)
  559. * @param length length of the rdm data
  560. * @returns true if we could determine the type, false otherwise
  561. */
  562. bool GuessMessageType(rdm_message_type *type,
  563. const uint8_t *data,
  564. unsigned int length) {
  565. static const unsigned int COMMAND_CLASS_OFFSET = 19;
  566. if (!data || length < COMMAND_CLASS_OFFSET + 1)
  567. return false;
  568. uint8_t command_class = data[COMMAND_CLASS_OFFSET];
  569. if (command_class == RDMCommand::GET_COMMAND ||
  570. command_class == RDMCommand::SET_COMMAND) {
  571. *type = RDM_REQUEST;
  572. return true;
  573. } else if (command_class == RDMCommand::GET_COMMAND_RESPONSE ||
  574. command_class == RDMCommand::SET_COMMAND_RESPONSE) {
  575. *type = RDM_RESPONSE;
  576. return true;
  577. }
  578. return false;
  579. }
  580. /*
  581. * Generate a NACK response with a reason code
  582. */
  583. RDMResponse *NackWithReason(const RDMRequest *request,
  584. rdm_nack_reason reason_enum) {
  585. uint16_t reason = ola::network::HostToNetwork(static_cast<uint16_t>(
  586. reason_enum));
  587. if (request->CommandClass() == ola::rdm::RDMCommand::GET_COMMAND) {
  588. return new ola::rdm::RDMGetResponse(
  589. request->DestinationUID(),
  590. request->SourceUID(),
  591. request->TransactionNumber(),
  592. RDM_NACK_REASON,
  593. 0,
  594. request->SubDevice(),
  595. request->ParamId(),
  596. reinterpret_cast<uint8_t*>(&reason),
  597. sizeof(reason));
  598. } else {
  599. return new ola::rdm::RDMSetResponse(
  600. request->DestinationUID(),
  601. request->SourceUID(),
  602. request->TransactionNumber(),
  603. RDM_NACK_REASON,
  604. 0,
  605. request->SubDevice(),
  606. request->ParamId(),
  607. reinterpret_cast<uint8_t*>(&reason),
  608. sizeof(reason));
  609. }
  610. }
  611. /*
  612. * Generate a ACK Response with some data
  613. */
  614. RDMResponse *GetResponseFromData(const RDMRequest *request,
  615. const uint8_t *data,
  616. unsigned int length,
  617. rdm_response_type type,
  618. uint8_t outstanding_messages) {
  619. // we can reuse GetResponseWithPid
  620. return GetResponseWithPid(request,
  621. request->ParamId(),
  622. data,
  623. length,
  624. type,
  625. outstanding_messages);
  626. }
  627. /**
  628. * Generate a queued message response
  629. */
  630. RDMResponse *GetResponseWithPid(const RDMRequest *request,
  631. uint16_t pid,
  632. const uint8_t *data,
  633. unsigned int length,
  634. uint8_t type,
  635. uint8_t outstanding_messages) {
  636. if (request->CommandClass() == ola::rdm::RDMCommand::GET_COMMAND) {
  637. return new RDMGetResponse(
  638. request->DestinationUID(),
  639. request->SourceUID(),
  640. request->TransactionNumber(),
  641. type,
  642. outstanding_messages,
  643. request->SubDevice(),
  644. pid,
  645. data,
  646. length);
  647. } else {
  648. return new RDMSetResponse(
  649. request->DestinationUID(),
  650. request->SourceUID(),
  651. request->TransactionNumber(),
  652. type,
  653. outstanding_messages,
  654. request->SubDevice(),
  655. pid,
  656. data,
  657. length);
  658. }
  659. }
  660. /**
  661. * Inflate a discovery command
  662. */
  663. RDMDiscoveryCommand* RDMDiscoveryCommand::InflateFromData(
  664. const uint8_t *data,
  665. unsigned int length) {
  666. rdm_command_message command_message;
  667. rdm_response_code code = VerifyData(data, length, &command_message);
  668. if (code != RDM_COMPLETED_OK)
  669. return NULL;
  670. uint16_t sub_device = ((command_message.sub_device[0] << 8) +
  671. command_message.sub_device[1]);
  672. uint16_t param_id = ((command_message.param_id[0] << 8) +
  673. command_message.param_id[1]);
  674. RDMCommandClass command_class = ConvertCommandClass(
  675. command_message.command_class);
  676. if (command_class == DISCOVER_COMMAND) {
  677. return new RDMDiscoveryCommand(
  678. UID(command_message.source_uid),
  679. UID(command_message.destination_uid),
  680. command_message.transaction_number, // transaction #
  681. command_message.port_id, // port id
  682. command_message.message_count, // message count
  683. sub_device,
  684. param_id,
  685. data + sizeof(rdm_command_message),
  686. command_message.param_data_length); // data length
  687. } else {
  688. OLA_WARN << "Expected a RDM discovery command but got " << command_class;
  689. return NULL;
  690. }
  691. }
  692. /**
  693. * Inflate a discovery command from some data.
  694. */
  695. RDMDiscoveryCommand* RDMDiscoveryCommand::InflateFromData(const string &data) {
  696. return InflateFromData(reinterpret_cast<const uint8_t*>(data.data()),
  697. data.size());
  698. }
  699. /**
  700. * Constructor for the DiscoveryUniqueBranchRequest
  701. */
  702. DiscoveryUniqueBranchRequest::DiscoveryUniqueBranchRequest(
  703. const UID &source,
  704. const UID &lower,
  705. const UID &upper,
  706. uint8_t transaction_number,
  707. uint8_t port_id)
  708. : RDMDiscoveryCommand(source,
  709. UID::AllDevices(),
  710. transaction_number,
  711. port_id,
  712. 0, // message count
  713. ROOT_RDM_DEVICE,
  714. PID_DISC_UNIQUE_BRANCH,
  715. NULL,
  716. 0) {
  717. unsigned int length = sizeof(m_param_data);
  718. lower.Pack(m_param_data, length);
  719. upper.Pack(m_param_data + UID::UID_SIZE, length - UID::UID_SIZE);
  720. SetData(m_param_data, sizeof(m_param_data));
  721. }
  722. } // rdm
  723. } // ola