Typica is a free program for professional coffee roasters. https://typica.us
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. @** Phidget22 library.
  2. \noindent Around the same time as the \pn{} 1.8.0 release, Phidgets released a
  3. new hardware communications library with no regard for compatibility with
  4. existing software and poor communication around that fact. They did, however,
  5. provide hardware specimen for testing that requires the new library.
  6. API differences are significant enough that it makes more sense to write new
  7. code for interacting with phidget22 than attempting to retrofit existing
  8. phidget21 code. By leaving both in, there is no configuration disription for
  9. people already using hardware previously supported and it is possible to use
  10. both libraries simultaneously to communicate with different hardware.
  11. The option to configure devices with this library should only be shown if the
  12. library is installed. The library name is different on a Mac.
  13. @<Register top level device configuration nodes@>=
  14. #if __APPLE__
  15. QLibrary phidget22check("Phidget22.framework/Phidget22");
  16. #else
  17. QLibrary phidget22check("phidget22");
  18. #endif
  19. if(phidget22check.load())
  20. {
  21. inserter = new NodeInserter(tr("Phidget22"), tr("Phidget22"), "phidget22",
  22. NULL);
  23. topLevelNodeInserters.append(inserter);
  24. phidget22check.unload();
  25. }
  26. @ A top level configuration is used to group channels using this library while
  27. child nodes provide channel configuration. The configuration widgets need to be
  28. registered so they can be instantiated as appropriate.
  29. @<Register device configuration widgets@>=
  30. app.registerDeviceConfigurationWidget("phidget22",
  31. PhidgetConfWidget::staticMetaObject);
  32. app.registerDeviceConfigurationWidget("phidgetchannel",
  33. PhidgetChannelConfWidget::staticMetaObject);
  34. @ The first configuration widget just serves as a parent to all channels using
  35. this library. There does not seem to be a need for the configuration to mirror
  36. how the hardware is connected, so this serves as a parent node for any number
  37. of devices connected either directly or through a hub.
  38. @<Class declarations@>=
  39. class PhidgetConfWidget : public BasicDeviceConfigurationWidget
  40. {
  41. Q_OBJECT
  42. public:
  43. Q_INVOKABLE PhidgetConfWidget(DeviceTreeModel *model,
  44. const QModelIndex &index);
  45. private slots:
  46. void addChannel();
  47. };
  48. @ The only thing this configuration widget provides is a way to create child
  49. nodes.
  50. @<Phidget implementation@>=
  51. PhidgetConfWidget::PhidgetConfWidget(DeviceTreeModel *model,
  52. const QModelIndex &index)
  53. : BasicDeviceConfigurationWidget(model, index)
  54. {
  55. QHBoxLayout *layout = new QHBoxLayout;
  56. QPushButton *addChannelButton = new QPushButton(tr("Add Channel"));
  57. connect(addChannelButton, SIGNAL(clicked()), this, SLOT(addChannel()));
  58. layout->addWidget(addChannelButton);
  59. setLayout(layout);
  60. }
  61. void PhidgetConfWidget::addChannel()
  62. {
  63. insertChildNode(tr("Channel"), "phidgetchannel");
  64. }
  65. @ For this library, \pn{} supports a broader range of hardware. This requires
  66. slightly more involved hardware configuration to ensure that a given channel
  67. configuration consistently refers to the same sensor.
  68. Channels will be initialized with a device serial number, a channel number, and
  69. other channel specific configuration options as applicable. These other
  70. configuration options depend on the sensor type associated with the channel. A
  71. thermocouple requires different configuration options than an RTD while the
  72. built in ambient temperature sensors on some devices do not require additional
  73. configuration.
  74. At present, only temperature sensors are supported, however this code could be
  75. extended to support other options.
  76. To simplify configuration, a combo box is provided which displays all of the
  77. currently connected channels that \pn{} supports and allows a configuration
  78. widget to obtain relevant channel information when the desired channel is
  79. selected.
  80. @<Class declarations@>=
  81. class PhidgetChannelSelector : public QComboBox
  82. {
  83. Q_OBJECT
  84. public:
  85. PhidgetChannelSelector();
  86. ~PhidgetChannelSelector();
  87. void addChannel(void *device);
  88. void removeChannel(void *device);
  89. private:
  90. QLibrary driver;
  91. void *manager;
  92. @<Phidget22 function pointers@>@;
  93. };
  94. @ At this point, it becomes necessary to call functions in the library. To avoid
  95. a dependency on phidget22.h some function pointer types are created.
  96. @<Additional type definitions@>=
  97. #if defined(__stdcall)
  98. #define CCONV __stdcall
  99. #else
  100. #if defined(_MSC_VER)
  101. #define CCONV __stdcall
  102. #else
  103. #define CCONV
  104. #endif
  105. #endif
  106. typedef int (CCONV *PhidgetPointer)(void *);
  107. typedef int (CCONV *PhidgetPointerStringOut)(void *, char **);
  108. typedef int (CCONV *PhidgetPointerIntOut)(void *, int *);
  109. typedef void (CCONV *PhidgetManagerCallback)(void *, void *, void *);
  110. typedef int (CCONV *PhidgetPointerCallbackPointer)(void *,
  111. PhidgetManagerCallback,
  112. void *);
  113. @ These are used to define function pointers that will be used to
  114. communicate with the library.
  115. @<Phidget22 function pointers@>=
  116. PhidgetPointer createManager;
  117. PhidgetPointerCallbackPointer setOnAttachManager;
  118. PhidgetPointerCallbackPointer setOnDetachManager;
  119. PhidgetPointer openManager;
  120. PhidgetPointerStringOut getDeviceName;
  121. PhidgetPointerIntOut getDeviceSerialNumber;
  122. PhidgetPointerIntOut getChannel;
  123. PhidgetPointerIntOut getChannelClass;
  124. PhidgetPointerIntOut getChannelSubclass;
  125. PhidgetPointer closeManager;
  126. PhidgetPointer deleteManager;
  127. @ These pointers must be initialized before they can be used.
  128. @<Initialize phidget22 function pointers@>=
  129. if((createManager = (PhidgetPointer) driver.resolve("PhidgetManager_create")) == 0 ||
  130. (setOnAttachManager = (PhidgetPointerCallbackPointer) driver.resolve("PhidgetManager_setOnAttachHandler")) == 0 ||
  131. (setOnDetachManager = (PhidgetPointerCallbackPointer) driver.resolve("PhidgetManager_setOnDetachHandler")) == 0 ||
  132. (openManager = (PhidgetPointer) driver.resolve("PhidgetManager_open")) == 0 ||
  133. (getDeviceName = (PhidgetPointerStringOut) driver.resolve("Phidget_getDeviceName")) == 0 ||
  134. (getDeviceSerialNumber = (PhidgetPointerIntOut) driver.resolve("Phidget_getDeviceSerialNumber")) == 0 ||
  135. (getChannel = (PhidgetPointerIntOut) driver.resolve("Phidget_getChannel")) == 0 ||
  136. (getChannelClass = (PhidgetPointerIntOut) driver.resolve("Phidget_getChannelClass")) == 0 ||
  137. (getChannelSubclass = (PhidgetPointerIntOut) driver.resolve("Phidget_getChannelSubclass")) == 0 ||
  138. (closeManager = (PhidgetPointer) driver.resolve("PhidgetManager_close")) == 0 ||
  139. (deleteManager = (PhidgetPointer) driver.resolve("PhidgetManager_delete")) == 0)
  140. {
  141. QMessageBox::critical(NULL, tr("Typica: Link error"),
  142. tr("Failed to link a required symbol in phidget22."));
  143. return;
  144. }
  145. @ The constuctor sets up a manager so that appropriate channels can be added to
  146. the combo box.
  147. @<Phidget implementation@>=
  148. PhidgetChannelSelector::PhidgetChannelSelector() : QComboBox(), manager(NULL)
  149. {
  150. #if __APPLE__
  151. driver.setFileName("Phidget22.framework/Phidget22");
  152. #else
  153. driver.setFileName("phidget22");
  154. #endif
  155. if(!driver.load())
  156. {
  157. QMessageBox::critical(NULL, tr("Typica: Driver not found"),
  158. tr("Failed to find phidget22. Please install it."));
  159. return;
  160. }
  161. @<Initialize phidget22 function pointers@>@;
  162. createManager(&manager);
  163. setOnAttachManager(manager, ChannelListAddChannel, this);
  164. setOnDetachManager(manager, ChannelListRemoveChannel, this);
  165. openManager(manager);
  166. }
  167. @ The callbacks registered in the constructor pass a pointer to the combo box
  168. so that contents can be updated from the relevant global functions.
  169. @<Additional functions@>=
  170. void CCONV ChannelListAddChannel(void *, void *context, void *device)
  171. {
  172. PhidgetChannelSelector *list =
  173. qobject_cast<PhidgetChannelSelector*>((QObject*)context);
  174. list->addChannel(device);
  175. }
  176. void CCONV ChannelListRemoveChannel(void *, void *context, void *device)
  177. {
  178. PhidgetChannelSelector *list =
  179. qobject_cast<PhidgetChannelSelector*>((QObject*)context);
  180. list->removeChannel(device);
  181. }
  182. @ These require function prototypes.
  183. @<Additional function prototypes@>=
  184. void CCONV ChannelListAddChannel(void *manager, void *context, void *device);
  185. void CCONV ChannelListRemoveChannel(void *manager, void *context,
  186. void *device);
  187. @ Adding and removing channels from the list happens in class where pointers
  188. to the required library functions are known.
  189. @<Phidget implementation@>=
  190. void PhidgetChannelSelector::addChannel(void *device)
  191. {
  192. char *deviceName;
  193. int deviceSerialNumber;
  194. int channel;
  195. int channelClass;
  196. int channelSubclass;
  197. getDeviceName(device, &deviceName);
  198. getDeviceSerialNumber(device, &deviceSerialNumber);
  199. getChannel(device, &channel);
  200. getChannelClass(device, &channelClass);
  201. getChannelSubclass(device, &channelSubclass);
  202. QMap<QString,QVariant> itemData;
  203. if(channelClass == 28) // Temperature sensor
  204. {
  205. itemData.insert("serialNumber", QString("%1").arg(deviceSerialNumber));
  206. itemData.insert("channel", QString("%1").arg(channel));
  207. itemData.insert("class", QString("%1").arg(channelClass));
  208. itemData.insert("subclass", QString("%1").arg(channelSubclass));
  209. addItem(QString("%1: %2").arg(deviceName).arg(channel), QVariant(itemData));
  210. }
  211. }
  212. @ On removal, no attempt is made to match each call to the exact channel
  213. referenced. Rather, the assumption is that all channels on a device with a
  214. matching serial number should be removed at once and in the case of a
  215. multi-channel device getting removed, subsequent calls will just fail to match
  216. anything. The most common expected scenario is that only one device will be
  217. connected or not connected, so removing this results in an empty list for
  218. channels that were removed early.
  219. @<Phidget implementation@>=
  220. void PhidgetChannelSelector::removeChannel(void *device)
  221. {
  222. int deviceSerialNumber;
  223. getDeviceSerialNumber(device, &deviceSerialNumber);
  224. for(int i = count() - 1; i >= 0; i--)
  225. {
  226. QMap<QString,QVariant> data = itemData(i).toMap();
  227. if(data.value("serialNumber").toInt() == deviceSerialNumber)
  228. {
  229. removeItem(i);
  230. }
  231. }
  232. }
  233. @ A destructor closes and deletes the manager.
  234. @<Phidget implementation@>=
  235. PhidgetChannelSelector::~PhidgetChannelSelector()
  236. {
  237. if(manager != NULL)
  238. {
  239. closeManager(manager);
  240. deleteManager(&manager);
  241. }
  242. }
  243. @ Channel configuration provides a |PhidgetChannelSelector| for choosing
  244. among connected devices but also displays the relevant configuration data.
  245. @<Class declarations@>=
  246. class PhidgetChannelConfWidget : public BasicDeviceConfigurationWidget
  247. {
  248. Q_OBJECT
  249. public:
  250. Q_INVOKABLE PhidgetChannelConfWidget(DeviceTreeModel *model,
  251. const QModelIndex &index);
  252. public slots:
  253. void changeSelectedChannel(int index);
  254. void updateSerialNumber(const QString &value);
  255. void updateChannel(const QString &value);
  256. void updateColumnName(const QString &value);
  257. void updateChannelType(int value);
  258. void updateTCType(int value);
  259. void updateRTDType(int value);
  260. void updateRTDWiring(int value);
  261. private:
  262. PhidgetChannelSelector *channelSelector;
  263. QLineEdit *serialNumber;
  264. QLineEdit *channel;
  265. QComboBox *subtype;
  266. QStackedLayout *subtypeLayout;
  267. QComboBox *tctype;
  268. QComboBox *rtdtype;
  269. QComboBox *rtdwiring;
  270. };
  271. @ The constructor is responsible for setting up the interface.
  272. @<Phidget implementation@>=
  273. PhidgetChannelConfWidget::PhidgetChannelConfWidget(DeviceTreeModel *model,
  274. const QModelIndex &index)
  275. : BasicDeviceConfigurationWidget(model, index),
  276. channelSelector(new PhidgetChannelSelector),
  277. serialNumber(new QLineEdit),
  278. channel(new QLineEdit),
  279. subtype(new QComboBox),
  280. subtypeLayout(new QStackedLayout),
  281. tctype(new QComboBox),
  282. rtdtype(new QComboBox),
  283. rtdwiring(new QComboBox)
  284. {
  285. QVBoxLayout *outerLayout = new QVBoxLayout;
  286. QFormLayout *layout = new QFormLayout;
  287. QLineEdit *columnName = new QLineEdit;
  288. subtype->addItem(tr("IC"), QVariant(1));
  289. subtype->addItem(tr("RTD"), QVariant(32));
  290. subtype->addItem(tr("Thermocouple"), QVariant(33));
  291. layout->addRow(tr("Channels:"), channelSelector);
  292. layout->addRow(tr("Column Name:"), columnName);
  293. layout->addRow(tr("Serial Number:"), serialNumber);
  294. layout->addRow(tr("Channel Number:"), channel);
  295. layout->addRow(tr("Channel Type:"), subtype);
  296. serialNumber->setEnabled(false);
  297. channel->setEnabled(false);
  298. subtype->setEnabled(false);
  299. outerLayout->addLayout(layout);
  300. QWidget *icconfiguration = new QWidget;
  301. QWidget *rtdconfiguration = new QWidget;
  302. QFormLayout *rtdconfigurationLayout = new QFormLayout;
  303. rtdtype->addItem(tr("PT100 with .00385 curve"), QVariant(1));
  304. rtdtype->addItem(tr("PT1000 with .00385 curve"), QVariant(2));
  305. rtdtype->addItem(tr("PT100 with .00392 curve"), QVariant(3));
  306. rtdtype->addItem(tr("PT1000 with .00392 curve"), QVariant(4));
  307. rtdconfigurationLayout->addRow(tr("RTD type:"), rtdtype);
  308. rtdwiring->addItem(tr("2 wire"), QVariant(1));
  309. rtdwiring->addItem(tr("3 wire"), QVariant(2));
  310. rtdwiring->addItem(tr("4 wire"), QVariant(3));
  311. rtdconfigurationLayout->addRow(tr("RTD wiring:"), rtdwiring);
  312. rtdconfiguration->setLayout(rtdconfigurationLayout);
  313. QWidget *tcconfiguration = new QWidget;
  314. QFormLayout *tcconfigurationLayout = new QFormLayout;
  315. tctype->addItem(tr("Type J"), QVariant(1));
  316. tctype->addItem(tr("Type K"), QVariant(2));
  317. tctype->addItem(tr("Type E"), QVariant(3));
  318. tctype->addItem(tr("Type T"), QVariant(4));
  319. tcconfigurationLayout->addRow(tr("Thermocouple type:"), tctype);
  320. tcconfiguration->setLayout(tcconfigurationLayout);
  321. subtypeLayout->addWidget(icconfiguration);
  322. subtypeLayout->addWidget(rtdconfiguration);
  323. subtypeLayout->addWidget(tcconfiguration);
  324. @<Get device configuration data for current node@>@;
  325. for(int i = 0; i < configData.size(); i++)
  326. {
  327. node = configData.at(i).toElement();
  328. if(node.attribute("name") == "serialnumber")
  329. {
  330. serialNumber->setText(node.attribute("value"));
  331. }
  332. else if(node.attribute("name") == "channel")
  333. {
  334. channel->setText(node.attribute("value"));
  335. }
  336. else if(node.attribute("name") == "columnname")
  337. {
  338. columnName->setText(node.attribute("value"));
  339. }
  340. else if(node.attribute("name") == "channeltype")
  341. {
  342. subtype->setCurrentIndex(subtype->
  343. findData(QVariant(node.attribute("value").toInt())));
  344. subtypeLayout->setCurrentIndex(subtype->currentIndex());
  345. }
  346. else if(node.attribute("name") == "tctype")
  347. {
  348. tctype->setCurrentIndex(tctype->
  349. findData(QVariant(node.attribute("value").toInt())));
  350. }
  351. else if(node.attribute("name") == "rtdtype")
  352. {
  353. rtdtype->setCurrentIndex(rtdtype->
  354. findData(QVariant(node.attribute("value").toInt())));
  355. }
  356. else if(node.attribute("name") == "rtdwiring")
  357. {
  358. rtdwiring->setCurrentIndex(rtdwiring->
  359. findData(QVariant(node.attribute("value").toInt())));
  360. }
  361. }
  362. outerLayout->addLayout(subtypeLayout);
  363. setLayout(outerLayout);
  364. connect(channelSelector, SIGNAL(currentIndexChanged(int)),
  365. this, SLOT(changeSelectedChannel(int)));
  366. connect(subtype, SIGNAL(currentIndexChanged(int)),
  367. subtypeLayout, SLOT(setCurrentIndex(int)));
  368. connect(serialNumber, SIGNAL(textChanged(QString)),
  369. this, SLOT(updateSerialNumber(QString)));
  370. connect(channel, SIGNAL(textChanged(QString)),
  371. this, SLOT(updateChannel(QString)));
  372. connect(columnName, SIGNAL(textEdited(QString)),
  373. this, SLOT(updateColumnName(QString)));
  374. connect(subtype, SIGNAL(currentIndexChanged(int)),
  375. this, SLOT(updateChannelType(int)));
  376. connect(tctype, SIGNAL(currentIndexChanged(int)),
  377. this, SLOT(updateTCType(int)));
  378. connect(rtdtype, SIGNAL(currentIndexChanged(int)),
  379. this, SLOT(updateRTDType(int)));
  380. connect(rtdwiring, SIGNAL(currentIndexChanged(int)),
  381. this, SLOT(updateRTDWiring(int)));
  382. }
  383. @ The combo box provides a convenient way to populate required configuration
  384. fields with values that are not immediately obvious.
  385. @<Phidget implementation@>=
  386. void PhidgetChannelConfWidget::changeSelectedChannel(int index)
  387. {
  388. QMap<QString,QVariant> data = channelSelector->itemData(index).toMap();
  389. serialNumber->setText(data.value("serialNumber").toString());
  390. channel->setText(data.value("channel").toString());
  391. subtype->setCurrentIndex(subtype->
  392. findData(QVariant(data.value("subclass").toString().toInt())));
  393. }
  394. @ Channel configuration settings are persisted as they are updated.
  395. @<Phidget implementation@>=
  396. void PhidgetChannelConfWidget::updateSerialNumber(const QString &value)
  397. {
  398. updateAttribute("serialnumber", value);
  399. }
  400. void PhidgetChannelConfWidget::updateChannel(const QString &value)
  401. {
  402. updateAttribute("channel", value);
  403. }
  404. void PhidgetChannelConfWidget::updateColumnName(const QString &value)
  405. {
  406. updateAttribute("columnname", value);
  407. }
  408. void PhidgetChannelConfWidget::updateChannelType(int value)
  409. {
  410. updateAttribute("channeltype", subtype->itemData(value).toString());
  411. }
  412. void PhidgetChannelConfWidget::updateTCType(int value)
  413. {
  414. updateAttribute("tctype", tctype->itemData(value).toString());
  415. }
  416. void PhidgetChannelConfWidget::updateRTDType(int value)
  417. {
  418. updateAttribute("rtdtype", rtdtype->itemData(value).toString());
  419. }
  420. void PhidgetChannelConfWidget::updateRTDWiring(int value)
  421. {
  422. updateAttribute("rtdwiring", rtdwiring->itemData(value).toString());
  423. }
  424. @ Class implementations are currently folded into typica.cpp.
  425. @<Class implementations@>=
  426. @<Phidget implementation@>@;