|
@@ -7,7 +7,7 @@ provide hardware specimen for testing that requires the new library.
|
7
|
7
|
|
8
|
8
|
API differences are significant enough that it makes more sense to write new
|
9
|
9
|
code for interacting with phidget22 than attempting to retrofit existing
|
10
|
|
-phidget21 code. By leaving both in, there is no configuration disription for
|
|
10
|
+phidget21 code. By leaving both in, there is no configuration disruption for
|
11
|
11
|
people already using hardware previously supported and it is possible to use
|
12
|
12
|
both libraries simultaneously to communicate with different hardware.
|
13
|
13
|
|
|
@@ -37,6 +37,8 @@ app.registerDeviceConfigurationWidget("phidget22",
|
37
|
37
|
PhidgetConfWidget::staticMetaObject);
|
38
|
38
|
app.registerDeviceConfigurationWidget("phidgetchannel",
|
39
|
39
|
PhidgetChannelConfWidget::staticMetaObject);
|
|
40
|
+app.registerDeviceConfigurationWidget("phidgetcurrentchannel",
|
|
41
|
+ PhidgetCurrentChannelConfWidget::staticMetaObject);
|
40
|
42
|
|
41
|
43
|
@ The first configuration widget just serves as a parent to all channels using
|
42
|
44
|
this library. There does not seem to be a need for the configuration to mirror
|
|
@@ -50,13 +52,17 @@ class PhidgetConfWidget : public BasicDeviceConfigurationWidget
|
50
|
52
|
public:
|
51
|
53
|
Q_INVOKABLE PhidgetConfWidget(DeviceTreeModel *model,
|
52
|
54
|
const QModelIndex &index);
|
53
|
|
- private slots:
|
54
|
|
- void addChannel();
|
55
|
55
|
};
|
56
|
56
|
|
57
|
57
|
@ The only thing this configuration widget provides is a way to create child
|
58
|
58
|
nodes.
|
59
|
59
|
|
|
60
|
+Originally, this only supported channels that use the TemperatureInput API.
|
|
61
|
+With the addition of support for other input types, the decision was made to
|
|
62
|
+give each channel type its own node type and configuration widget rather than
|
|
63
|
+attempt to cram every configuration option for all supported types into the
|
|
64
|
+same configuration control.
|
|
65
|
+
|
60
|
66
|
@<Phidget implementation@>=
|
61
|
67
|
PhidgetConfWidget::PhidgetConfWidget(DeviceTreeModel *model,
|
62
|
68
|
const QModelIndex &index)
|
|
@@ -64,16 +70,24 @@ PhidgetConfWidget::PhidgetConfWidget(DeviceTreeModel *model,
|
64
|
70
|
{
|
65
|
71
|
QHBoxLayout *layout = new QHBoxLayout;
|
66
|
72
|
QPushButton *addChannelButton = new QPushButton(tr("Add Channel"));
|
67
|
|
- connect(addChannelButton, SIGNAL(clicked()), this, SLOT(addChannel()));
|
|
73
|
+ QMenu *channelTypeMenu = new QMenu;
|
|
74
|
+ NodeInserter *temperatureChannel =
|
|
75
|
+ new NodeInserter(tr("Temperature Channel"),
|
|
76
|
+ tr("Temperature Channel"), "phidgetchannel");
|
|
77
|
+ connect(temperatureChannel, SIGNAL(triggered(QString, QString)),
|
|
78
|
+ this, SLOT(insertChildNode(QString, QString)));
|
|
79
|
+ channelTypeMenu->addAction(temperatureChannel);
|
|
80
|
+ NodeInserter *currentChannel =
|
|
81
|
+ new NodeInserter(tr("Current Channel"),
|
|
82
|
+ tr("Current Channel"), "phidgetcurrentchannel");
|
|
83
|
+ connect(currentChannel, SIGNAL(triggered(QString, QString)),
|
|
84
|
+ this, SLOT(insertChildNode(QString, QString)));
|
|
85
|
+ channelTypeMenu->addAction(currentChannel);
|
|
86
|
+ addChannelButton->setMenu(channelTypeMenu);
|
68
|
87
|
layout->addWidget(addChannelButton);
|
69
|
88
|
setLayout(layout);
|
70
|
89
|
}
|
71
|
90
|
|
72
|
|
-void PhidgetConfWidget::addChannel()
|
73
|
|
-{
|
74
|
|
- insertChildNode(tr("Channel"), "phidgetchannel");
|
75
|
|
-}
|
76
|
|
-
|
77
|
91
|
@ For this library, \pn{} supports a broader range of hardware. This requires
|
78
|
92
|
slightly more involved hardware configuration to ensure that a given channel
|
79
|
93
|
configuration consistently refers to the same sensor.
|
|
@@ -85,24 +99,43 @@ thermocouple requires different configuration options than an RTD while the
|
85
|
99
|
built in ambient temperature sensors on some devices do not require additional
|
86
|
100
|
configuration.
|
87
|
101
|
|
88
|
|
-At present, only temperature sensors are supported, however this code could be
|
89
|
|
-extended to support other options.
|
90
|
|
-
|
91
|
102
|
To simplify configuration, a combo box is provided which displays all of the
|
92
|
103
|
currently connected channels that \pn{} supports and allows a configuration
|
93
|
104
|
widget to obtain relevant channel information when the desired channel is
|
94
|
105
|
selected.
|
95
|
106
|
|
|
107
|
+By passing an optional channel type into the constructor, this will only
|
|
108
|
+display channels matching the specified type. Some potentially interesting
|
|
109
|
+channel types incude:
|
|
110
|
+
|
|
111
|
+\medskip
|
|
112
|
+
|
|
113
|
+\settabs 4 \columns
|
|
114
|
+
|
|
115
|
+\+&2&Current Input\cr
|
|
116
|
+\+&5&Digital Input\cr
|
|
117
|
+\+&6&Digital Output\cr
|
|
118
|
+\+&28&Temperature Input\cr
|
|
119
|
+\+&29&Voltage Input\cr
|
|
120
|
+\+&30&Voltage Output\cr
|
|
121
|
+\+&38&Current Output\cr
|
|
122
|
+\smallskip
|
|
123
|
+
|
|
124
|
+\centerline{Table \secno: A Selection of Phidget Channel Types}
|
|
125
|
+
|
|
126
|
+\medskip
|
|
127
|
+
|
96
|
128
|
@<Class declarations@>=
|
97
|
129
|
class PhidgetChannelSelector : public QComboBox
|
98
|
130
|
{
|
99
|
131
|
Q_OBJECT
|
100
|
|
- public:
|
101
|
|
- PhidgetChannelSelector();
|
|
132
|
+ public:@/
|
|
133
|
+ PhidgetChannelSelector(int channeltype = 0);
|
102
|
134
|
~PhidgetChannelSelector();
|
103
|
135
|
void addChannel(void *device);
|
104
|
136
|
void removeChannel(void *device);
|
105
|
|
- private:
|
|
137
|
+ private:@/
|
|
138
|
+ int typefilter;
|
106
|
139
|
QLibrary driver;
|
107
|
140
|
void *manager;
|
108
|
141
|
@<Phidget22 function pointers@>@;
|
|
@@ -127,6 +160,7 @@ typedef int (CCONV *PhidgetPointerStringOut)(void *, char **);
|
127
|
160
|
typedef int (CCONV *PhidgetPointerIntOut)(void *, int *);
|
128
|
161
|
typedef void (CCONV *PhidgetManagerCallback)(void *, void *, void *);
|
129
|
162
|
typedef void (CCONV *PhidgetValueCallback)(void *, void *, double);
|
|
163
|
+typedef void (CCONV *PhidgetErrorCallback)(void *, void *, int, const char *);
|
130
|
164
|
typedef int (CCONV *PhidgetPointerCallbackPointer)(void *,
|
131
|
165
|
PhidgetManagerCallback,
|
132
|
166
|
void *);
|
|
@@ -134,6 +168,8 @@ typedef int (CCONV *PhidgetPointerVCPointer)(void *,
|
134
|
168
|
PhidgetValueCallback,
|
135
|
169
|
void *);
|
136
|
170
|
typedef int (CCONV *PhidgetPointerIntIn)(void *, int);
|
|
171
|
+typedef int (CCONV *PhidgetPointerECPointer)(void *, PhidgetErrorCallback,
|
|
172
|
+ void *);
|
137
|
173
|
|
138
|
174
|
@ These are used to define function pointers that will be used to
|
139
|
175
|
communicate with the library.
|
|
@@ -177,7 +213,8 @@ if((createManager = (PhidgetPointer) driver.resolve("PhidgetManager_create")) ==
|
177
|
213
|
the combo box.
|
178
|
214
|
|
179
|
215
|
@<Phidget implementation@>=
|
180
|
|
-PhidgetChannelSelector::PhidgetChannelSelector() : QComboBox(), manager(NULL)
|
|
216
|
+PhidgetChannelSelector::PhidgetChannelSelector(int channeltype) :
|
|
217
|
+ QComboBox(), typefilter(channeltype), manager(NULL)
|
181
|
218
|
{
|
182
|
219
|
#if __APPLE__
|
183
|
220
|
driver.setFileName("Phidget22.framework/Phidget22");
|
|
@@ -244,14 +281,16 @@ void PhidgetChannelSelector::addChannel(void *device)
|
244
|
281
|
|
245
|
282
|
QMap<QString,QVariant> itemData;
|
246
|
283
|
|
247
|
|
- if(channelClass == 28) // Temperature sensor
|
|
284
|
+ if(typefilter != 0 && channelClass == typefilter)
|
248
|
285
|
{
|
249
|
286
|
itemData.insert("serialNumber", QString("%1").arg(deviceSerialNumber));
|
250
|
287
|
itemData.insert("channel", QString("%1").arg(channel));
|
251
|
288
|
itemData.insert("class", QString("%1").arg(channelClass));
|
252
|
|
- itemData.insert("subclass", QString("%1").arg(channelSubclass));
|
|
289
|
+ itemData.insert("subclass",
|
|
290
|
+ QString("%1").arg(channelSubclass));
|
253
|
291
|
itemData.insert("hubport", QString("%1").arg(hubPort));
|
254
|
|
- addItem(QString("%1: %2").arg(deviceName).arg(channel), QVariant(itemData));
|
|
292
|
+ addItem(QString("%1: %2").arg(deviceName).arg(channel),
|
|
293
|
+ QVariant(itemData));
|
255
|
294
|
}
|
256
|
295
|
}
|
257
|
296
|
|
|
@@ -294,6 +333,11 @@ PhidgetChannelSelector::~PhidgetChannelSelector()
|
294
|
333
|
|
295
|
334
|
@ Channel configuration provides a |PhidgetChannelSelector| for choosing
|
296
|
335
|
among connected devices but also displays the relevant configuration data.
|
|
336
|
+
|
|
337
|
+This class only deals with temperature channels as that was the only channel
|
|
338
|
+type originally supported. Other configuration classes should be used for
|
|
339
|
+other channel types to allow type specific configuration options to be
|
|
340
|
+presented sensibly.
|
297
|
341
|
|
298
|
342
|
@<Class declarations@>=
|
299
|
343
|
class PhidgetChannelConfWidget : public BasicDeviceConfigurationWidget
|
|
@@ -331,7 +375,7 @@ class PhidgetChannelConfWidget : public BasicDeviceConfigurationWidget
|
331
|
375
|
PhidgetChannelConfWidget::PhidgetChannelConfWidget(DeviceTreeModel *model,
|
332
|
376
|
const QModelIndex &index)
|
333
|
377
|
: BasicDeviceConfigurationWidget(model, index),
|
334
|
|
- channelSelector(new PhidgetChannelSelector),
|
|
378
|
+ channelSelector(new PhidgetChannelSelector(28)),
|
335
|
379
|
serialNumber(new QLineEdit),
|
336
|
380
|
channel(new QLineEdit),
|
337
|
381
|
hubPort(new QLineEdit),
|
|
@@ -527,6 +571,188 @@ void PhidgetChannelConfWidget::updateHidden(int value)
|
527
|
571
|
updateAttribute("hidden", value == 0 ? "false" : "true");
|
528
|
572
|
}
|
529
|
573
|
|
|
574
|
+@ The current input channel is intended for devices that can measure 4-20mA
|
|
575
|
+current signals. The output from such a channel is likely to be hidden and
|
|
576
|
+redirected to something that bring those measurements into whatever scale the
|
|
577
|
+signal represents. For example, the motivating hardware for this feature was a
|
|
578
|
+device that used 4-20mA to represent an approximation of the Agtron Gourmet
|
|
579
|
+Scale in the range of 25-95. Another potential use is measuring gas pressure,
|
|
580
|
+in which case it would be desirable to present this in terms of an appropriate
|
|
581
|
+pressure unit. Longer term it would be nice to add support for custom units and
|
|
582
|
+allow different graphing configurations for different units.
|
|
583
|
+
|
|
584
|
+@<Class declarations@>=
|
|
585
|
+class PhidgetCurrentChannelConfWidget : BasicDeviceConfigurationWidget
|
|
586
|
+{
|
|
587
|
+ Q_OBJECT@;
|
|
588
|
+ public:@/
|
|
589
|
+ Q_INVOKABLE PhidgetCurrentChannelConfWidget(DeviceTreeModel *model,
|
|
590
|
+ const QModelIndex &index);
|
|
591
|
+ public slots:@/
|
|
592
|
+ void changeSelectedChannel(int index);
|
|
593
|
+ void updateSerialNumber(const QString &value);
|
|
594
|
+ void updateChannel(const QString &value);
|
|
595
|
+ void updateHubPort(const QString &value);
|
|
596
|
+ void updateColumnName(const QString &value);
|
|
597
|
+ void updatePowerSupply(int value);
|
|
598
|
+ void updateDataInterval(int value);
|
|
599
|
+ void updateHidden(int value);
|
|
600
|
+ private:@/
|
|
601
|
+ PhidgetChannelSelector *channelSelector;
|
|
602
|
+ QLineEdit *serialNumber;
|
|
603
|
+ QLineEdit *channel;
|
|
604
|
+ QLineEdit *hubPort;
|
|
605
|
+ QComboBox *powerSupply;
|
|
606
|
+};
|
|
607
|
+
|
|
608
|
+@ The constructor is responsible for setting up the interface. This is
|
|
609
|
+slightly simpler than the configuration for temperature inputs as instead of
|
|
610
|
+requiring information about RTD types and wiring or thermocouple types, a
|
|
611
|
+current input only requires selecting the power supply and data interval.
|
|
612
|
+
|
|
613
|
+It might be a good idea to go back to the temperature channels and allow the
|
|
614
|
+data interval to be set there as well instead of relying on the default
|
|
615
|
+sample rate.
|
|
616
|
+
|
|
617
|
+@<Phidget implementation@>=
|
|
618
|
+PhidgetCurrentChannelConfWidget::PhidgetCurrentChannelConfWidget(
|
|
619
|
+ DeviceTreeModel *model, const QModelIndex &index
|
|
620
|
+) :
|
|
621
|
+ BasicDeviceConfigurationWidget(model, index),
|
|
622
|
+ channelSelector(new PhidgetChannelSelector(2)),
|
|
623
|
+ serialNumber(new QLineEdit),
|
|
624
|
+ channel(new QLineEdit),
|
|
625
|
+ hubPort(new QLineEdit),
|
|
626
|
+ powerSupply(new QComboBox)
|
|
627
|
+{
|
|
628
|
+ QFormLayout *layout = new QFormLayout;
|
|
629
|
+ layout->addRow(tr("Channel:"), channelSelector);
|
|
630
|
+ QLineEdit *columnName = new QLineEdit;
|
|
631
|
+ layout->addRow(tr("Column Name:"), columnName);
|
|
632
|
+ powerSupply->addItem(tr("12V"), QVariant(2));
|
|
633
|
+ powerSupply->addItem(tr("24V"), QVariant(3));
|
|
634
|
+ layout->addRow(tr("Power Supply:"), powerSupply);
|
|
635
|
+ QSpinBox *dataInterval = new QSpinBox;
|
|
636
|
+ dataInterval->setMinimum(20);
|
|
637
|
+ dataInterval->setMaximum(1000);
|
|
638
|
+ dataInterval->setValue(250);
|
|
639
|
+ layout->addRow(tr("Data Interval:"), dataInterval);
|
|
640
|
+ QCheckBox *hidden = new QCheckBox(tr("Hide channel"));
|
|
641
|
+ layout->addRow(hidden);
|
|
642
|
+ serialNumber->setEnabled(false);
|
|
643
|
+ channel->setEnabled(false);
|
|
644
|
+ hubPort->setEnabled(false);
|
|
645
|
+ layout->addRow(tr("Serial Number:"), serialNumber);
|
|
646
|
+ layout->addRow(tr("Channel Number:"), channel);
|
|
647
|
+ layout->addRow(tr("Hub Port:"), hubPort);
|
|
648
|
+ @<Get device configuration data for current node@>@;
|
|
649
|
+ for(int i = 0; i < configData.size(); i++)
|
|
650
|
+ {
|
|
651
|
+ node = configData.at(i).toElement();
|
|
652
|
+ if(node.attribute("name") == "serialnumber")
|
|
653
|
+ {
|
|
654
|
+ serialNumber->setText(node.attribute("value"));
|
|
655
|
+ }
|
|
656
|
+ else if(node.attribute("name") == "channel")
|
|
657
|
+ {
|
|
658
|
+ channel->setText(node.attribute("value"));
|
|
659
|
+ }
|
|
660
|
+ else if(node.attribute("name") == "columnname")
|
|
661
|
+ {
|
|
662
|
+ columnName->setText(node.attribute("value"));
|
|
663
|
+ }
|
|
664
|
+ else if(node.attribute("name") == "hidden")
|
|
665
|
+ {
|
|
666
|
+ hidden->setCheckState(node.attribute("value") == "true" ?
|
|
667
|
+ Qt::Checked : Qt::Unchecked);
|
|
668
|
+ }
|
|
669
|
+ else if(node.attribute("name") == "powersupply")
|
|
670
|
+ {
|
|
671
|
+ powerSupply->setCurrentIndex(
|
|
672
|
+ powerSupply->findData(
|
|
673
|
+ QVariant(node.attribute("value").toInt())));
|
|
674
|
+ }
|
|
675
|
+ else if(node.attribute("name") == "datainterval")
|
|
676
|
+ {
|
|
677
|
+ dataInterval->setValue(node.attribute("value").toInt());
|
|
678
|
+ }
|
|
679
|
+ }
|
|
680
|
+ setLayout(layout);
|
|
681
|
+ updateSerialNumber(serialNumber->text());
|
|
682
|
+ updateChannel(channel->text());
|
|
683
|
+ updateColumnName(columnName->text());
|
|
684
|
+ updateHubPort(hubPort->text());
|
|
685
|
+ updateHidden(hidden->checkState());
|
|
686
|
+ updatePowerSupply(powerSupply->currentIndex());
|
|
687
|
+ updateDataInterval(dataInterval->value());
|
|
688
|
+ connect(channelSelector, SIGNAL(currentIndexChanged(int)),
|
|
689
|
+ this, SLOT(changeSelectedChannel(int)));
|
|
690
|
+ connect(serialNumber, SIGNAL(textChanged(QString)),
|
|
691
|
+ this, SLOT(updateSerialNumber(QString)));
|
|
692
|
+ connect(channel, SIGNAL(textChanged(QString)),
|
|
693
|
+ this, SLOT(updateChannel(QString)));
|
|
694
|
+ connect(columnName, SIGNAL(textChanged(QString)),
|
|
695
|
+ this, SLOT(updateColumnName(QString)));
|
|
696
|
+ connect(hubPort, SIGNAL(textChanged(QString)),
|
|
697
|
+ this, SLOT(updateHubPort(QString)));
|
|
698
|
+ connect(hidden, SIGNAL(stateChanged(int)),
|
|
699
|
+ this, SLOT(updateHidden(int)));
|
|
700
|
+ connect(powerSupply, SIGNAL(currentIndexChanged(int)),
|
|
701
|
+ this, SLOT(updatePowerSupply(int)));
|
|
702
|
+ connect(dataInterval, SIGNAL(valueChanged(int)),
|
|
703
|
+ this, SLOT(updateDataInterval(int)));
|
|
704
|
+}
|
|
705
|
+
|
|
706
|
+@ The combo box is responsible for setting a variety of required configuration
|
|
707
|
+fields with values the user has no reasonable expectation of knowing.
|
|
708
|
+
|
|
709
|
+@<Phidget implementation@>=
|
|
710
|
+void PhidgetCurrentChannelConfWidget::changeSelectedChannel(int index)
|
|
711
|
+{
|
|
712
|
+ QMap<QString, QVariant> data = channelSelector->itemData(index).toMap();
|
|
713
|
+ serialNumber->setText(data.value("serialNumber").toString());
|
|
714
|
+ channel->setText(data.value("channel").toString());
|
|
715
|
+ hubPort->setText(data.value("hubport").toString());
|
|
716
|
+}
|
|
717
|
+
|
|
718
|
+@ Channel configuration settings are persisted as they are updated as usual.
|
|
719
|
+
|
|
720
|
+@<Phidget implementation@>=
|
|
721
|
+void PhidgetCurrentChannelConfWidget::updateSerialNumber(const QString &value)
|
|
722
|
+{
|
|
723
|
+ updateAttribute("serialnumber", value);
|
|
724
|
+}
|
|
725
|
+
|
|
726
|
+void PhidgetCurrentChannelConfWidget::updateChannel(const QString &value)
|
|
727
|
+{
|
|
728
|
+ updateAttribute("channel", value);
|
|
729
|
+}
|
|
730
|
+
|
|
731
|
+void PhidgetCurrentChannelConfWidget::updateColumnName(const QString &value)
|
|
732
|
+{
|
|
733
|
+ updateAttribute("columnname", value);
|
|
734
|
+}
|
|
735
|
+
|
|
736
|
+void PhidgetCurrentChannelConfWidget::updateHubPort(const QString &value)
|
|
737
|
+{
|
|
738
|
+ updateAttribute("hubport", value);
|
|
739
|
+}
|
|
740
|
+
|
|
741
|
+void PhidgetCurrentChannelConfWidget::updateHidden(int value)
|
|
742
|
+{
|
|
743
|
+ updateAttribute("hidden", value == 0 ? "false" : "true");
|
|
744
|
+}
|
|
745
|
+
|
|
746
|
+void PhidgetCurrentChannelConfWidget::updatePowerSupply(int value)
|
|
747
|
+{
|
|
748
|
+ updateAttribute("powersupply", powerSupply->itemData(value).toString());
|
|
749
|
+}
|
|
750
|
+
|
|
751
|
+void PhidgetCurrentChannelConfWidget::updateDataInterval(int value)
|
|
752
|
+{
|
|
753
|
+ updateAttribute("datainterval", QString("%1").arg(value));
|
|
754
|
+}
|
|
755
|
+
|
530
|
756
|
@ The hardware communnications code provides a single class that reads the
|
531
|
757
|
saved configuration data, creates |Channel| objects for the logging view to
|
532
|
758
|
connect various things to, and pushes data out on those channels. Internally,
|
|
@@ -543,11 +769,17 @@ struct PhidgetChannelData
|
543
|
769
|
QString indicatorLabel;
|
544
|
770
|
int serialNumber;
|
545
|
771
|
int channelNumber;
|
|
772
|
+ int majorType;
|
546
|
773
|
int channelType;
|
547
|
774
|
int hubPort;
|
|
775
|
+ int dataInterval;
|
|
776
|
+ // Set for temperature channels
|
548
|
777
|
int tcType;
|
549
|
778
|
int rtdType;
|
550
|
779
|
int wiring;
|
|
780
|
+ // Set for current channels
|
|
781
|
+ int powerSupply;
|
|
782
|
+ // Non-specialized
|
551
|
783
|
bool hidden;
|
552
|
784
|
void *device;
|
553
|
785
|
};
|
|
@@ -567,6 +799,7 @@ class Phidget22 : public QObject
|
567
|
799
|
Q_INVOKABLE bool isChannelHidden(int channel);
|
568
|
800
|
Q_INVOKABLE QString channelColumnName(int channel);
|
569
|
801
|
Q_INVOKABLE QString channelIndicatorText(int channel);
|
|
802
|
+ Q_INVOKABLE QString channelType(int channel);
|
570
|
803
|
public slots:
|
571
|
804
|
void start();
|
572
|
805
|
void stop();
|
|
@@ -574,16 +807,21 @@ class Phidget22 : public QObject
|
574
|
807
|
QList<PhidgetChannelData *> channelConfiguration;
|
575
|
808
|
QLibrary driver;
|
576
|
809
|
PhidgetPointer p_createTemperatureSensor;
|
|
810
|
+ PhidgetPointer p_createCurrentSensor;
|
577
|
811
|
PhidgetPointerIntIn p_setSerialNumber;
|
578
|
812
|
PhidgetPointerIntIn p_setChannelNumber;
|
579
|
813
|
PhidgetPointerIntIn p_setHubPort;
|
580
|
814
|
PhidgetPointerIntIn p_setTCType;
|
581
|
815
|
PhidgetPointerIntIn p_setRTDType;
|
582
|
816
|
PhidgetPointerIntIn p_setRTDWiring;
|
|
817
|
+ PhidgetPointerIntIn p_setCurrentPowerSupply;
|
|
818
|
+ PhidgetPointerIntIn p_setCurrentDataInterval;
|
583
|
819
|
PhidgetPointerVCPointer p_setNewDataCallback;
|
|
820
|
+ PhidgetPointerVCPointer p_setCurrentNewDataCallback;
|
584
|
821
|
PhidgetPointerIntIn p_open;
|
585
|
822
|
PhidgetPointer p_close;
|
586
|
823
|
PhidgetPointer p_delete;
|
|
824
|
+ PhidgetPointerECPointer p_setOnErrorCallback;
|
587
|
825
|
};
|
588
|
826
|
|
589
|
827
|
@ The constructor reads the previously saved hardware configuration data and
|
|
@@ -612,6 +850,17 @@ Phidget22::Phidget22(const QModelIndex &index) : QObject(NULL)
|
612
|
850
|
model->data(channelIndex, Qt::DisplayRole).toString();
|
613
|
851
|
c->device = NULL;
|
614
|
852
|
c->hubPort = -1;
|
|
853
|
+ c->dataInterval = -1;
|
|
854
|
+ c->powerSupply = -1;
|
|
855
|
+ if(channelReferenceElement.attribute("driver") == "phidgetchannel")
|
|
856
|
+ {
|
|
857
|
+ c->majorType = 28; // Temperature Input
|
|
858
|
+ }
|
|
859
|
+ else if(channelReferenceElement.attribute("driver") ==
|
|
860
|
+ "phidgetcurrentchannel")
|
|
861
|
+ {
|
|
862
|
+ c->majorType = 2; // Current Input
|
|
863
|
+ }
|
615
|
864
|
for(int j = 0; j < channelConfigData.size(); j++)
|
616
|
865
|
{
|
617
|
866
|
QDomElement node = channelConfigData.at(j).toElement();
|
|
@@ -639,6 +888,14 @@ Phidget22::Phidget22(const QModelIndex &index) : QObject(NULL)
|
639
|
888
|
{
|
640
|
889
|
c->wiring = node.attribute("value").toInt();
|
641
|
890
|
}
|
|
891
|
+ else if(node.attribute("name") == "powersupply")
|
|
892
|
+ {
|
|
893
|
+ c->powerSupply = node.attribute("value").toInt();
|
|
894
|
+ }
|
|
895
|
+ else if(node.attribute("name") == "datainterval")
|
|
896
|
+ {
|
|
897
|
+ c->dataInterval = node.attribute("value").toInt();
|
|
898
|
+ }
|
642
|
899
|
else if(node.attribute("name") == "hidden")
|
643
|
900
|
{
|
644
|
901
|
c->hidden = (node.attribute("value") == "true");
|
|
@@ -690,66 +947,131 @@ QString Phidget22::channelIndicatorText(int channel)
|
690
|
947
|
return channelConfiguration.at(channel)->indicatorLabel;
|
691
|
948
|
}
|
692
|
949
|
|
|
950
|
+QString Phidget22::channelType(int channel)
|
|
951
|
+{
|
|
952
|
+ return (channelConfiguration.at(channel)->majorType == 28 ? "T" : "C");
|
|
953
|
+}
|
|
954
|
+
|
693
|
955
|
@ Once the hardware configuration has been read and the UI has been set up, we
|
694
|
956
|
can start talking to the hardware and start getting measurements.
|
695
|
957
|
|
|
958
|
+Now that multiple channel types are supported which each require slightly
|
|
959
|
+different initialization procedures, it would be nice to see if channel
|
|
960
|
+initialization can be reordered to avoid repeatedly checking the channel type
|
|
961
|
+without duplicating code. Alternately, shared features could be separated into
|
|
962
|
+their own chunks.
|
|
963
|
+
|
696
|
964
|
@<Phidget implementation@>=
|
697
|
965
|
void Phidget22::start()
|
698
|
966
|
{
|
699
|
|
-#if __APPLE__
|
700
|
|
- driver.setFileName("Phidget22.framework/Phidget22");
|
701
|
|
-#else
|
702
|
|
- driver.setFileName("phidget22");
|
703
|
|
-#endif
|
704
|
|
- if(!driver.load())
|
705
|
|
- {
|
706
|
|
- QMessageBox::critical(NULL, tr("Typica: Driver not found"),
|
707
|
|
- tr("Failed to find phidget22. Please install it."));
|
708
|
|
- return;
|
709
|
|
- }
|
710
|
|
- if((p_createTemperatureSensor = (PhidgetPointer)driver.resolve("PhidgetTemperatureSensor_create")) == 0 ||
|
711
|
|
- (p_setSerialNumber = (PhidgetPointerIntIn)driver.resolve("Phidget_setDeviceSerialNumber")) == 0 ||
|
712
|
|
- (p_setChannelNumber = (PhidgetPointerIntIn)driver.resolve("Phidget_setChannel")) == 0 ||
|
713
|
|
- (p_setTCType = (PhidgetPointerIntIn)driver.resolve("PhidgetTemperatureSensor_setThermocoupleType")) == 0 ||
|
714
|
|
- (p_setRTDType = (PhidgetPointerIntIn)driver.resolve("PhidgetTemperatureSensor_setRTDType")) == 0 ||
|
715
|
|
- (p_setRTDWiring = (PhidgetPointerIntIn)driver.resolve("PhidgetTemperatureSensor_setRTDWireSetup")) == 0 ||
|
716
|
|
- (p_setNewDataCallback = (PhidgetPointerVCPointer)driver.resolve("PhidgetTemperatureSensor_setOnTemperatureChangeHandler")) == 0 ||
|
717
|
|
- (p_open = (PhidgetPointerIntIn)driver.resolve("Phidget_openWaitForAttachment")) == 0 ||
|
718
|
|
- (p_close = (PhidgetPointer)driver.resolve("Phidget_close")) == 0 ||
|
719
|
|
- (p_delete = (PhidgetPointer)driver.resolve("PhidgetTemperatureSensor_delete")) == 0 ||
|
720
|
|
- (p_setHubPort = (PhidgetPointerIntIn)driver.resolve("Phidget_setHubPort")) == 0)
|
721
|
|
- {
|
722
|
|
- QMessageBox::critical(NULL, tr("Typica: Link error"),
|
723
|
|
- tr("Failed to link a required symbol in phidget22."));
|
724
|
|
- return;
|
725
|
|
- }
|
|
967
|
+ @<Load Phidget22 library@>@;
|
|
968
|
+ @<Resolve Phidget22 function pointers@>@;
|
|
969
|
+
|
726
|
970
|
for(int i = 0; i < channelConfiguration.length(); i++)
|
727
|
971
|
{
|
728
|
972
|
PhidgetChannelData *c = channelConfiguration.at(i);
|
729
|
|
- p_createTemperatureSensor(&(c->device));
|
730
|
|
- p_setSerialNumber(c->device, c->serialNumber);
|
731
|
|
- p_setChannelNumber(c->device, c->channelNumber);
|
732
|
|
- switch(c->channelType)
|
|
973
|
+ switch(c->majorType)
|
733
|
974
|
{
|
734
|
|
- case 32:
|
735
|
|
- p_setRTDType(c->device, c->rtdType);
|
736
|
|
- p_setRTDWiring(c->device, c->wiring);
|
|
975
|
+ case 2:
|
|
976
|
+ p_createCurrentSensor(&(c->device));
|
|
977
|
+ p_setOnErrorCallback(c->device,
|
|
978
|
+ Phidget22CurrentErrorCallback,
|
|
979
|
+ c->channel);
|
737
|
980
|
break;
|
738
|
|
- case 33:
|
739
|
|
- p_setTCType(c->device, c->tcType);
|
|
981
|
+ case 28:
|
|
982
|
+ p_createTemperatureSensor(&(c->device));
|
740
|
983
|
break;
|
741
|
984
|
default:
|
742
|
985
|
break;
|
743
|
986
|
}
|
|
987
|
+ p_setSerialNumber(c->device, c->serialNumber);
|
|
988
|
+ p_setChannelNumber(c->device, c->channelNumber);
|
|
989
|
+ if(c->majorType == 28) //Set up temperature channel
|
|
990
|
+ {
|
|
991
|
+ switch(c->channelType)
|
|
992
|
+ {
|
|
993
|
+ case 32:
|
|
994
|
+ p_setRTDType(c->device, c->rtdType);
|
|
995
|
+ p_setRTDWiring(c->device, c->wiring);
|
|
996
|
+ break;
|
|
997
|
+ case 33:
|
|
998
|
+ p_setTCType(c->device, c->tcType);
|
|
999
|
+ break;
|
|
1000
|
+ default:
|
|
1001
|
+ break;
|
|
1002
|
+ }
|
|
1003
|
+ }
|
744
|
1004
|
if(c->hubPort >= 0)
|
745
|
1005
|
{
|
746
|
1006
|
p_setHubPort(c->device, c->hubPort);
|
747
|
1007
|
}
|
748
|
|
- p_setNewDataCallback(c->device, Phidget22ValueCallback, c->channel);
|
|
1008
|
+ switch(c->majorType)
|
|
1009
|
+ {
|
|
1010
|
+ case 2:
|
|
1011
|
+ p_setCurrentNewDataCallback(c->device,
|
|
1012
|
+ Phidget22CurrentValueCallback,
|
|
1013
|
+ c->channel);
|
|
1014
|
+ break;
|
|
1015
|
+ case 28:
|
|
1016
|
+ p_setNewDataCallback(c->device, Phidget22ValueCallback, c->channel);
|
|
1017
|
+ break;
|
|
1018
|
+ default:
|
|
1019
|
+ break;
|
|
1020
|
+ }
|
749
|
1021
|
p_open(c->device, 5000);
|
|
1022
|
+ /* The data interval must be set after opening the channel, otherwise
|
|
1023
|
+ the change has no effect. */
|
|
1024
|
+ if(c->majorType == 2)
|
|
1025
|
+ {
|
|
1026
|
+ p_setCurrentPowerSupply(c->device, c->powerSupply);
|
|
1027
|
+ p_setCurrentDataInterval(c->device, c->dataInterval);
|
|
1028
|
+ }
|
750
|
1029
|
}
|
751
|
1030
|
}
|
752
|
1031
|
|
|
1032
|
+@ The library we need is slightly different depending on the current platform.
|
|
1033
|
+If the library is not installed, an error is displayed.
|
|
1034
|
+
|
|
1035
|
+@<Load Phidget22 library@>=
|
|
1036
|
+#if __APPLE__
|
|
1037
|
+ driver.setFileName("Phidget22.framework/Phidget22");
|
|
1038
|
+#else
|
|
1039
|
+ driver.setFileName("phidget22");
|
|
1040
|
+#endif
|
|
1041
|
+if(!driver.load())
|
|
1042
|
+{
|
|
1043
|
+ QMessageBox::critical(NULL, tr("Typica: Driver not found"),
|
|
1044
|
+ tr("Failed to find phidget22. Please install it."));
|
|
1045
|
+ return;
|
|
1046
|
+}
|
|
1047
|
+
|
|
1048
|
+@ Several function pointers are required to call into the library. If any of
|
|
1049
|
+these fail to resolve, the most likely cause is that an incompatible library
|
|
1050
|
+with the same name has been installed.
|
|
1051
|
+
|
|
1052
|
+@<Resolve Phidget22 function pointers@>=
|
|
1053
|
+if((p_createTemperatureSensor = (PhidgetPointer)driver.resolve("PhidgetTemperatureSensor_create")) == 0 ||
|
|
1054
|
+ (p_createCurrentSensor = (PhidgetPointer)driver.resolve("PhidgetCurrentInput_create")) == 0 ||
|
|
1055
|
+ (p_setSerialNumber = (PhidgetPointerIntIn)driver.resolve("Phidget_setDeviceSerialNumber")) == 0 ||
|
|
1056
|
+ (p_setChannelNumber = (PhidgetPointerIntIn)driver.resolve("Phidget_setChannel")) == 0 ||
|
|
1057
|
+ (p_setTCType = (PhidgetPointerIntIn)driver.resolve("PhidgetTemperatureSensor_setThermocoupleType")) == 0 ||
|
|
1058
|
+ (p_setRTDType = (PhidgetPointerIntIn)driver.resolve("PhidgetTemperatureSensor_setRTDType")) == 0 ||
|
|
1059
|
+ (p_setRTDWiring = (PhidgetPointerIntIn)driver.resolve("PhidgetTemperatureSensor_setRTDWireSetup")) == 0 ||
|
|
1060
|
+ (p_setCurrentPowerSupply = (PhidgetPointerIntIn)driver.resolve("PhidgetCurrentInput_setPowerSupply")) == 0 ||
|
|
1061
|
+ (p_setCurrentDataInterval = (PhidgetPointerIntIn)driver.resolve("PhidgetCurrentInput_setDataInterval")) == 0 ||
|
|
1062
|
+ (p_setNewDataCallback = (PhidgetPointerVCPointer)driver.resolve("PhidgetTemperatureSensor_setOnTemperatureChangeHandler")) == 0 ||
|
|
1063
|
+ (p_setCurrentNewDataCallback = (PhidgetPointerVCPointer)driver.resolve("PhidgetCurrentInput_setOnCurrentChangeHandler")) == 0 ||
|
|
1064
|
+ (p_open = (PhidgetPointerIntIn)driver.resolve("Phidget_openWaitForAttachment")) == 0 ||
|
|
1065
|
+ (p_close = (PhidgetPointer)driver.resolve("Phidget_close")) == 0 ||
|
|
1066
|
+ (p_delete = (PhidgetPointer)driver.resolve("PhidgetTemperatureSensor_delete")) == 0 ||
|
|
1067
|
+ (p_setHubPort = (PhidgetPointerIntIn)driver.resolve("Phidget_setHubPort")) == 0 ||
|
|
1068
|
+ (p_setOnErrorCallback = (PhidgetPointerECPointer)driver.resolve("Phidget_setOnErrorHandler")) == 0)
|
|
1069
|
+{
|
|
1070
|
+ QMessageBox::critical(NULL, tr("Typica: Link error"),
|
|
1071
|
+ tr("Failed to link a required symbol in phidget22."));
|
|
1072
|
+ return;
|
|
1073
|
+}
|
|
1074
|
+
|
753
|
1075
|
@ New values are delivered to a callback function outside of the class, but
|
754
|
1076
|
with a pointer to the relevant |Channel| object. This means that all the
|
755
|
1077
|
callback needs to do is perform the unit conversion, assemble the |Measurement|
|
|
@@ -759,6 +1081,19 @@ Unfortunately, there can be no guarantee that new measurements will be
|
759
|
1081
|
available on all channels simultaneously. Hopefully this will not be too
|
760
|
1082
|
problematic.
|
761
|
1083
|
|
|
1084
|
+Temperature values and current values are handled separately with the
|
|
1085
|
+former requiring a conversion into Fahrenheit and the latter providing a
|
|
1086
|
+conversion into mA as the most common use is expected to be reading from
|
|
1087
|
+4-20mA sensors. Additional input types might require their own callbacks. For
|
|
1088
|
+example, a voltage input callback might not perform any conversion.
|
|
1089
|
+
|
|
1090
|
+For current channels, the initial use case potentially uses the entire 0-20mA
|
|
1091
|
+range, which can result in errors being generated instead of measurements. Out
|
|
1092
|
+of range and saturation errors should be converted to 0 and 20 respectively.
|
|
1093
|
+This may be the wrong call if someone wants to use a larger range current
|
|
1094
|
+sensor such as 30A, but until someone provides a concrete use case I'm not
|
|
1095
|
+going to worry about that.
|
|
1096
|
+
|
762
|
1097
|
@<Additional functions@>=
|
763
|
1098
|
void CCONV Phidget22ValueCallback(void *, void *context, double value)
|
764
|
1099
|
{
|
|
@@ -768,10 +1103,46 @@ void CCONV Phidget22ValueCallback(void *, void *context, double value)
|
768
|
1103
|
channel->input(measure);
|
769
|
1104
|
}
|
770
|
1105
|
|
771
|
|
-@ A function prototype is provided.
|
|
1106
|
+void CCONV Phidget22CurrentValueCallback(void *, void *context, double value)
|
|
1107
|
+{
|
|
1108
|
+ Channel *channel = (Channel*)context;
|
|
1109
|
+ QTime time = QTime::currentTime();
|
|
1110
|
+ Measurement measure(value * 1000.0, time, Units::Unitless);
|
|
1111
|
+ channel->input(measure);
|
|
1112
|
+}
|
|
1113
|
+
|
|
1114
|
+void CCONV Phidget22CurrentErrorCallback(void *, void *context, int error,
|
|
1115
|
+ const char *)
|
|
1116
|
+{
|
|
1117
|
+ Channel *channel = (Channel*)context;
|
|
1118
|
+ QTime time = QTime::currentTime();
|
|
1119
|
+ switch(error)
|
|
1120
|
+ {
|
|
1121
|
+ case 4103: // Measurement below valid range
|
|
1122
|
+ {
|
|
1123
|
+ Measurement measure(0.0, time, Units::Unitless);
|
|
1124
|
+ channel->input(measure);
|
|
1125
|
+ }
|
|
1126
|
+ break;
|
|
1127
|
+ case 4105: // Measurement above valid range
|
|
1128
|
+ {
|
|
1129
|
+ Measurement measure(20.0, time, Units::Unitless);
|
|
1130
|
+ channel->input(measure);
|
|
1131
|
+ }
|
|
1132
|
+ break;
|
|
1133
|
+ default:
|
|
1134
|
+ break;
|
|
1135
|
+ }
|
|
1136
|
+}
|
|
1137
|
+
|
|
1138
|
+@ Function prototypes are provided.
|
772
|
1139
|
|
773
|
1140
|
@<Additional function prototypes@>=
|
774
|
1141
|
void CCONV Phidget22ValueCallback(void *device, void *context, double value);
|
|
1142
|
+void CCONV Phidget22CurrentValueCallback(void *device, void *context,
|
|
1143
|
+ double value);
|
|
1144
|
+void CCONV Phidget22CurrentErrorCallback(void *device, void *context,
|
|
1145
|
+ int error, const char *description);
|
775
|
1146
|
|
776
|
1147
|
@ When the logging window is closed, it is important to close all open channels
|
777
|
1148
|
and delete their handles.
|