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.

phidget22.w 42KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208
  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 disruption 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. app.registerDeviceConfigurationWidget("phidgetcurrentchannel",
  35. PhidgetCurrentChannelConfWidget::staticMetaObject);
  36. @ The first configuration widget just serves as a parent to all channels using
  37. this library. There does not seem to be a need for the configuration to mirror
  38. how the hardware is connected, so this serves as a parent node for any number
  39. of devices connected either directly or through a hub.
  40. @<Class declarations@>=
  41. class PhidgetConfWidget : public BasicDeviceConfigurationWidget
  42. {
  43. Q_OBJECT
  44. public:
  45. Q_INVOKABLE PhidgetConfWidget(DeviceTreeModel *model,
  46. const QModelIndex &index);
  47. };
  48. @ The only thing this configuration widget provides is a way to create child
  49. nodes.
  50. Originally, this only supported channels that use the TemperatureInput API.
  51. With the addition of support for other input types, the decision was made to
  52. give each channel type its own node type and configuration widget rather than
  53. attempt to cram every configuration option for all supported types into the
  54. same configuration control.
  55. @<Phidget implementation@>=
  56. PhidgetConfWidget::PhidgetConfWidget(DeviceTreeModel *model,
  57. const QModelIndex &index)
  58. : BasicDeviceConfigurationWidget(model, index)
  59. {
  60. QHBoxLayout *layout = new QHBoxLayout;
  61. QPushButton *addChannelButton = new QPushButton(tr("Add Channel"));
  62. QMenu *channelTypeMenu = new QMenu;
  63. NodeInserter *temperatureChannel =
  64. new NodeInserter(tr("Temperature Channel"),
  65. tr("Temperature Channel"), "phidgetchannel");
  66. connect(temperatureChannel, SIGNAL(triggered(QString, QString)),
  67. this, SLOT(insertChildNode(QString, QString)));
  68. channelTypeMenu->addAction(temperatureChannel);
  69. NodeInserter *currentChannel =
  70. new NodeInserter(tr("Current Channel"),
  71. tr("Current Channel"), "phidgetcurrentchannel");
  72. connect(currentChannel, SIGNAL(triggered(QString, QString)),
  73. this, SLOT(insertChildNode(QString, QString)));
  74. channelTypeMenu->addAction(currentChannel);
  75. addChannelButton->setMenu(channelTypeMenu);
  76. layout->addWidget(addChannelButton);
  77. setLayout(layout);
  78. }
  79. @ For this library, \pn{} supports a broader range of hardware. This requires
  80. slightly more involved hardware configuration to ensure that a given channel
  81. configuration consistently refers to the same sensor.
  82. Channels will be initialized with a device serial number, a channel number, and
  83. other channel specific configuration options as applicable. These other
  84. configuration options depend on the sensor type associated with the channel. A
  85. thermocouple requires different configuration options than an RTD while the
  86. built in ambient temperature sensors on some devices do not require additional
  87. configuration.
  88. To simplify configuration, a combo box is provided which displays all of the
  89. currently connected channels that \pn{} supports and allows a configuration
  90. widget to obtain relevant channel information when the desired channel is
  91. selected.
  92. By passing an optional channel type into the constructor, this will only
  93. display channels matching the specified type. Some potentially interesting
  94. channel types incude:
  95. \medskip
  96. \settabs 4 \columns
  97. \+&2&Current Input\cr
  98. \+&5&Digital Input\cr
  99. \+&6&Digital Output\cr
  100. \+&28&Temperature Input\cr
  101. \+&29&Voltage Input\cr
  102. \+&30&Voltage Output\cr
  103. \+&38&Current Output\cr
  104. \smallskip
  105. \centerline{Table \secno: A Selection of Phidget Channel Types}
  106. \medskip
  107. @<Class declarations@>=
  108. class PhidgetChannelSelector : public QComboBox
  109. {
  110. Q_OBJECT
  111. public:@/
  112. PhidgetChannelSelector(int channeltype = 0);
  113. ~PhidgetChannelSelector();
  114. void addChannel(void *device);
  115. void removeChannel(void *device);
  116. private:@/
  117. int typefilter;
  118. QLibrary driver;
  119. void *manager;
  120. @<Phidget22 function pointers@>@;
  121. };
  122. @ At this point, it becomes necessary to call functions in the library. To avoid
  123. a dependency on phidget22.h some function pointer types are created.
  124. @<Additional type definitions@>=
  125. #if defined(__stdcall)
  126. #define CCONV __stdcall
  127. #else
  128. #if defined(_MSC_VER)
  129. #define CCONV __stdcall
  130. #else
  131. #define CCONV
  132. #endif
  133. #endif
  134. typedef int (CCONV *PhidgetPointer)(void *);
  135. typedef int (CCONV *PhidgetPointerStringOut)(void *, char **);
  136. typedef int (CCONV *PhidgetPointerIntOut)(void *, int *);
  137. typedef void (CCONV *PhidgetManagerCallback)(void *, void *, void *);
  138. typedef void (CCONV *PhidgetValueCallback)(void *, void *, double);
  139. typedef void (CCONV *PhidgetErrorCallback)(void *, void *, int, const char *);
  140. typedef int (CCONV *PhidgetPointerCallbackPointer)(void *,
  141. PhidgetManagerCallback,
  142. void *);
  143. typedef int (CCONV *PhidgetPointerVCPointer)(void *,
  144. PhidgetValueCallback,
  145. void *);
  146. typedef int (CCONV *PhidgetPointerIntIn)(void *, int);
  147. typedef int (CCONV *PhidgetPointerECPointer)(void *, PhidgetErrorCallback,
  148. void *);
  149. @ These are used to define function pointers that will be used to
  150. communicate with the library.
  151. @<Phidget22 function pointers@>=
  152. PhidgetPointer createManager;
  153. PhidgetPointerCallbackPointer setOnAttachManager;
  154. PhidgetPointerCallbackPointer setOnDetachManager;
  155. PhidgetPointer openManager;
  156. PhidgetPointerStringOut getDeviceName;
  157. PhidgetPointerIntOut getDeviceSerialNumber;
  158. PhidgetPointerIntOut getChannel;
  159. PhidgetPointerIntOut getChannelClass;
  160. PhidgetPointerIntOut getChannelSubclass;
  161. PhidgetPointerIntOut getHubPort;
  162. PhidgetPointer closeManager;
  163. PhidgetPointer deleteManager;
  164. @ These pointers must be initialized before they can be used.
  165. @<Initialize phidget22 function pointers@>=
  166. if((createManager = (PhidgetPointer) driver.resolve("PhidgetManager_create")) == 0 ||
  167. (setOnAttachManager = (PhidgetPointerCallbackPointer) driver.resolve("PhidgetManager_setOnAttachHandler")) == 0 ||
  168. (setOnDetachManager = (PhidgetPointerCallbackPointer) driver.resolve("PhidgetManager_setOnDetachHandler")) == 0 ||
  169. (openManager = (PhidgetPointer) driver.resolve("PhidgetManager_open")) == 0 ||
  170. (getDeviceName = (PhidgetPointerStringOut) driver.resolve("Phidget_getDeviceName")) == 0 ||
  171. (getDeviceSerialNumber = (PhidgetPointerIntOut) driver.resolve("Phidget_getDeviceSerialNumber")) == 0 ||
  172. (getChannel = (PhidgetPointerIntOut) driver.resolve("Phidget_getChannel")) == 0 ||
  173. (getChannelClass = (PhidgetPointerIntOut) driver.resolve("Phidget_getChannelClass")) == 0 ||
  174. (getChannelSubclass = (PhidgetPointerIntOut) driver.resolve("Phidget_getChannelSubclass")) == 0 ||
  175. (getHubPort = (PhidgetPointerIntOut) driver.resolve("Phidget_getHubPort")) == 0 ||
  176. (closeManager = (PhidgetPointer) driver.resolve("PhidgetManager_close")) == 0 ||
  177. (deleteManager = (PhidgetPointer) driver.resolve("PhidgetManager_delete")) == 0)
  178. {
  179. QMessageBox::critical(NULL, tr("Typica: Link error"),
  180. tr("Failed to link a required symbol in phidget22."));
  181. return;
  182. }
  183. @ The constuctor sets up a manager so that appropriate channels can be added to
  184. the combo box.
  185. @<Phidget implementation@>=
  186. PhidgetChannelSelector::PhidgetChannelSelector(int channeltype) :
  187. QComboBox(), typefilter(channeltype), manager(NULL)
  188. {
  189. #if __APPLE__
  190. driver.setFileName("Phidget22.framework/Phidget22");
  191. #else
  192. driver.setFileName("phidget22");
  193. #endif
  194. if(!driver.load())
  195. {
  196. QMessageBox::critical(NULL, tr("Typica: Driver not found"),
  197. tr("Failed to find phidget22. Please install it."));
  198. return;
  199. }
  200. @<Initialize phidget22 function pointers@>@;
  201. createManager(&manager);
  202. setOnAttachManager(manager, ChannelListAddChannel, this);
  203. setOnDetachManager(manager, ChannelListRemoveChannel, this);
  204. openManager(manager);
  205. }
  206. @ The callbacks registered in the constructor pass a pointer to the combo box
  207. so that contents can be updated from the relevant global functions.
  208. @<Additional functions@>=
  209. void CCONV ChannelListAddChannel(void *, void *context, void *device)
  210. {
  211. PhidgetChannelSelector *list =
  212. qobject_cast<PhidgetChannelSelector*>((QObject*)context);
  213. list->addChannel(device);
  214. }
  215. void CCONV ChannelListRemoveChannel(void *, void *context, void *device)
  216. {
  217. PhidgetChannelSelector *list =
  218. qobject_cast<PhidgetChannelSelector*>((QObject*)context);
  219. list->removeChannel(device);
  220. }
  221. @ These require function prototypes.
  222. @<Additional function prototypes@>=
  223. void CCONV ChannelListAddChannel(void *manager, void *context, void *device);
  224. void CCONV ChannelListRemoveChannel(void *manager, void *context,
  225. void *device);
  226. @ Adding and removing channels from the list happens in class where pointers
  227. to the required library functions are known.
  228. @<Phidget implementation@>=
  229. void PhidgetChannelSelector::addChannel(void *device)
  230. {
  231. char *deviceName;
  232. int deviceSerialNumber;
  233. int channel;
  234. int channelClass;
  235. int channelSubclass;
  236. int hubPort;
  237. getDeviceName(device, &deviceName);
  238. getDeviceSerialNumber(device, &deviceSerialNumber);
  239. getChannel(device, &channel);
  240. getChannelClass(device, &channelClass);
  241. getChannelSubclass(device, &channelSubclass);
  242. getHubPort(device, &hubPort);
  243. QMap<QString,QVariant> itemData;
  244. if(typefilter != 0 && channelClass == typefilter)
  245. {
  246. itemData.insert("serialNumber", QString("%1").arg(deviceSerialNumber));
  247. itemData.insert("channel", QString("%1").arg(channel));
  248. itemData.insert("class", QString("%1").arg(channelClass));
  249. itemData.insert("subclass",
  250. QString("%1").arg(channelSubclass));
  251. itemData.insert("hubport", QString("%1").arg(hubPort));
  252. addItem(QString("%1: %2").arg(deviceName).arg(channel),
  253. QVariant(itemData));
  254. }
  255. }
  256. @ On removal, no attempt is made to match each call to the exact channel
  257. referenced. Rather, the assumption is that all channels on a device with a
  258. matching serial number should be removed at once and in the case of a
  259. multi-channel device getting removed, subsequent calls will just fail to match
  260. anything. The most common expected scenario is that only one device will be
  261. connected or not connected, so removing this results in an empty list for
  262. channels that were removed early.
  263. @<Phidget implementation@>=
  264. void PhidgetChannelSelector::removeChannel(void *device)
  265. {
  266. int deviceSerialNumber;
  267. getDeviceSerialNumber(device, &deviceSerialNumber);
  268. for(int i = count() - 1; i >= 0; i--)
  269. {
  270. QMap<QString,QVariant> data = itemData(i).toMap();
  271. if(data.value("serialNumber").toInt() == deviceSerialNumber)
  272. {
  273. removeItem(i);
  274. }
  275. }
  276. }
  277. @ A destructor closes and deletes the manager.
  278. @<Phidget implementation@>=
  279. PhidgetChannelSelector::~PhidgetChannelSelector()
  280. {
  281. if(manager != NULL)
  282. {
  283. closeManager(manager);
  284. deleteManager(&manager);
  285. }
  286. }
  287. @ Channel configuration provides a |PhidgetChannelSelector| for choosing
  288. among connected devices but also displays the relevant configuration data.
  289. This class only deals with temperature channels as that was the only channel
  290. type originally supported. Other configuration classes should be used for
  291. other channel types to allow type specific configuration options to be
  292. presented sensibly.
  293. @<Class declarations@>=
  294. class PhidgetChannelConfWidget : public BasicDeviceConfigurationWidget
  295. {
  296. Q_OBJECT
  297. public:
  298. Q_INVOKABLE PhidgetChannelConfWidget(DeviceTreeModel *model,
  299. const QModelIndex &index);
  300. public slots:
  301. void changeSelectedChannel(int index);
  302. void updateSerialNumber(const QString &value);
  303. void updateChannel(const QString &value);
  304. void updateHubPort(const QString &value);
  305. void updateColumnName(const QString &value);
  306. void updateChannelType(int value);
  307. void updateTCType(int value);
  308. void updateRTDType(int value);
  309. void updateRTDWiring(int value);
  310. void updateHidden(int value);
  311. private:
  312. PhidgetChannelSelector *channelSelector;
  313. QLineEdit *serialNumber;
  314. QLineEdit *channel;
  315. QLineEdit *hubPort;
  316. QComboBox *subtype;
  317. QStackedLayout *subtypeLayout;
  318. QComboBox *tctype;
  319. QComboBox *rtdtype;
  320. QComboBox *rtdwiring;
  321. };
  322. @ The constructor is responsible for setting up the interface.
  323. @<Phidget implementation@>=
  324. PhidgetChannelConfWidget::PhidgetChannelConfWidget(DeviceTreeModel *model,
  325. const QModelIndex &index)
  326. : BasicDeviceConfigurationWidget(model, index),
  327. channelSelector(new PhidgetChannelSelector(28)),
  328. serialNumber(new QLineEdit),
  329. channel(new QLineEdit),
  330. hubPort(new QLineEdit),
  331. subtype(new QComboBox),
  332. subtypeLayout(new QStackedLayout),
  333. tctype(new QComboBox),
  334. rtdtype(new QComboBox),
  335. rtdwiring(new QComboBox)
  336. {
  337. QVBoxLayout *outerLayout = new QVBoxLayout;
  338. QFormLayout *layout = new QFormLayout;
  339. QLineEdit *columnName = new QLineEdit;
  340. subtype->addItem(tr("IC"), QVariant(1));
  341. subtype->addItem(tr("RTD"), QVariant(32));
  342. subtype->addItem(tr("Thermocouple"), QVariant(33));
  343. layout->addRow(tr("Channels:"), channelSelector);
  344. layout->addRow(tr("Column Name:"), columnName);
  345. QCheckBox *hidden = new QCheckBox(tr("Hide channel"));
  346. layout->addRow(hidden);
  347. layout->addRow(tr("Serial Number:"), serialNumber);
  348. layout->addRow(tr("Hub Port:"), hubPort);
  349. layout->addRow(tr("Channel Number:"), channel);
  350. layout->addRow(tr("Channel Type:"), subtype);
  351. serialNumber->setEnabled(false);
  352. channel->setEnabled(false);
  353. subtype->setEnabled(false);
  354. outerLayout->addLayout(layout);
  355. QWidget *icconfiguration = new QWidget;
  356. QWidget *rtdconfiguration = new QWidget;
  357. QFormLayout *rtdconfigurationLayout = new QFormLayout;
  358. rtdtype->addItem(tr("PT100 with .00385 curve"), QVariant(1));
  359. rtdtype->addItem(tr("PT1000 with .00385 curve"), QVariant(2));
  360. rtdtype->addItem(tr("PT100 with .00392 curve"), QVariant(3));
  361. rtdtype->addItem(tr("PT1000 with .00392 curve"), QVariant(4));
  362. rtdconfigurationLayout->addRow(tr("RTD type:"), rtdtype);
  363. rtdwiring->addItem(tr("2 wire"), QVariant(1));
  364. rtdwiring->addItem(tr("3 wire"), QVariant(2));
  365. rtdwiring->addItem(tr("4 wire"), QVariant(3));
  366. rtdconfigurationLayout->addRow(tr("RTD wiring:"), rtdwiring);
  367. rtdconfiguration->setLayout(rtdconfigurationLayout);
  368. QWidget *tcconfiguration = new QWidget;
  369. QFormLayout *tcconfigurationLayout = new QFormLayout;
  370. tctype->addItem(tr("Type J"), QVariant(1));
  371. tctype->addItem(tr("Type K"), QVariant(2));
  372. tctype->addItem(tr("Type E"), QVariant(3));
  373. tctype->addItem(tr("Type T"), QVariant(4));
  374. tcconfigurationLayout->addRow(tr("Thermocouple type:"), tctype);
  375. tcconfiguration->setLayout(tcconfigurationLayout);
  376. subtypeLayout->addWidget(icconfiguration);
  377. subtypeLayout->addWidget(rtdconfiguration);
  378. subtypeLayout->addWidget(tcconfiguration);
  379. @<Get device configuration data for current node@>@;
  380. for(int i = 0; i < configData.size(); i++)
  381. {
  382. node = configData.at(i).toElement();
  383. if(node.attribute("name") == "serialnumber")
  384. {
  385. serialNumber->setText(node.attribute("value"));
  386. }
  387. else if(node.attribute("name") == "channel")
  388. {
  389. channel->setText(node.attribute("value"));
  390. }
  391. else if(node.attribute("name") == "columnname")
  392. {
  393. columnName->setText(node.attribute("value"));
  394. }
  395. else if(node.attribute("name") == "channeltype")
  396. {
  397. subtype->setCurrentIndex(subtype->
  398. findData(QVariant(node.attribute("value").toInt())));
  399. subtypeLayout->setCurrentIndex(subtype->currentIndex());
  400. }
  401. else if(node.attribute("name") == "tctype")
  402. {
  403. tctype->setCurrentIndex(tctype->
  404. findData(QVariant(node.attribute("value").toInt())));
  405. }
  406. else if(node.attribute("name") == "rtdtype")
  407. {
  408. rtdtype->setCurrentIndex(rtdtype->
  409. findData(QVariant(node.attribute("value").toInt())));
  410. }
  411. else if(node.attribute("name") == "rtdwiring")
  412. {
  413. rtdwiring->setCurrentIndex(rtdwiring->
  414. findData(QVariant(node.attribute("value").toInt())));
  415. }
  416. else if(node.attribute("name") == "hubport")
  417. {
  418. hubPort->setText(node.attribute("value"));
  419. }
  420. else if(node.attribute("name") == "hidden")
  421. {
  422. hidden->setCheckState(node.attribute("value") == "true" ? Qt::Checked : Qt::Unchecked);
  423. }
  424. }
  425. outerLayout->addLayout(subtypeLayout);
  426. setLayout(outerLayout);
  427. updateSerialNumber(serialNumber->text());
  428. updateChannel(channel->text());
  429. updateColumnName(columnName->text());
  430. updateChannelType(subtype->currentIndex());
  431. updateTCType(tctype->currentIndex());
  432. updateRTDType(rtdtype->currentIndex());
  433. updateRTDWiring(rtdwiring->currentIndex());
  434. updateHubPort(hubPort->text());
  435. updateHidden(hidden->checkState());
  436. connect(channelSelector, SIGNAL(currentIndexChanged(int)),
  437. this, SLOT(changeSelectedChannel(int)));
  438. connect(subtype, SIGNAL(currentIndexChanged(int)),
  439. subtypeLayout, SLOT(setCurrentIndex(int)));
  440. connect(serialNumber, SIGNAL(textChanged(QString)),
  441. this, SLOT(updateSerialNumber(QString)));
  442. connect(channel, SIGNAL(textChanged(QString)),
  443. this, SLOT(updateChannel(QString)));
  444. connect(columnName, SIGNAL(textEdited(QString)),
  445. this, SLOT(updateColumnName(QString)));
  446. connect(subtype, SIGNAL(currentIndexChanged(int)),
  447. this, SLOT(updateChannelType(int)));
  448. connect(tctype, SIGNAL(currentIndexChanged(int)),
  449. this, SLOT(updateTCType(int)));
  450. connect(rtdtype, SIGNAL(currentIndexChanged(int)),
  451. this, SLOT(updateRTDType(int)));
  452. connect(rtdwiring, SIGNAL(currentIndexChanged(int)),
  453. this, SLOT(updateRTDWiring(int)));
  454. connect(hubPort, SIGNAL(textChanged(QString)),
  455. this, SLOT(updateHubPort(QString)));
  456. connect(hidden, SIGNAL(stateChanged(int)),
  457. this, SLOT(updateHidden(int)));
  458. }
  459. @ The combo box provides a convenient way to populate required configuration
  460. fields with values that are not immediately obvious.
  461. @<Phidget implementation@>=
  462. void PhidgetChannelConfWidget::changeSelectedChannel(int index)
  463. {
  464. QMap<QString,QVariant> data = channelSelector->itemData(index).toMap();
  465. serialNumber->setText(data.value("serialNumber").toString());
  466. channel->setText(data.value("channel").toString());
  467. subtype->setCurrentIndex(subtype->
  468. findData(QVariant(data.value("subclass").toString().toInt())));
  469. hubPort->setText(data.value("hubport").toString());
  470. }
  471. @ Channel configuration settings are persisted as they are updated.
  472. @<Phidget implementation@>=
  473. void PhidgetChannelConfWidget::updateSerialNumber(const QString &value)
  474. {
  475. updateAttribute("serialnumber", value);
  476. }
  477. void PhidgetChannelConfWidget::updateChannel(const QString &value)
  478. {
  479. updateAttribute("channel", value);
  480. }
  481. void PhidgetChannelConfWidget::updateColumnName(const QString &value)
  482. {
  483. updateAttribute("columnname", value);
  484. }
  485. void PhidgetChannelConfWidget::updateChannelType(int value)
  486. {
  487. updateAttribute("channeltype", subtype->itemData(value).toString());
  488. }
  489. void PhidgetChannelConfWidget::updateTCType(int value)
  490. {
  491. updateAttribute("tctype", tctype->itemData(value).toString());
  492. }
  493. void PhidgetChannelConfWidget::updateRTDType(int value)
  494. {
  495. updateAttribute("rtdtype", rtdtype->itemData(value).toString());
  496. }
  497. void PhidgetChannelConfWidget::updateRTDWiring(int value)
  498. {
  499. updateAttribute("rtdwiring", rtdwiring->itemData(value).toString());
  500. }
  501. void PhidgetChannelConfWidget::updateHubPort(const QString &value)
  502. {
  503. updateAttribute("hubport", value);
  504. }
  505. void PhidgetChannelConfWidget::updateHidden(int value)
  506. {
  507. updateAttribute("hidden", value == 0 ? "false" : "true");
  508. }
  509. @ The current input channel is intended for devices that can measure 4-20mA
  510. current signals. The output from such a channel is likely to be hidden and
  511. redirected to something that bring those measurements into whatever scale the
  512. signal represents. For example, the motivating hardware for this feature was a
  513. device that used 4-20mA to represent an approximation of the Agtron Gourmet
  514. Scale in the range of 25-95. Another potential use is measuring gas pressure,
  515. in which case it would be desirable to present this in terms of an appropriate
  516. pressure unit. Longer term it would be nice to add support for custom units and
  517. allow different graphing configurations for different units.
  518. @<Class declarations@>=
  519. class PhidgetCurrentChannelConfWidget : BasicDeviceConfigurationWidget
  520. {
  521. Q_OBJECT@;
  522. public:@/
  523. Q_INVOKABLE PhidgetCurrentChannelConfWidget(DeviceTreeModel *model,
  524. const QModelIndex &index);
  525. public slots:@/
  526. void changeSelectedChannel(int index);
  527. void updateSerialNumber(const QString &value);
  528. void updateChannel(const QString &value);
  529. void updateHubPort(const QString &value);
  530. void updateColumnName(const QString &value);
  531. void updatePowerSupply(int value);
  532. void updateDataInterval(int value);
  533. void updateHidden(int value);
  534. private:@/
  535. PhidgetChannelSelector *channelSelector;
  536. QLineEdit *serialNumber;
  537. QLineEdit *channel;
  538. QLineEdit *hubPort;
  539. QComboBox *powerSupply;
  540. };
  541. @ The constructor is responsible for setting up the interface. This is
  542. slightly simpler than the configuration for temperature inputs as instead of
  543. requiring information about RTD types and wiring or thermocouple types, a
  544. current input only requires selecting the power supply and data interval.
  545. It might be a good idea to go back to the temperature channels and allow the
  546. data interval to be set there as well instead of relying on the default
  547. sample rate.
  548. @<Phidget implementation@>=
  549. PhidgetCurrentChannelConfWidget::PhidgetCurrentChannelConfWidget(
  550. DeviceTreeModel *model, const QModelIndex &index
  551. ) :
  552. BasicDeviceConfigurationWidget(model, index),
  553. channelSelector(new PhidgetChannelSelector(2)),
  554. serialNumber(new QLineEdit),
  555. channel(new QLineEdit),
  556. hubPort(new QLineEdit),
  557. powerSupply(new QComboBox)
  558. {
  559. QFormLayout *layout = new QFormLayout;
  560. layout->addRow(tr("Channel:"), channelSelector);
  561. QLineEdit *columnName = new QLineEdit;
  562. layout->addRow(tr("Column Name:"), columnName);
  563. powerSupply->addItem(tr("12V"), QVariant(2));
  564. powerSupply->addItem(tr("24V"), QVariant(3));
  565. layout->addRow(tr("Power Supply:"), powerSupply);
  566. QSpinBox *dataInterval = new QSpinBox;
  567. dataInterval->setMinimum(20);
  568. dataInterval->setMaximum(1000);
  569. dataInterval->setValue(250);
  570. layout->addRow(tr("Data Interval:"), dataInterval);
  571. QCheckBox *hidden = new QCheckBox(tr("Hide channel"));
  572. layout->addRow(hidden);
  573. serialNumber->setEnabled(false);
  574. channel->setEnabled(false);
  575. hubPort->setEnabled(false);
  576. layout->addRow(tr("Serial Number:"), serialNumber);
  577. layout->addRow(tr("Channel Number:"), channel);
  578. layout->addRow(tr("Hub Port:"), hubPort);
  579. @<Get device configuration data for current node@>@;
  580. for(int i = 0; i < configData.size(); i++)
  581. {
  582. node = configData.at(i).toElement();
  583. if(node.attribute("name") == "serialnumber")
  584. {
  585. serialNumber->setText(node.attribute("value"));
  586. }
  587. else if(node.attribute("name") == "channel")
  588. {
  589. channel->setText(node.attribute("value"));
  590. }
  591. else if(node.attribute("name") == "columnname")
  592. {
  593. columnName->setText(node.attribute("value"));
  594. }
  595. else if(node.attribute("name") == "hidden")
  596. {
  597. hidden->setCheckState(node.attribute("value") == "true" ?
  598. Qt::Checked : Qt::Unchecked);
  599. }
  600. else if(node.attribute("name") == "powersupply")
  601. {
  602. powerSupply->setCurrentIndex(
  603. powerSupply->findData(
  604. QVariant(node.attribute("value").toInt())));
  605. }
  606. else if(node.attribute("name") == "datainterval")
  607. {
  608. dataInterval->setValue(node.attribute("value").toInt());
  609. }
  610. }
  611. setLayout(layout);
  612. updateSerialNumber(serialNumber->text());
  613. updateChannel(channel->text());
  614. updateColumnName(columnName->text());
  615. updateHubPort(hubPort->text());
  616. updateHidden(hidden->checkState());
  617. updatePowerSupply(powerSupply->currentIndex());
  618. updateDataInterval(dataInterval->value());
  619. connect(channelSelector, SIGNAL(currentIndexChanged(int)),
  620. this, SLOT(changeSelectedChannel(int)));
  621. connect(serialNumber, SIGNAL(textChanged(QString)),
  622. this, SLOT(updateSerialNumber(QString)));
  623. connect(channel, SIGNAL(textChanged(QString)),
  624. this, SLOT(updateChannel(QString)));
  625. connect(columnName, SIGNAL(textChanged(QString)),
  626. this, SLOT(updateColumnName(QString)));
  627. connect(hubPort, SIGNAL(textChanged(QString)),
  628. this, SLOT(updateHubPort(QString)));
  629. connect(hidden, SIGNAL(stateChanged(int)),
  630. this, SLOT(updateHidden(int)));
  631. connect(powerSupply, SIGNAL(currentIndexChanged(int)),
  632. this, SLOT(updatePowerSupply(int)));
  633. connect(dataInterval, SIGNAL(valueChanged(int)),
  634. this, SLOT(updateDataInterval(int)));
  635. }
  636. @ The combo box is responsible for setting a variety of required configuration
  637. fields with values the user has no reasonable expectation of knowing.
  638. @<Phidget implementation@>=
  639. void PhidgetCurrentChannelConfWidget::changeSelectedChannel(int index)
  640. {
  641. QMap<QString, QVariant> data = channelSelector->itemData(index).toMap();
  642. serialNumber->setText(data.value("serialNumber").toString());
  643. channel->setText(data.value("channel").toString());
  644. hubPort->setText(data.value("hubport").toString());
  645. }
  646. @ Channel configuration settings are persisted as they are updated as usual.
  647. @<Phidget implementation@>=
  648. void PhidgetCurrentChannelConfWidget::updateSerialNumber(const QString &value)
  649. {
  650. updateAttribute("serialnumber", value);
  651. }
  652. void PhidgetCurrentChannelConfWidget::updateChannel(const QString &value)
  653. {
  654. updateAttribute("channel", value);
  655. }
  656. void PhidgetCurrentChannelConfWidget::updateColumnName(const QString &value)
  657. {
  658. updateAttribute("columnname", value);
  659. }
  660. void PhidgetCurrentChannelConfWidget::updateHubPort(const QString &value)
  661. {
  662. updateAttribute("hubport", value);
  663. }
  664. void PhidgetCurrentChannelConfWidget::updateHidden(int value)
  665. {
  666. updateAttribute("hidden", value == 0 ? "false" : "true");
  667. }
  668. void PhidgetCurrentChannelConfWidget::updatePowerSupply(int value)
  669. {
  670. updateAttribute("powersupply", powerSupply->itemData(value).toString());
  671. }
  672. void PhidgetCurrentChannelConfWidget::updateDataInterval(int value)
  673. {
  674. updateAttribute("datainterval", QString("%1").arg(value));
  675. }
  676. @ The hardware communnications code provides a single class that reads the
  677. saved configuration data, creates |Channel| objects for the logging view to
  678. connect various things to, and pushes data out on those channels. Internally,
  679. there is more variability in how these channels must be set up, so rather than
  680. just having a bunch of lists for the various properties, not all of which might
  681. be relevant, instead, the channel configuration data will all be kept in a
  682. structure.
  683. @<Class declarations@>=
  684. struct PhidgetChannelData
  685. {
  686. Channel *channel;
  687. QString columnName;
  688. QString indicatorLabel;
  689. int serialNumber;
  690. int channelNumber;
  691. int majorType;
  692. int channelType;
  693. int hubPort;
  694. int dataInterval;
  695. // Set for temperature channels
  696. int tcType;
  697. int rtdType;
  698. int wiring;
  699. // Set for current channels
  700. int powerSupply;
  701. // Non-specialized
  702. bool hidden;
  703. void *device;
  704. };
  705. @ The host environment requires a class that handles communication with the
  706. hardware. The public interface has been kept the same as the phidget21 code to
  707. minimize changes required in the configuration files.
  708. @<Class declarations@>=
  709. class Phidget22 : public QObject
  710. {
  711. Q_OBJECT
  712. public:
  713. Q_INVOKABLE Phidget22(const QModelIndex &deviceIndex);
  714. Q_INVOKABLE int channelCount();
  715. Channel* getChannel(int channel);
  716. Q_INVOKABLE bool isChannelHidden(int channel);
  717. Q_INVOKABLE QString channelColumnName(int channel);
  718. Q_INVOKABLE QString channelIndicatorText(int channel);
  719. Q_INVOKABLE QString channelType(int channel);
  720. public slots:
  721. void start();
  722. void stop();
  723. private:
  724. QList<PhidgetChannelData *> channelConfiguration;
  725. QLibrary driver;
  726. PhidgetPointer p_createTemperatureSensor;
  727. PhidgetPointer p_createCurrentSensor;
  728. PhidgetPointerIntIn p_setSerialNumber;
  729. PhidgetPointerIntIn p_setChannelNumber;
  730. PhidgetPointerIntIn p_setHubPort;
  731. PhidgetPointerIntIn p_setTCType;
  732. PhidgetPointerIntIn p_setRTDType;
  733. PhidgetPointerIntIn p_setRTDWiring;
  734. PhidgetPointerIntIn p_setCurrentPowerSupply;
  735. PhidgetPointerIntIn p_setCurrentDataInterval;
  736. PhidgetPointerVCPointer p_setNewDataCallback;
  737. PhidgetPointerVCPointer p_setCurrentNewDataCallback;
  738. PhidgetPointerIntIn p_open;
  739. PhidgetPointer p_close;
  740. PhidgetPointer p_delete;
  741. PhidgetPointerECPointer p_setOnErrorCallback;
  742. };
  743. @ The constructor reads the previously saved hardware configuration data and
  744. uses that to create the relevant channels. The channels are not initialized
  745. until the device is started.
  746. @<Phidget implementation@>=
  747. Phidget22::Phidget22(const QModelIndex &index) : QObject(NULL)
  748. {
  749. DeviceTreeModel *model = (DeviceTreeModel *)(index.model());
  750. if(model->hasChildren(index))
  751. {
  752. for(int i = 0; i < model->rowCount(index); i++)
  753. {
  754. QModelIndex channelIndex = model->index(i, 0, index);
  755. QDomElement channelReference = model->
  756. referenceElement(model->data(channelIndex, 32).toString());
  757. QDomElement channelReferenceElement = model->
  758. referenceElement(model->
  759. data(channelIndex, Qt::UserRole).toString());
  760. QDomNodeList channelConfigData =
  761. channelReferenceElement.elementsByTagName("attribute");
  762. PhidgetChannelData *c = new PhidgetChannelData;
  763. c->channel = new Channel;
  764. c->indicatorLabel =
  765. model->data(channelIndex, Qt::DisplayRole).toString();
  766. c->device = NULL;
  767. c->hubPort = -1;
  768. c->dataInterval = -1;
  769. c->powerSupply = -1;
  770. if(channelReferenceElement.attribute("driver") == "phidgetchannel")
  771. {
  772. c->majorType = 28; // Temperature Input
  773. }
  774. else if(channelReferenceElement.attribute("driver") ==
  775. "phidgetcurrentchannel")
  776. {
  777. c->majorType = 2; // Current Input
  778. }
  779. for(int j = 0; j < channelConfigData.size(); j++)
  780. {
  781. QDomElement node = channelConfigData.at(j).toElement();
  782. if(node.attribute("name") == "serialnumber")
  783. {
  784. c->serialNumber = node.attribute("value").toInt();
  785. }
  786. else if(node.attribute("name") == "channel")
  787. {
  788. c->channelNumber = node.attribute("value").toInt();
  789. }
  790. else if(node.attribute("name") == "channeltype")
  791. {
  792. c->channelType = node.attribute("value").toInt();
  793. }
  794. else if(node.attribute("name") == "tctype")
  795. {
  796. c->tcType = node.attribute("value").toInt();
  797. }
  798. else if(node.attribute("name") == "rtdtype")
  799. {
  800. c->rtdType = node.attribute("value").toInt();
  801. }
  802. else if(node.attribute("name") == "rtdwiring")
  803. {
  804. c->wiring = node.attribute("value").toInt();
  805. }
  806. else if(node.attribute("name") == "powersupply")
  807. {
  808. c->powerSupply = node.attribute("value").toInt();
  809. }
  810. else if(node.attribute("name") == "datainterval")
  811. {
  812. c->dataInterval = node.attribute("value").toInt();
  813. }
  814. else if(node.attribute("name") == "hidden")
  815. {
  816. c->hidden = (node.attribute("value") == "true");
  817. }
  818. else if(node.attribute("name") == "columnname")
  819. {
  820. c->columnName = node.attribute("value");
  821. }
  822. else if(node.attribute("name") == "hubport")
  823. {
  824. c->hubPort = node.attribute("value").toInt();
  825. }
  826. }
  827. channelConfiguration.append(c);
  828. }
  829. }
  830. }
  831. @ A bit of glue is needed to get the |Channel| objects out to the host
  832. environment.
  833. @<Phidget implementation@>=
  834. int Phidget22::channelCount()
  835. {
  836. return channelConfiguration.length();
  837. }
  838. Channel* Phidget22::getChannel(int channel)
  839. {
  840. return channelConfiguration.at(channel)->channel;
  841. }
  842. @ A little more glue allows the host environment to properly set up UI
  843. elements.
  844. @<Phidget implementation@>=
  845. bool Phidget22::isChannelHidden(int channel)
  846. {
  847. return channelConfiguration.at(channel)->hidden;
  848. }
  849. QString Phidget22::channelColumnName(int channel)
  850. {
  851. return channelConfiguration.at(channel)->columnName;
  852. }
  853. QString Phidget22::channelIndicatorText(int channel)
  854. {
  855. return channelConfiguration.at(channel)->indicatorLabel;
  856. }
  857. QString Phidget22::channelType(int channel)
  858. {
  859. return (channelConfiguration.at(channel)->majorType == 28 ? "T" : "C");
  860. }
  861. @ Once the hardware configuration has been read and the UI has been set up, we
  862. can start talking to the hardware and start getting measurements.
  863. Now that multiple channel types are supported which each require slightly
  864. different initialization procedures, it would be nice to see if channel
  865. initialization can be reordered to avoid repeatedly checking the channel type
  866. without duplicating code. Alternately, shared features could be separated into
  867. their own chunks.
  868. @<Phidget implementation@>=
  869. void Phidget22::start()
  870. {
  871. @<Load Phidget22 library@>@;
  872. @<Resolve Phidget22 function pointers@>@;
  873. for(int i = 0; i < channelConfiguration.length(); i++)
  874. {
  875. PhidgetChannelData *c = channelConfiguration.at(i);
  876. switch(c->majorType)
  877. {
  878. case 2:
  879. p_createCurrentSensor(&(c->device));
  880. p_setOnErrorCallback(c->device,
  881. Phidget22CurrentErrorCallback,
  882. c->channel);
  883. break;
  884. case 28:
  885. p_createTemperatureSensor(&(c->device));
  886. break;
  887. default:
  888. break;
  889. }
  890. p_setSerialNumber(c->device, c->serialNumber);
  891. p_setChannelNumber(c->device, c->channelNumber);
  892. if(c->majorType == 28) //Set up temperature channel
  893. {
  894. switch(c->channelType)
  895. {
  896. case 32:
  897. p_setRTDType(c->device, c->rtdType);
  898. p_setRTDWiring(c->device, c->wiring);
  899. break;
  900. case 33:
  901. p_setTCType(c->device, c->tcType);
  902. break;
  903. default:
  904. break;
  905. }
  906. }
  907. if(c->hubPort >= 0)
  908. {
  909. p_setHubPort(c->device, c->hubPort);
  910. }
  911. switch(c->majorType)
  912. {
  913. case 2:
  914. p_setCurrentNewDataCallback(c->device,
  915. Phidget22CurrentValueCallback,
  916. c->channel);
  917. break;
  918. case 28:
  919. p_setNewDataCallback(c->device, Phidget22ValueCallback, c->channel);
  920. break;
  921. default:
  922. break;
  923. }
  924. p_open(c->device, 5000);
  925. /* The data interval must be set after opening the channel, otherwise
  926. the change has no effect. */
  927. if(c->majorType == 2)
  928. {
  929. p_setCurrentPowerSupply(c->device, c->powerSupply);
  930. p_setCurrentDataInterval(c->device, c->dataInterval);
  931. }
  932. }
  933. }
  934. @ The library we need is slightly different depending on the current platform.
  935. If the library is not installed, an error is displayed.
  936. @<Load Phidget22 library@>=
  937. #if __APPLE__
  938. driver.setFileName("Phidget22.framework/Phidget22");
  939. #else
  940. driver.setFileName("phidget22");
  941. #endif
  942. if(!driver.load())
  943. {
  944. QMessageBox::critical(NULL, tr("Typica: Driver not found"),
  945. tr("Failed to find phidget22. Please install it."));
  946. return;
  947. }
  948. @ Several function pointers are required to call into the library. If any of
  949. these fail to resolve, the most likely cause is that an incompatible library
  950. with the same name has been installed.
  951. @<Resolve Phidget22 function pointers@>=
  952. if((p_createTemperatureSensor = (PhidgetPointer)driver.resolve("PhidgetTemperatureSensor_create")) == 0 ||
  953. (p_createCurrentSensor = (PhidgetPointer)driver.resolve("PhidgetCurrentInput_create")) == 0 ||
  954. (p_setSerialNumber = (PhidgetPointerIntIn)driver.resolve("Phidget_setDeviceSerialNumber")) == 0 ||
  955. (p_setChannelNumber = (PhidgetPointerIntIn)driver.resolve("Phidget_setChannel")) == 0 ||
  956. (p_setTCType = (PhidgetPointerIntIn)driver.resolve("PhidgetTemperatureSensor_setThermocoupleType")) == 0 ||
  957. (p_setRTDType = (PhidgetPointerIntIn)driver.resolve("PhidgetTemperatureSensor_setRTDType")) == 0 ||
  958. (p_setRTDWiring = (PhidgetPointerIntIn)driver.resolve("PhidgetTemperatureSensor_setRTDWireSetup")) == 0 ||
  959. (p_setCurrentPowerSupply = (PhidgetPointerIntIn)driver.resolve("PhidgetCurrentInput_setPowerSupply")) == 0 ||
  960. (p_setCurrentDataInterval = (PhidgetPointerIntIn)driver.resolve("PhidgetCurrentInput_setDataInterval")) == 0 ||
  961. (p_setNewDataCallback = (PhidgetPointerVCPointer)driver.resolve("PhidgetTemperatureSensor_setOnTemperatureChangeHandler")) == 0 ||
  962. (p_setCurrentNewDataCallback = (PhidgetPointerVCPointer)driver.resolve("PhidgetCurrentInput_setOnCurrentChangeHandler")) == 0 ||
  963. (p_open = (PhidgetPointerIntIn)driver.resolve("Phidget_openWaitForAttachment")) == 0 ||
  964. (p_close = (PhidgetPointer)driver.resolve("Phidget_close")) == 0 ||
  965. (p_delete = (PhidgetPointer)driver.resolve("PhidgetTemperatureSensor_delete")) == 0 ||
  966. (p_setHubPort = (PhidgetPointerIntIn)driver.resolve("Phidget_setHubPort")) == 0 ||
  967. (p_setOnErrorCallback = (PhidgetPointerECPointer)driver.resolve("Phidget_setOnErrorHandler")) == 0)
  968. {
  969. QMessageBox::critical(NULL, tr("Typica: Link error"),
  970. tr("Failed to link a required symbol in phidget22."));
  971. return;
  972. }
  973. @ New values are delivered to a callback function outside of the class, but
  974. with a pointer to the relevant |Channel| object. This means that all the
  975. callback needs to do is perform the unit conversion, assemble the |Measurement|
  976. and send that out.
  977. Unfortunately, there can be no guarantee that new measurements will be
  978. available on all channels simultaneously. Hopefully this will not be too
  979. problematic.
  980. Temperature values and current values are handled separately with the
  981. former requiring a conversion into Fahrenheit and the latter providing a
  982. conversion into mA as the most common use is expected to be reading from
  983. 4-20mA sensors. Additional input types might require their own callbacks. For
  984. example, a voltage input callback might not perform any conversion.
  985. For current channels, the initial use case potentially uses the entire 0-20mA
  986. range, which can result in errors being generated instead of measurements. Out
  987. of range and saturation errors should be converted to 0 and 20 respectively.
  988. This may be the wrong call if someone wants to use a larger range current
  989. sensor such as 30A, but until someone provides a concrete use case I'm not
  990. going to worry about that.
  991. @<Additional functions@>=
  992. void CCONV Phidget22ValueCallback(void *, void *context, double value)
  993. {
  994. Channel *channel = (Channel*)context;
  995. QTime time = QTime::currentTime();
  996. Measurement measure(value * 9.0 / 5.0 + 32.0, time);
  997. channel->input(measure);
  998. }
  999. void CCONV Phidget22CurrentValueCallback(void *, void *context, double value)
  1000. {
  1001. Channel *channel = (Channel*)context;
  1002. QTime time = QTime::currentTime();
  1003. Measurement measure(value * 1000.0, time, Units::Unitless);
  1004. channel->input(measure);
  1005. }
  1006. void CCONV Phidget22CurrentErrorCallback(void *, void *context, int error,
  1007. const char *)
  1008. {
  1009. Channel *channel = (Channel*)context;
  1010. QTime time = QTime::currentTime();
  1011. switch(error)
  1012. {
  1013. case 4103: // Measurement below valid range
  1014. {
  1015. Measurement measure(0.0, time, Units::Unitless);
  1016. channel->input(measure);
  1017. }
  1018. break;
  1019. case 4105: // Measurement above valid range
  1020. {
  1021. Measurement measure(20.0, time, Units::Unitless);
  1022. channel->input(measure);
  1023. }
  1024. break;
  1025. default:
  1026. break;
  1027. }
  1028. }
  1029. @ Function prototypes are provided.
  1030. @<Additional function prototypes@>=
  1031. void CCONV Phidget22ValueCallback(void *device, void *context, double value);
  1032. void CCONV Phidget22CurrentValueCallback(void *device, void *context,
  1033. double value);
  1034. void CCONV Phidget22CurrentErrorCallback(void *device, void *context,
  1035. int error, const char *description);
  1036. @ When the logging window is closed, it is important to close all open channels
  1037. and delete their handles.
  1038. @<Phidget implementation@>=
  1039. void Phidget22::stop()
  1040. {
  1041. for(int i = 0; i < channelConfiguration.length(); i++)
  1042. {
  1043. PhidgetChannelData *c = channelConfiguration.at(i);
  1044. p_close(c->device);
  1045. p_delete(&(c->device));
  1046. }
  1047. }
  1048. @ Class implementations are currently folded into typica.cpp.
  1049. @<Class implementations@>=
  1050. @<Phidget implementation@>@;
  1051. @ The hardware communications class needs to be available from the host
  1052. environment.
  1053. @<Set up the scripting engine@>=
  1054. constructor = engine->newFunction(constructPhidget22);
  1055. value = engine->newQMetaObject(&Phidget22::staticMetaObject, constructor);
  1056. engine->globalObject().setProperty("Phidget22", value);
  1057. @ Two function prototypes are needed.
  1058. @<Function prototypes for scripting@>=
  1059. QScriptValue constructPhidget22(QScriptContext *context, QScriptEngine *engine);
  1060. QScriptValue Phidget22_getChannel(QScriptContext *context, QScriptEngine *engine);
  1061. @ The constructor is trivial.
  1062. @<Functions for scripting@>=
  1063. QScriptValue constructPhidget22(QScriptContext *context, QScriptEngine *engine)
  1064. {
  1065. if(context->argumentCount() != 1)
  1066. {
  1067. context->throwError("Incorrect number of arguments");
  1068. }
  1069. QScriptValue object = engine->newQObject(new Phidget22(argument<QModelIndex>(0, context)), QScriptEngine::ScriptOwnership);
  1070. setQObjectProperties(object, engine);
  1071. object.setProperty("getChannel", engine->newFunction(Phidget22_getChannel));
  1072. return object;
  1073. }
  1074. @ A wrapper is used for getting channels.
  1075. @<Functions for scripting@>=
  1076. QScriptValue Phidget22_getChannel(QScriptContext *context, QScriptEngine *engine)
  1077. {
  1078. Phidget22 *self = getself<Phidget22 *>(context);
  1079. QScriptValue object;
  1080. if(self)
  1081. {
  1082. object = engine->newQObject(self->getChannel(argument<int>(0, context)));
  1083. setChannelProperties(object, engine);
  1084. }
  1085. return object;
  1086. }