Typica is a free program for professional coffee roasters. https://typica.us
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

phidget22.w 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828
  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 void (CCONV *PhidgetValueCallback)(void *, void *, double);
  111. typedef int (CCONV *PhidgetPointerCallbackPointer)(void *,
  112. PhidgetManagerCallback,
  113. void *);
  114. typedef int (CCONV *PhidgetPointerVCPointer)(void *,
  115. PhidgetValueCallback,
  116. void *);
  117. typedef int (CCONV *PhidgetPointerIntIn)(void *, int);
  118. @ These are used to define function pointers that will be used to
  119. communicate with the library.
  120. @<Phidget22 function pointers@>=
  121. PhidgetPointer createManager;
  122. PhidgetPointerCallbackPointer setOnAttachManager;
  123. PhidgetPointerCallbackPointer setOnDetachManager;
  124. PhidgetPointer openManager;
  125. PhidgetPointerStringOut getDeviceName;
  126. PhidgetPointerIntOut getDeviceSerialNumber;
  127. PhidgetPointerIntOut getChannel;
  128. PhidgetPointerIntOut getChannelClass;
  129. PhidgetPointerIntOut getChannelSubclass;
  130. PhidgetPointerIntOut getHubPort;
  131. PhidgetPointer closeManager;
  132. PhidgetPointer deleteManager;
  133. @ These pointers must be initialized before they can be used.
  134. @<Initialize phidget22 function pointers@>=
  135. if((createManager = (PhidgetPointer) driver.resolve("PhidgetManager_create")) == 0 ||
  136. (setOnAttachManager = (PhidgetPointerCallbackPointer) driver.resolve("PhidgetManager_setOnAttachHandler")) == 0 ||
  137. (setOnDetachManager = (PhidgetPointerCallbackPointer) driver.resolve("PhidgetManager_setOnDetachHandler")) == 0 ||
  138. (openManager = (PhidgetPointer) driver.resolve("PhidgetManager_open")) == 0 ||
  139. (getDeviceName = (PhidgetPointerStringOut) driver.resolve("Phidget_getDeviceName")) == 0 ||
  140. (getDeviceSerialNumber = (PhidgetPointerIntOut) driver.resolve("Phidget_getDeviceSerialNumber")) == 0 ||
  141. (getChannel = (PhidgetPointerIntOut) driver.resolve("Phidget_getChannel")) == 0 ||
  142. (getChannelClass = (PhidgetPointerIntOut) driver.resolve("Phidget_getChannelClass")) == 0 ||
  143. (getChannelSubclass = (PhidgetPointerIntOut) driver.resolve("Phidget_getChannelSubclass")) == 0 ||
  144. (getHubPort = (PhidgetPointerIntOut) driver.resolve("Phidget_getHubPort")) == 0 ||
  145. (closeManager = (PhidgetPointer) driver.resolve("PhidgetManager_close")) == 0 ||
  146. (deleteManager = (PhidgetPointer) driver.resolve("PhidgetManager_delete")) == 0)
  147. {
  148. QMessageBox::critical(NULL, tr("Typica: Link error"),
  149. tr("Failed to link a required symbol in phidget22."));
  150. return;
  151. }
  152. @ The constuctor sets up a manager so that appropriate channels can be added to
  153. the combo box.
  154. @<Phidget implementation@>=
  155. PhidgetChannelSelector::PhidgetChannelSelector() : QComboBox(), manager(NULL)
  156. {
  157. #if __APPLE__
  158. driver.setFileName("Phidget22.framework/Phidget22");
  159. #else
  160. driver.setFileName("phidget22");
  161. #endif
  162. if(!driver.load())
  163. {
  164. QMessageBox::critical(NULL, tr("Typica: Driver not found"),
  165. tr("Failed to find phidget22. Please install it."));
  166. return;
  167. }
  168. @<Initialize phidget22 function pointers@>@;
  169. createManager(&manager);
  170. setOnAttachManager(manager, ChannelListAddChannel, this);
  171. setOnDetachManager(manager, ChannelListRemoveChannel, this);
  172. openManager(manager);
  173. }
  174. @ The callbacks registered in the constructor pass a pointer to the combo box
  175. so that contents can be updated from the relevant global functions.
  176. @<Additional functions@>=
  177. void CCONV ChannelListAddChannel(void *, void *context, void *device)
  178. {
  179. PhidgetChannelSelector *list =
  180. qobject_cast<PhidgetChannelSelector*>((QObject*)context);
  181. list->addChannel(device);
  182. }
  183. void CCONV ChannelListRemoveChannel(void *, void *context, void *device)
  184. {
  185. PhidgetChannelSelector *list =
  186. qobject_cast<PhidgetChannelSelector*>((QObject*)context);
  187. list->removeChannel(device);
  188. }
  189. @ These require function prototypes.
  190. @<Additional function prototypes@>=
  191. void CCONV ChannelListAddChannel(void *manager, void *context, void *device);
  192. void CCONV ChannelListRemoveChannel(void *manager, void *context,
  193. void *device);
  194. @ Adding and removing channels from the list happens in class where pointers
  195. to the required library functions are known.
  196. @<Phidget implementation@>=
  197. void PhidgetChannelSelector::addChannel(void *device)
  198. {
  199. char *deviceName;
  200. int deviceSerialNumber;
  201. int channel;
  202. int channelClass;
  203. int channelSubclass;
  204. int hubPort;
  205. getDeviceName(device, &deviceName);
  206. getDeviceSerialNumber(device, &deviceSerialNumber);
  207. getChannel(device, &channel);
  208. getChannelClass(device, &channelClass);
  209. getChannelSubclass(device, &channelSubclass);
  210. getHubPort(device, &hubPort);
  211. QMap<QString,QVariant> itemData;
  212. if(channelClass == 28) // Temperature sensor
  213. {
  214. itemData.insert("serialNumber", QString("%1").arg(deviceSerialNumber));
  215. itemData.insert("channel", QString("%1").arg(channel));
  216. itemData.insert("class", QString("%1").arg(channelClass));
  217. itemData.insert("subclass", QString("%1").arg(channelSubclass));
  218. itemData.insert("hubport", QString("%1").arg(hubPort));
  219. addItem(QString("%1: %2").arg(deviceName).arg(channel), QVariant(itemData));
  220. }
  221. }
  222. @ On removal, no attempt is made to match each call to the exact channel
  223. referenced. Rather, the assumption is that all channels on a device with a
  224. matching serial number should be removed at once and in the case of a
  225. multi-channel device getting removed, subsequent calls will just fail to match
  226. anything. The most common expected scenario is that only one device will be
  227. connected or not connected, so removing this results in an empty list for
  228. channels that were removed early.
  229. @<Phidget implementation@>=
  230. void PhidgetChannelSelector::removeChannel(void *device)
  231. {
  232. int deviceSerialNumber;
  233. getDeviceSerialNumber(device, &deviceSerialNumber);
  234. for(int i = count() - 1; i >= 0; i--)
  235. {
  236. QMap<QString,QVariant> data = itemData(i).toMap();
  237. if(data.value("serialNumber").toInt() == deviceSerialNumber)
  238. {
  239. removeItem(i);
  240. }
  241. }
  242. }
  243. @ A destructor closes and deletes the manager.
  244. @<Phidget implementation@>=
  245. PhidgetChannelSelector::~PhidgetChannelSelector()
  246. {
  247. if(manager != NULL)
  248. {
  249. closeManager(manager);
  250. deleteManager(&manager);
  251. }
  252. }
  253. @ Channel configuration provides a |PhidgetChannelSelector| for choosing
  254. among connected devices but also displays the relevant configuration data.
  255. @<Class declarations@>=
  256. class PhidgetChannelConfWidget : public BasicDeviceConfigurationWidget
  257. {
  258. Q_OBJECT
  259. public:
  260. Q_INVOKABLE PhidgetChannelConfWidget(DeviceTreeModel *model,
  261. const QModelIndex &index);
  262. public slots:
  263. void changeSelectedChannel(int index);
  264. void updateSerialNumber(const QString &value);
  265. void updateChannel(const QString &value);
  266. void updateHubPort(const QString &value);
  267. void updateColumnName(const QString &value);
  268. void updateChannelType(int value);
  269. void updateTCType(int value);
  270. void updateRTDType(int value);
  271. void updateRTDWiring(int value);
  272. void updateHidden(int value);
  273. private:
  274. PhidgetChannelSelector *channelSelector;
  275. QLineEdit *serialNumber;
  276. QLineEdit *channel;
  277. QLineEdit *hubPort;
  278. QComboBox *subtype;
  279. QStackedLayout *subtypeLayout;
  280. QComboBox *tctype;
  281. QComboBox *rtdtype;
  282. QComboBox *rtdwiring;
  283. };
  284. @ The constructor is responsible for setting up the interface.
  285. @<Phidget implementation@>=
  286. PhidgetChannelConfWidget::PhidgetChannelConfWidget(DeviceTreeModel *model,
  287. const QModelIndex &index)
  288. : BasicDeviceConfigurationWidget(model, index),
  289. channelSelector(new PhidgetChannelSelector),
  290. serialNumber(new QLineEdit),
  291. channel(new QLineEdit),
  292. hubPort(new QLineEdit),
  293. subtype(new QComboBox),
  294. subtypeLayout(new QStackedLayout),
  295. tctype(new QComboBox),
  296. rtdtype(new QComboBox),
  297. rtdwiring(new QComboBox)
  298. {
  299. QVBoxLayout *outerLayout = new QVBoxLayout;
  300. QFormLayout *layout = new QFormLayout;
  301. QLineEdit *columnName = new QLineEdit;
  302. subtype->addItem(tr("IC"), QVariant(1));
  303. subtype->addItem(tr("RTD"), QVariant(32));
  304. subtype->addItem(tr("Thermocouple"), QVariant(33));
  305. layout->addRow(tr("Channels:"), channelSelector);
  306. layout->addRow(tr("Column Name:"), columnName);
  307. QCheckBox *hidden = new QCheckBox(tr("Hide channel"));
  308. layout->addRow(hidden);
  309. layout->addRow(tr("Serial Number:"), serialNumber);
  310. layout->addRow(tr("Hub Port:"), hubPort);
  311. layout->addRow(tr("Channel Number:"), channel);
  312. layout->addRow(tr("Channel Type:"), subtype);
  313. serialNumber->setEnabled(false);
  314. channel->setEnabled(false);
  315. subtype->setEnabled(false);
  316. outerLayout->addLayout(layout);
  317. QWidget *icconfiguration = new QWidget;
  318. QWidget *rtdconfiguration = new QWidget;
  319. QFormLayout *rtdconfigurationLayout = new QFormLayout;
  320. rtdtype->addItem(tr("PT100 with .00385 curve"), QVariant(1));
  321. rtdtype->addItem(tr("PT1000 with .00385 curve"), QVariant(2));
  322. rtdtype->addItem(tr("PT100 with .00392 curve"), QVariant(3));
  323. rtdtype->addItem(tr("PT1000 with .00392 curve"), QVariant(4));
  324. rtdconfigurationLayout->addRow(tr("RTD type:"), rtdtype);
  325. rtdwiring->addItem(tr("2 wire"), QVariant(1));
  326. rtdwiring->addItem(tr("3 wire"), QVariant(2));
  327. rtdwiring->addItem(tr("4 wire"), QVariant(3));
  328. rtdconfigurationLayout->addRow(tr("RTD wiring:"), rtdwiring);
  329. rtdconfiguration->setLayout(rtdconfigurationLayout);
  330. QWidget *tcconfiguration = new QWidget;
  331. QFormLayout *tcconfigurationLayout = new QFormLayout;
  332. tctype->addItem(tr("Type J"), QVariant(1));
  333. tctype->addItem(tr("Type K"), QVariant(2));
  334. tctype->addItem(tr("Type E"), QVariant(3));
  335. tctype->addItem(tr("Type T"), QVariant(4));
  336. tcconfigurationLayout->addRow(tr("Thermocouple type:"), tctype);
  337. tcconfiguration->setLayout(tcconfigurationLayout);
  338. subtypeLayout->addWidget(icconfiguration);
  339. subtypeLayout->addWidget(rtdconfiguration);
  340. subtypeLayout->addWidget(tcconfiguration);
  341. @<Get device configuration data for current node@>@;
  342. for(int i = 0; i < configData.size(); i++)
  343. {
  344. node = configData.at(i).toElement();
  345. if(node.attribute("name") == "serialnumber")
  346. {
  347. serialNumber->setText(node.attribute("value"));
  348. }
  349. else if(node.attribute("name") == "channel")
  350. {
  351. channel->setText(node.attribute("value"));
  352. }
  353. else if(node.attribute("name") == "columnname")
  354. {
  355. columnName->setText(node.attribute("value"));
  356. }
  357. else if(node.attribute("name") == "channeltype")
  358. {
  359. subtype->setCurrentIndex(subtype->
  360. findData(QVariant(node.attribute("value").toInt())));
  361. subtypeLayout->setCurrentIndex(subtype->currentIndex());
  362. }
  363. else if(node.attribute("name") == "tctype")
  364. {
  365. tctype->setCurrentIndex(tctype->
  366. findData(QVariant(node.attribute("value").toInt())));
  367. }
  368. else if(node.attribute("name") == "rtdtype")
  369. {
  370. rtdtype->setCurrentIndex(rtdtype->
  371. findData(QVariant(node.attribute("value").toInt())));
  372. }
  373. else if(node.attribute("name") == "rtdwiring")
  374. {
  375. rtdwiring->setCurrentIndex(rtdwiring->
  376. findData(QVariant(node.attribute("value").toInt())));
  377. }
  378. else if(node.attribute("name") == "hubport")
  379. {
  380. hubPort->setText(node.attribute("value"));
  381. }
  382. else if(node.attribute("name") == "hidden")
  383. {
  384. hidden->setCheckState(node.attribute("value") == "true" ? Qt::Checked : Qt::Unchecked);
  385. }
  386. }
  387. outerLayout->addLayout(subtypeLayout);
  388. setLayout(outerLayout);
  389. connect(channelSelector, SIGNAL(currentIndexChanged(int)),
  390. this, SLOT(changeSelectedChannel(int)));
  391. connect(subtype, SIGNAL(currentIndexChanged(int)),
  392. subtypeLayout, SLOT(setCurrentIndex(int)));
  393. connect(serialNumber, SIGNAL(textChanged(QString)),
  394. this, SLOT(updateSerialNumber(QString)));
  395. connect(channel, SIGNAL(textChanged(QString)),
  396. this, SLOT(updateChannel(QString)));
  397. connect(columnName, SIGNAL(textEdited(QString)),
  398. this, SLOT(updateColumnName(QString)));
  399. connect(subtype, SIGNAL(currentIndexChanged(int)),
  400. this, SLOT(updateChannelType(int)));
  401. connect(tctype, SIGNAL(currentIndexChanged(int)),
  402. this, SLOT(updateTCType(int)));
  403. connect(rtdtype, SIGNAL(currentIndexChanged(int)),
  404. this, SLOT(updateRTDType(int)));
  405. connect(rtdwiring, SIGNAL(currentIndexChanged(int)),
  406. this, SLOT(updateRTDWiring(int)));
  407. connect(hubPort, SIGNAL(textChanged(QString)),
  408. this, SLOT(updateHubPort(QString)));
  409. connect(hidden, SIGNAL(stateChanged(int)),
  410. this, SLOT(updateHidden(int)));
  411. }
  412. @ The combo box provides a convenient way to populate required configuration
  413. fields with values that are not immediately obvious.
  414. @<Phidget implementation@>=
  415. void PhidgetChannelConfWidget::changeSelectedChannel(int index)
  416. {
  417. QMap<QString,QVariant> data = channelSelector->itemData(index).toMap();
  418. serialNumber->setText(data.value("serialNumber").toString());
  419. channel->setText(data.value("channel").toString());
  420. subtype->setCurrentIndex(subtype->
  421. findData(QVariant(data.value("subclass").toString().toInt())));
  422. hubPort->setText(data.value("hubport").toString());
  423. }
  424. @ Channel configuration settings are persisted as they are updated.
  425. @<Phidget implementation@>=
  426. void PhidgetChannelConfWidget::updateSerialNumber(const QString &value)
  427. {
  428. updateAttribute("serialnumber", value);
  429. }
  430. void PhidgetChannelConfWidget::updateChannel(const QString &value)
  431. {
  432. updateAttribute("channel", value);
  433. }
  434. void PhidgetChannelConfWidget::updateColumnName(const QString &value)
  435. {
  436. updateAttribute("columnname", value);
  437. }
  438. void PhidgetChannelConfWidget::updateChannelType(int value)
  439. {
  440. updateAttribute("channeltype", subtype->itemData(value).toString());
  441. }
  442. void PhidgetChannelConfWidget::updateTCType(int value)
  443. {
  444. updateAttribute("tctype", tctype->itemData(value).toString());
  445. }
  446. void PhidgetChannelConfWidget::updateRTDType(int value)
  447. {
  448. updateAttribute("rtdtype", rtdtype->itemData(value).toString());
  449. }
  450. void PhidgetChannelConfWidget::updateRTDWiring(int value)
  451. {
  452. updateAttribute("rtdwiring", rtdwiring->itemData(value).toString());
  453. }
  454. void PhidgetChannelConfWidget::updateHubPort(const QString &value)
  455. {
  456. updateAttribute("hubport", value);
  457. }
  458. void PhidgetChannelConfWidget::updateHidden(int value)
  459. {
  460. updateAttribute("hidden", value == 0 ? "false" : "true");
  461. }
  462. @ The hardware communnications code provides a single class that reads the
  463. saved configuration data, creates |Channel| objects for the logging view to
  464. connect various things to, and pushes data out on those channels. Internally,
  465. there is more variability in how these channels must be set up, so rather than
  466. just having a bunch of lists for the various properties, not all of which might
  467. be relevant, instead, the channel configuration data will all be kept in a
  468. structure.
  469. @<Class declarations@>=
  470. struct PhidgetChannelData
  471. {
  472. Channel *channel;
  473. QString columnName;
  474. QString indicatorLabel;
  475. int serialNumber;
  476. int channelNumber;
  477. int channelType;
  478. int hubPort;
  479. int tcType;
  480. int rtdType;
  481. int wiring;
  482. bool hidden;
  483. void *device;
  484. };
  485. @ The host environment requires a class that handles communication with the
  486. hardware. The public interface has been kept the same as the phidget21 code to
  487. minimize changes required in the configuration files.
  488. @<Class declarations@>=
  489. class Phidget22 : public QObject
  490. {
  491. Q_OBJECT
  492. public:
  493. Q_INVOKABLE Phidget22(const QModelIndex &deviceIndex);
  494. Q_INVOKABLE int channelCount();
  495. Channel* getChannel(int channel);
  496. Q_INVOKABLE bool isChannelHidden(int channel);
  497. Q_INVOKABLE QString channelColumnName(int channel);
  498. Q_INVOKABLE QString channelIndicatorText(int channel);
  499. public slots:
  500. void start();
  501. void stop();
  502. private:
  503. QList<PhidgetChannelData *> channelConfiguration;
  504. QLibrary driver;
  505. PhidgetPointer p_createTemperatureSensor;
  506. PhidgetPointerIntIn p_setSerialNumber;
  507. PhidgetPointerIntIn p_setChannelNumber;
  508. PhidgetPointerIntIn p_setHubPort;
  509. PhidgetPointerIntIn p_setTCType;
  510. PhidgetPointerIntIn p_setRTDType;
  511. PhidgetPointerIntIn p_setRTDWiring;
  512. PhidgetPointerVCPointer p_setNewDataCallback;
  513. PhidgetPointerIntIn p_open;
  514. PhidgetPointer p_close;
  515. PhidgetPointer p_delete;
  516. };
  517. @ The constructor reads the previously saved hardware configuration data and
  518. uses that to create the relevant channels. The channels are not initialized
  519. until the device is started.
  520. @<Phidget implementation@>=
  521. Phidget22::Phidget22(const QModelIndex &index) : QObject(NULL)
  522. {
  523. DeviceTreeModel *model = (DeviceTreeModel *)(index.model());
  524. if(model->hasChildren(index))
  525. {
  526. for(int i = 0; i < model->rowCount(index); i++)
  527. {
  528. QModelIndex channelIndex = model->index(i, 0, index);
  529. QDomElement channelReference = model->
  530. referenceElement(model->data(channelIndex, 32).toString());
  531. QDomElement channelReferenceElement = model->
  532. referenceElement(model->
  533. data(channelIndex, Qt::UserRole).toString());
  534. QDomNodeList channelConfigData =
  535. channelReferenceElement.elementsByTagName("attribute");
  536. PhidgetChannelData *c = new PhidgetChannelData;
  537. c->channel = new Channel;
  538. c->indicatorLabel =
  539. model->data(channelIndex, Qt::DisplayRole).toString();
  540. c->device = NULL;
  541. c->hubPort = -1;
  542. for(int j = 0; j < channelConfigData.size(); j++)
  543. {
  544. QDomElement node = channelConfigData.at(j).toElement();
  545. if(node.attribute("name") == "serialnumber")
  546. {
  547. c->serialNumber = node.attribute("value").toInt();
  548. }
  549. else if(node.attribute("name") == "channel")
  550. {
  551. c->channelNumber = node.attribute("value").toInt();
  552. }
  553. else if(node.attribute("name") == "channeltype")
  554. {
  555. c->channelType = node.attribute("value").toInt();
  556. }
  557. else if(node.attribute("name") == "tctype")
  558. {
  559. c->tcType = node.attribute("value").toInt();
  560. }
  561. else if(node.attribute("name") == "rtdtype")
  562. {
  563. c->rtdType = node.attribute("value").toInt();
  564. }
  565. else if(node.attribute("name") == "rtdwiring")
  566. {
  567. c->wiring = node.attribute("value").toInt();
  568. }
  569. else if(node.attribute("name") == "hidden")
  570. {
  571. c->hidden = (node.attribute("value") == "true");
  572. }
  573. else if(node.attribute("name") == "columnname")
  574. {
  575. c->columnName = node.attribute("value");
  576. }
  577. else if(node.attribute("name") == "hubport")
  578. {
  579. c->hubPort = node.attribute("value").toInt();
  580. }
  581. }
  582. channelConfiguration.append(c);
  583. }
  584. }
  585. }
  586. @ A bit of glue is needed to get the |Channel| objects out to the host
  587. environment.
  588. @<Phidget implementation@>=
  589. int Phidget22::channelCount()
  590. {
  591. return channelConfiguration.length();
  592. }
  593. Channel* Phidget22::getChannel(int channel)
  594. {
  595. return channelConfiguration.at(channel)->channel;
  596. }
  597. @ A little more glue allows the host environment to properly set up UI
  598. elements.
  599. @<Phidget implementation@>=
  600. bool Phidget22::isChannelHidden(int channel)
  601. {
  602. return channelConfiguration.at(channel)->hidden;
  603. }
  604. QString Phidget22::channelColumnName(int channel)
  605. {
  606. return channelConfiguration.at(channel)->columnName;
  607. }
  608. QString Phidget22::channelIndicatorText(int channel)
  609. {
  610. return channelConfiguration.at(channel)->indicatorLabel;
  611. }
  612. @ Once the hardware configuration has been read and the UI has been set up, we
  613. can start talking to the hardware and start getting measurements.
  614. @<Phidget implementation@>=
  615. void Phidget22::start()
  616. {
  617. #if __APPLE__
  618. driver.setFileName("Phidget22.framework/Phidget22");
  619. #else
  620. driver.setFileName("phidget22");
  621. #endif
  622. if(!driver.load())
  623. {
  624. QMessageBox::critical(NULL, tr("Typica: Driver not found"),
  625. tr("Failed to find phidget22. Please install it."));
  626. return;
  627. }
  628. if((p_createTemperatureSensor = (PhidgetPointer)driver.resolve("PhidgetTemperatureSensor_create")) == 0 ||
  629. (p_setSerialNumber = (PhidgetPointerIntIn)driver.resolve("Phidget_setDeviceSerialNumber")) == 0 ||
  630. (p_setChannelNumber = (PhidgetPointerIntIn)driver.resolve("Phidget_setChannel")) == 0 ||
  631. (p_setTCType = (PhidgetPointerIntIn)driver.resolve("PhidgetTemperatureSensor_setThermocoupleType")) == 0 ||
  632. (p_setRTDType = (PhidgetPointerIntIn)driver.resolve("PhidgetTemperatureSensor_setRTDType")) == 0 ||
  633. (p_setRTDWiring = (PhidgetPointerIntIn)driver.resolve("PhidgetTemperatureSensor_setRTDWireSetup")) == 0 ||
  634. (p_setNewDataCallback = (PhidgetPointerVCPointer)driver.resolve("PhidgetTemperatureSensor_setOnTemperatureChangeHandler")) == 0 ||
  635. (p_open = (PhidgetPointerIntIn)driver.resolve("Phidget_openWaitForAttachment")) == 0 ||
  636. (p_close = (PhidgetPointer)driver.resolve("Phidget_close")) == 0 ||
  637. (p_delete = (PhidgetPointer)driver.resolve("PhidgetTemperatureSensor_delete")) == 0 ||
  638. (p_setHubPort = (PhidgetPointerIntIn)driver.resolve("Phidget_setHubPort")) == 0)
  639. {
  640. QMessageBox::critical(NULL, tr("Typica: Link error"),
  641. tr("Failed to link a required symbol in phidget22."));
  642. return;
  643. }
  644. for(int i = 0; i < channelConfiguration.length(); i++)
  645. {
  646. PhidgetChannelData *c = channelConfiguration.at(i);
  647. p_createTemperatureSensor(&(c->device));
  648. p_setSerialNumber(c->device, c->serialNumber);
  649. p_setChannelNumber(c->device, c->channelNumber);
  650. switch(c->channelType)
  651. {
  652. case 32:
  653. p_setRTDType(c->device, c->rtdType);
  654. p_setRTDWiring(c->device, c->wiring);
  655. break;
  656. case 33:
  657. p_setTCType(c->device, c->tcType);
  658. break;
  659. default:
  660. break;
  661. }
  662. if(c->hubPort >= 0)
  663. {
  664. p_setHubPort(c->device, c->hubPort);
  665. }
  666. p_setNewDataCallback(c->device, Phidget22ValueCallback, c->channel);
  667. p_open(c->device, 5000);
  668. }
  669. }
  670. @ New values are delivered to a callback function outside of the class, but
  671. with a pointer to the relevant |Channel| object. This means that all the
  672. callback needs to do is perform the unit conversion, assemble the |Measurement|
  673. and send that out.
  674. Unfortunately, there can be no guarantee that new measurements will be
  675. available on all channels simultaneously. Hopefully this will not be too
  676. problematic.
  677. @<Additional functions@>=
  678. void CCONV Phidget22ValueCallback(void *, void *context, double value)
  679. {
  680. Channel *channel = (Channel*)context;
  681. QTime time = QTime::currentTime();
  682. Measurement measure(value * 9.0 / 5.0 + 32.0, time);
  683. channel->input(measure);
  684. }
  685. @ A function prototype is provided.
  686. @<Additional function prototypes@>=
  687. void CCONV Phidget22ValueCallback(void *device, void *context, double value);
  688. @ When the logging window is closed, it is important to close all open channels
  689. and delete their handles.
  690. @<Phidget implementation@>=
  691. void Phidget22::stop()
  692. {
  693. for(int i = 0; i < channelConfiguration.length(); i++)
  694. {
  695. PhidgetChannelData *c = channelConfiguration.at(i);
  696. p_close(c->device);
  697. p_delete(&(c->device));
  698. }
  699. }
  700. @ Class implementations are currently folded into typica.cpp.
  701. @<Class implementations@>=
  702. @<Phidget implementation@>@;
  703. @ The hardware communications class needs to be available from the host
  704. environment.
  705. @<Set up the scripting engine@>=
  706. constructor = engine->newFunction(constructPhidget22);
  707. value = engine->newQMetaObject(&Phidget22::staticMetaObject, constructor);
  708. engine->globalObject().setProperty("Phidget22", value);
  709. @ Two function prototypes are needed.
  710. @<Function prototypes for scripting@>=
  711. QScriptValue constructPhidget22(QScriptContext *context, QScriptEngine *engine);
  712. QScriptValue Phidget22_getChannel(QScriptContext *context, QScriptEngine *engine);
  713. @ The constructor is trivial.
  714. @<Functions for scripting@>=
  715. QScriptValue constructPhidget22(QScriptContext *context, QScriptEngine *engine)
  716. {
  717. if(context->argumentCount() != 1)
  718. {
  719. context->throwError("Incorrect number of arguments");
  720. }
  721. QScriptValue object = engine->newQObject(new Phidget22(argument<QModelIndex>(0, context)), QScriptEngine::ScriptOwnership);
  722. setQObjectProperties(object, engine);
  723. object.setProperty("getChannel", engine->newFunction(Phidget22_getChannel));
  724. return object;
  725. }
  726. @ A wrapper is used for getting channels.
  727. @<Functions for scripting@>=
  728. QScriptValue Phidget22_getChannel(QScriptContext *context, QScriptEngine *engine)
  729. {
  730. Phidget22 *self = getself<Phidget22 *>(context);
  731. QScriptValue object;
  732. if(self)
  733. {
  734. object = engine->newQObject(self->getChannel(argument<int>(0, context)));
  735. setChannelProperties(object, engine);
  736. }
  737. return object;
  738. }