Browse Source

Configuration controls for temperature sensors accessed through libphidget22

Neal Wilson 6 years ago
parent
commit
e565a0a48f
2 changed files with 489 additions and 0 deletions
  1. 484
    0
      src/phidget22.w
  2. 5
    0
      src/typica.w

+ 484
- 0
src/phidget22.w View File

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

+ 5
- 0
src/typica.w View File

@@ -523,7 +523,10 @@ generated file empty.
523 523
 #define PROGRAM_NAME "Typica"
524 524
 
525 525
 @<Header files to include@>@/
526
+@<Additional type definitions@>@/
527
+@<Additional function prototypes@>@/
526 528
 @<Class declarations@>@/
529
+@<Additional functions@>@/
527 530
 @<Function prototypes for scripting@>@/
528 531
 @<Logging function prototype@>@/
529 532
 @<Class implementations@>@/
@@ -19614,6 +19617,8 @@ topLevelNodeInserters.append(inserter);
19614 19617
 
19615 19618
 @i phidgets.w
19616 19619
 
19620
+@i phidget22.w
19621
+
19617 19622
 @* Configuration widget for a calibrated data series.
19618 19623
 
19619 19624
 \noindent This control is used for adding a |LinearSplineInterpolator| to the

Loading…
Cancel
Save