PageRenderTime 22ms CodeModel.GetById 42ms RepoModel.GetById 62ms app.codeStats 0ms

/common/rdm/PidStoreLoader.cpp

https://code.google.com/
C++ | 488 lines | 362 code | 64 blank | 62 comment | 54 complexity | 40b12db6741bc290d7cb829fbc0284b0 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. * PidStore.cpp
  17. * The PidStore and Pid classes.
  18. * Copyright (C) 2011 Simon Newton
  19. */
  20. #include <errno.h>
  21. #include <google/protobuf/io/zero_copy_stream_impl.h>
  22. #include <google/protobuf/text_format.h>
  23. #include <fstream>
  24. #include <set>
  25. #include <string>
  26. #include <vector>
  27. #include "common/rdm/DescriptorConsistencyChecker.h"
  28. #include "common/rdm/PidStoreLoader.h"
  29. #include "common/rdm/Pids.pb.h"
  30. #include "ola/Logging.h"
  31. #include "ola/rdm/PidStore.h"
  32. #include "ola/rdm/RDMEnums.h"
  33. namespace ola {
  34. namespace rdm {
  35. using ola::messaging::FieldDescriptor;
  36. using std::map;
  37. using std::set;
  38. using std::vector;
  39. /**
  40. * Load Pid information from a file.
  41. * @param file the path to the file to load
  42. * @param validate set to true if we should perform validation of the contents.
  43. * @returns A pointer to a new RootPidStore or NULL if loading failed.
  44. */
  45. const RootPidStore *PidStoreLoader::LoadFromFile(const string &file,
  46. bool validate) {
  47. std::ifstream proto_file(file.data());
  48. if (!proto_file.is_open()) {
  49. OLA_WARN << "Missing " << file << ": " << strerror(errno);
  50. return NULL;
  51. }
  52. const RootPidStore *store = LoadFromStream(&proto_file, validate);
  53. proto_file.close();
  54. return store;
  55. }
  56. /**
  57. * Load Pid information from a string
  58. * @param file the path to the file to load
  59. * @param validate set to true if we should perform validation of the contents.
  60. * @returns A pointer to a new RootPidStore or NULL if loading failed.
  61. */
  62. const RootPidStore *PidStoreLoader::LoadFromStream(std::istream *data,
  63. bool validate) {
  64. ola::rdm::pid::PidStore pid_store_pb;
  65. google::protobuf::io::IstreamInputStream input_stream(data);
  66. bool ok = google::protobuf::TextFormat::Parse(&input_stream, &pid_store_pb);
  67. if (!ok)
  68. return NULL;
  69. return BuildStore(pid_store_pb, validate);
  70. }
  71. /**
  72. * Build the root store from a protocol buffer.
  73. */
  74. const RootPidStore *PidStoreLoader::BuildStore(
  75. const ola::rdm::pid::PidStore &store_pb,
  76. bool validate) {
  77. RootPidStore::ManufacturerMap manufacturer_map;
  78. set<uint16_t> seen_values;
  79. set<string> seen_names;
  80. vector<const PidDescriptor*> esta_pids;
  81. if (!GetPidList(&esta_pids, store_pb, validate, true)) {
  82. return NULL;
  83. }
  84. bool ok = true;
  85. for (int i = 0; i < store_pb.manufacturer_size(); ++i) {
  86. const ola::rdm::pid::Manufacturer &manufacturer = store_pb.manufacturer(i);
  87. RootPidStore::ManufacturerMap::const_iterator iter = manufacturer_map.find(
  88. manufacturer.manufacturer_id());
  89. if (iter != manufacturer_map.end()) {
  90. OLA_WARN << "Manufacturer id " << manufacturer.manufacturer_id() <<
  91. "(" << manufacturer.manufacturer_name() <<
  92. ") listed more than once in the pids file";
  93. ok = false;
  94. break;
  95. }
  96. vector<const PidDescriptor*> pids;
  97. if (!GetPidList(&pids, manufacturer, validate, false)) {
  98. ok = false;
  99. break;
  100. }
  101. manufacturer_map[manufacturer.manufacturer_id()] = new PidStore(
  102. pids);
  103. }
  104. if (!ok) {
  105. RootPidStore::ManufacturerMap::iterator iter = manufacturer_map.begin();
  106. for (; iter != manufacturer_map.end(); ++iter)
  107. delete iter->second;
  108. return NULL;
  109. }
  110. OLA_DEBUG << "Load Complete";
  111. const PidStore *esta_store = new PidStore(esta_pids);
  112. return new RootPidStore(esta_store,
  113. manufacturer_map,
  114. store_pb.version());
  115. }
  116. /**
  117. * Get a list of pids from a protobuf object
  118. */
  119. template <typename pb_object>
  120. bool PidStoreLoader::GetPidList(vector<const PidDescriptor*> *pids,
  121. const pb_object &store,
  122. bool validate,
  123. bool limit_pid_values) {
  124. set<uint16_t> seen_values;
  125. set<string> seen_names;
  126. bool ok = true;
  127. for (int i = 0; i < store.pid_size(); ++i) {
  128. const ola::rdm::pid::Pid &pid = store.pid(i);
  129. OLA_DEBUG << "Loading " << pid.name();
  130. if (validate) {
  131. set<uint16_t>::const_iterator value_iter = seen_values.find(pid.value());
  132. if (value_iter != seen_values.end()) {
  133. OLA_WARN << "Pid " << pid.value() << " exists multiple times in the "
  134. " pid file";
  135. ok = false;
  136. break;
  137. }
  138. seen_values.insert(pid.value());
  139. set<string>::const_iterator name_iter = seen_names.find(pid.name());
  140. if (name_iter != seen_names.end()) {
  141. OLA_WARN << "Pid " << pid.name() << " exists multiple times in the "
  142. " pid file";
  143. ok = false;
  144. break;
  145. }
  146. seen_names.insert(pid.name());
  147. if (limit_pid_values && pid.value() > 0x8000 and pid.value() < 0xffe0) {
  148. OLA_WARN << "ESTA Pid " << pid.name() << " (" << pid.value() << ")" <<
  149. " is outside acceptable range";
  150. ok = false;
  151. break;
  152. }
  153. }
  154. const PidDescriptor *descriptor = PidToDescriptor(pid, validate);
  155. if (!descriptor) {
  156. ok = false;
  157. break;
  158. }
  159. pids->push_back(descriptor);
  160. }
  161. if (!ok) {
  162. vector<const PidDescriptor*>::iterator iter = pids->begin();
  163. for (; iter != pids->end(); ++iter) {
  164. delete *iter;
  165. }
  166. return false;
  167. }
  168. return true;
  169. }
  170. /**
  171. * Build a PidDescriptor from a Pid protobuf object
  172. */
  173. PidDescriptor *PidStoreLoader::PidToDescriptor(const ola::rdm::pid::Pid &pid,
  174. bool validate) {
  175. // populate sub device validators
  176. PidDescriptor::sub_device_valiator get_validator =
  177. PidDescriptor::ANY_SUB_DEVICE;
  178. if (pid.has_get_sub_device_range())
  179. get_validator = ConvertSubDeviceValidator(pid.get_sub_device_range());
  180. PidDescriptor::sub_device_valiator set_validator =
  181. PidDescriptor::ANY_SUB_DEVICE;
  182. if (pid.has_set_sub_device_range())
  183. set_validator = ConvertSubDeviceValidator(pid.set_sub_device_range());
  184. // yuck, code smell. This should use protobuf reflections instead.
  185. const Descriptor *get_request = NULL;
  186. if (pid.has_get_request()) {
  187. get_request = FrameFormatToDescriptor(pid.get_request(), validate);
  188. if (!get_request)
  189. return NULL;
  190. }
  191. const Descriptor *get_response = NULL;
  192. if (pid.has_get_response()) {
  193. get_response = FrameFormatToDescriptor(pid.get_response(), validate);
  194. if (!get_response) {
  195. delete get_request;
  196. return NULL;
  197. }
  198. }
  199. const Descriptor *set_request = NULL;
  200. if (pid.has_set_request()) {
  201. set_request = FrameFormatToDescriptor(pid.set_request(), validate);
  202. if (!set_request) {
  203. delete get_request;
  204. delete get_response;
  205. return NULL;
  206. }
  207. }
  208. const Descriptor *set_response = NULL;
  209. if (pid.has_set_response()) {
  210. set_response = FrameFormatToDescriptor(pid.set_response(), validate);
  211. if (!set_response) {
  212. delete get_request;
  213. delete get_response;
  214. delete set_request;
  215. return NULL;
  216. }
  217. }
  218. PidDescriptor *descriptor = new PidDescriptor(
  219. pid.name(),
  220. pid.value(),
  221. get_request,
  222. get_response,
  223. set_request,
  224. set_response,
  225. get_validator,
  226. set_validator);
  227. return descriptor;
  228. }
  229. /**
  230. * Convert a protobuf frame format to a Descriptor object
  231. */
  232. const Descriptor* PidStoreLoader::FrameFormatToDescriptor(
  233. const ola::rdm::pid::FrameFormat &format,
  234. bool validate) {
  235. bool ok = true;
  236. vector<const FieldDescriptor*> fields;
  237. for (int i = 0; i < format.field_size(); ++i) {
  238. const FieldDescriptor *field = FieldToFieldDescriptor(format.field(i));
  239. if (!field) {
  240. ok = false;
  241. break;
  242. }
  243. fields.push_back(field);
  244. }
  245. if (!ok) {
  246. vector<const FieldDescriptor*>::iterator iter = fields.begin();
  247. for (; iter != fields.end(); ++iter) {
  248. delete *iter;
  249. }
  250. return NULL;
  251. }
  252. // we don't give these descriptors names
  253. const Descriptor *descriptor = new Descriptor("", fields);
  254. if (validate) {
  255. if (!m_checker.CheckConsistency(descriptor)) {
  256. OLA_WARN << "Frame format failed consistency check!";
  257. delete descriptor;
  258. return NULL;
  259. }
  260. }
  261. return descriptor;
  262. }
  263. /**
  264. * Convert a protobuf field object to a FieldDescriptor.
  265. */
  266. const FieldDescriptor *PidStoreLoader::FieldToFieldDescriptor(
  267. const ola::rdm::pid::Field &field) {
  268. const FieldDescriptor *descriptor = NULL;
  269. switch (field.type()) {
  270. case ola::rdm::pid::BOOL:
  271. descriptor = new ola::messaging::BoolFieldDescriptor(field.name());
  272. break;
  273. case ola::rdm::pid::UINT8:
  274. descriptor =
  275. IntegerFieldToFieldDescriptor<ola::messaging::UInt8FieldDescriptor>(
  276. field);
  277. break;
  278. case ola::rdm::pid::UINT16:
  279. descriptor =
  280. IntegerFieldToFieldDescriptor<ola::messaging::UInt16FieldDescriptor>(
  281. field);
  282. break;
  283. case ola::rdm::pid::UINT32:
  284. descriptor =
  285. IntegerFieldToFieldDescriptor<ola::messaging::UInt32FieldDescriptor>(
  286. field);
  287. break;
  288. case ola::rdm::pid::INT8:
  289. descriptor =
  290. IntegerFieldToFieldDescriptor<ola::messaging::Int8FieldDescriptor>(
  291. field);
  292. break;
  293. case ola::rdm::pid::INT16:
  294. descriptor =
  295. IntegerFieldToFieldDescriptor<ola::messaging::Int16FieldDescriptor>(
  296. field);
  297. break;
  298. case ola::rdm::pid::INT32:
  299. descriptor =
  300. IntegerFieldToFieldDescriptor<ola::messaging::Int32FieldDescriptor>(
  301. field);
  302. break;
  303. case ola::rdm::pid::STRING:
  304. descriptor = StringFieldToFieldDescriptor(field);
  305. break;
  306. case ola::rdm::pid::GROUP:
  307. descriptor = GroupFieldToFieldDescriptor(field);
  308. break;
  309. case ola::rdm::pid::IPV4:
  310. descriptor = new ola::messaging::IPV4FieldDescriptor(field.name());
  311. break;
  312. default:
  313. OLA_WARN << "Unknown field type: " << field.type();
  314. }
  315. return descriptor;
  316. }
  317. /**
  318. * Convert a integer protobuf field to a FieldDescriptor.
  319. */
  320. template <typename descriptor_class>
  321. const FieldDescriptor *PidStoreLoader::IntegerFieldToFieldDescriptor(
  322. const ola::rdm::pid::Field &field) {
  323. typename descriptor_class::IntervalVector intervals;
  324. typename descriptor_class::LabeledValues labels;
  325. for (int i = 0; i < field.range_size(); ++i) {
  326. const ola::rdm::pid::Range &range_value = field.range(i);
  327. typename descriptor_class::Interval interval(range_value.min(),
  328. range_value.max());
  329. intervals.push_back(interval);
  330. }
  331. // if not intervals were specified, we automatically add all the labels
  332. bool intervals_empty = intervals.empty();
  333. for (int i = 0; i < field.label_size(); ++i) {
  334. const ola::rdm::pid::LabeledValue &labeled_value = field.label(i);
  335. labels[labeled_value.label()] = labeled_value.value();
  336. if (intervals_empty) {
  337. typename descriptor_class::Interval interval(labeled_value.value(),
  338. labeled_value.value());
  339. intervals.push_back(interval);
  340. }
  341. }
  342. int8_t multipler = 0;
  343. if (field.has_multiplier())
  344. multipler = field.multiplier();
  345. return new descriptor_class(
  346. field.name(),
  347. intervals,
  348. labels,
  349. false,
  350. multipler);
  351. }
  352. /**
  353. * Convert a string protobuf field to a FieldDescriptor.
  354. */
  355. const FieldDescriptor *PidStoreLoader::StringFieldToFieldDescriptor(
  356. const ola::rdm::pid::Field &field) {
  357. uint8_t min = 0;
  358. if (field.has_min_size())
  359. min = field.min_size();
  360. if (!field.has_max_size()) {
  361. OLA_WARN << "String field failed to specify max size";
  362. return NULL;
  363. }
  364. return new ola::messaging::StringFieldDescriptor(
  365. field.name(),
  366. min,
  367. field.max_size());
  368. }
  369. /**
  370. * Convert a group protobuf field to a FieldDescriptor.
  371. */
  372. const FieldDescriptor *PidStoreLoader::GroupFieldToFieldDescriptor(
  373. const ola::rdm::pid::Field &field) {
  374. vector<const class FieldDescriptor*> fields;
  375. bool ok = true;
  376. uint16_t min = 0;
  377. int16_t max = ola::messaging::FieldDescriptorGroup::UNLIMITED_BLOCKS;
  378. if (field.has_min_size())
  379. min = field.min_size();
  380. if (field.has_max_size())
  381. max = field.max_size();
  382. for (int i = 0; i < field.field_size(); ++i) {
  383. const FieldDescriptor *descriptor = FieldToFieldDescriptor(field.field(i));
  384. if (!descriptor) {
  385. ok = false;
  386. break;
  387. }
  388. fields.push_back(descriptor);
  389. }
  390. if (!ok) {
  391. vector<const class FieldDescriptor*>::iterator iter = fields.begin();
  392. for (; iter != fields.end(); ++iter) {
  393. delete *iter;
  394. }
  395. return NULL;
  396. }
  397. return new ola::messaging::FieldDescriptorGroup(
  398. field.name(),
  399. fields,
  400. min,
  401. max);
  402. }
  403. /**
  404. * Convert a protobuf sub device enum to a PidDescriptor one.
  405. */
  406. PidDescriptor::sub_device_valiator PidStoreLoader::ConvertSubDeviceValidator(
  407. const ola::rdm::pid::SubDeviceRange &sub_device_range) {
  408. switch (sub_device_range) {
  409. case ola::rdm::pid::ROOT_DEVICE:
  410. return PidDescriptor::ROOT_DEVICE;
  411. case ola::rdm::pid::ROOT_OR_ALL_SUBDEVICE:
  412. return PidDescriptor::ANY_SUB_DEVICE;
  413. case ola::rdm::pid::ROOT_OR_SUBDEVICE:
  414. return PidDescriptor::NON_BROADCAST_SUB_DEVICE;
  415. case ola::rdm::pid::ONLY_SUBDEVICES:
  416. return PidDescriptor::SPECIFIC_SUB_DEVICE;
  417. default:
  418. OLA_WARN << "Unknown sub device validator: " << sub_device_range <<
  419. ", defaulting to all";
  420. return PidDescriptor::ANY_SUB_DEVICE;
  421. }
  422. }
  423. } // rdm
  424. } // ola