Browse Source

Phidget22 current channel support

Includes configuring current channels, reading current channel data, and allows some additional flexibility in numeric displays.
Neal Wilson 3 years ago
parent
commit
90aff0234c
2 changed files with 470 additions and 194 deletions
  1. 429
    58
      src/phidget22.w
  2. 41
    136
      src/typica.w

+ 429
- 58
src/phidget22.w View File

7
 
7
 
8
 API differences are significant enough that it makes more sense to write new
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
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
 people already using hardware previously supported and it is possible to use
11
 people already using hardware previously supported and it is possible to use
12
 both libraries simultaneously to communicate with different hardware.
12
 both libraries simultaneously to communicate with different hardware.
13
 
13
 
37
 	PhidgetConfWidget::staticMetaObject);
37
 	PhidgetConfWidget::staticMetaObject);
38
 app.registerDeviceConfigurationWidget("phidgetchannel",
38
 app.registerDeviceConfigurationWidget("phidgetchannel",
39
 	PhidgetChannelConfWidget::staticMetaObject);
39
 	PhidgetChannelConfWidget::staticMetaObject);
40
+app.registerDeviceConfigurationWidget("phidgetcurrentchannel",
41
+	PhidgetCurrentChannelConfWidget::staticMetaObject);
40
 
42
 
41
 @ The first configuration widget just serves as a parent to all channels using
43
 @ 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
44
 this library. There does not seem to be a need for the configuration to mirror
50
 	public:
52
 	public:
51
 		Q_INVOKABLE PhidgetConfWidget(DeviceTreeModel *model,
53
 		Q_INVOKABLE PhidgetConfWidget(DeviceTreeModel *model,
52
 		                              const QModelIndex &index);
54
 		                              const QModelIndex &index);
53
-	private slots:
54
-		void addChannel();
55
 };
55
 };
56
 
56
 
57
 @ The only thing this configuration widget provides is a way to create child
57
 @ The only thing this configuration widget provides is a way to create child
58
 nodes.
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
 @<Phidget implementation@>=
66
 @<Phidget implementation@>=
61
 PhidgetConfWidget::PhidgetConfWidget(DeviceTreeModel *model,
67
 PhidgetConfWidget::PhidgetConfWidget(DeviceTreeModel *model,
62
                                      const QModelIndex &index)
68
                                      const QModelIndex &index)
64
 {
70
 {
65
 	QHBoxLayout *layout = new QHBoxLayout;
71
 	QHBoxLayout *layout = new QHBoxLayout;
66
 	QPushButton *addChannelButton = new QPushButton(tr("Add Channel"));
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
 	layout->addWidget(addChannelButton);
87
 	layout->addWidget(addChannelButton);
69
 	setLayout(layout);
88
 	setLayout(layout);
70
 }
89
 }
71
 
90
 
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
91
 @ For this library, \pn{} supports a broader range of hardware. This requires
78
 slightly more involved hardware configuration to ensure that a given channel
92
 slightly more involved hardware configuration to ensure that a given channel
79
 configuration consistently refers to the same sensor.
93
 configuration consistently refers to the same sensor.
85
 built in ambient temperature sensors on some devices do not require additional
99
 built in ambient temperature sensors on some devices do not require additional
86
 configuration.
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
 To simplify configuration, a combo box is provided which displays all of the
102
 To simplify configuration, a combo box is provided which displays all of the
92
 currently connected channels that \pn{} supports and allows a configuration
103
 currently connected channels that \pn{} supports and allows a configuration
93
 widget to obtain relevant channel information when the desired channel is
104
 widget to obtain relevant channel information when the desired channel is
94
 selected.
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
 @<Class declarations@>=
128
 @<Class declarations@>=
97
 class PhidgetChannelSelector : public QComboBox
129
 class PhidgetChannelSelector : public QComboBox
98
 {
130
 {
99
 	Q_OBJECT
131
 	Q_OBJECT
100
-	public:
101
-		PhidgetChannelSelector();
132
+	public:@/
133
+		PhidgetChannelSelector(int channeltype = 0);
102
 		~PhidgetChannelSelector();
134
 		~PhidgetChannelSelector();
103
 		void addChannel(void *device);
135
 		void addChannel(void *device);
104
 		void removeChannel(void *device);
136
 		void removeChannel(void *device);
105
-	private:
137
+	private:@/
138
+		int typefilter;
106
 		QLibrary driver;
139
 		QLibrary driver;
107
 		void *manager;
140
 		void *manager;
108
 		@<Phidget22 function pointers@>@;
141
 		@<Phidget22 function pointers@>@;
127
 typedef int (CCONV *PhidgetPointerIntOut)(void *, int *);
160
 typedef int (CCONV *PhidgetPointerIntOut)(void *, int *);
128
 typedef void (CCONV *PhidgetManagerCallback)(void *, void *, void *);
161
 typedef void (CCONV *PhidgetManagerCallback)(void *, void *, void *);
129
 typedef void (CCONV *PhidgetValueCallback)(void *, void *, double);
162
 typedef void (CCONV *PhidgetValueCallback)(void *, void *, double);
163
+typedef void (CCONV *PhidgetErrorCallback)(void *, void *, int, const char *);
130
 typedef int (CCONV *PhidgetPointerCallbackPointer)(void *,
164
 typedef int (CCONV *PhidgetPointerCallbackPointer)(void *,
131
                                                    PhidgetManagerCallback,
165
                                                    PhidgetManagerCallback,
132
                                                    void *);
166
                                                    void *);
134
                                              PhidgetValueCallback,
168
                                              PhidgetValueCallback,
135
                                              void *);
169
                                              void *);
136
 typedef int (CCONV *PhidgetPointerIntIn)(void *, int);
170
 typedef int (CCONV *PhidgetPointerIntIn)(void *, int);
171
+typedef int (CCONV *PhidgetPointerECPointer)(void *, PhidgetErrorCallback,
172
+                                              void *);
137
 
173
 
138
 @ These are used to define function pointers that will be used to
174
 @ These are used to define function pointers that will be used to
139
 communicate with the library.
175
 communicate with the library.
177
 the combo box.
213
 the combo box.
178
 
214
 
179
 @<Phidget implementation@>=
215
 @<Phidget implementation@>=
180
-PhidgetChannelSelector::PhidgetChannelSelector() : QComboBox(), manager(NULL)
216
+PhidgetChannelSelector::PhidgetChannelSelector(int channeltype) :
217
+	QComboBox(), typefilter(channeltype), manager(NULL)
181
 {
218
 {
182
 #if __APPLE__
219
 #if __APPLE__
183
 	driver.setFileName("Phidget22.framework/Phidget22");
220
 	driver.setFileName("Phidget22.framework/Phidget22");
244
 	
281
 	
245
 	QMap<QString,QVariant> itemData;
282
 	QMap<QString,QVariant> itemData;
246
 	
283
 	
247
-	if(channelClass == 28) // Temperature sensor
284
+	if(typefilter != 0 && channelClass == typefilter)
248
 	{
285
 	{
249
 		itemData.insert("serialNumber", QString("%1").arg(deviceSerialNumber));
286
 		itemData.insert("serialNumber", QString("%1").arg(deviceSerialNumber));
250
 		itemData.insert("channel", QString("%1").arg(channel));
287
 		itemData.insert("channel", QString("%1").arg(channel));
251
 		itemData.insert("class", QString("%1").arg(channelClass));
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
 		itemData.insert("hubport", QString("%1").arg(hubPort));
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
                                                    
333
                                                    
295
 @ Channel configuration provides a |PhidgetChannelSelector| for choosing
334
 @ Channel configuration provides a |PhidgetChannelSelector| for choosing
296
 among connected devices but also displays the relevant configuration data.
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
 @<Class declarations@>=
342
 @<Class declarations@>=
299
 class PhidgetChannelConfWidget : public BasicDeviceConfigurationWidget
343
 class PhidgetChannelConfWidget : public BasicDeviceConfigurationWidget
331
 PhidgetChannelConfWidget::PhidgetChannelConfWidget(DeviceTreeModel *model,
375
 PhidgetChannelConfWidget::PhidgetChannelConfWidget(DeviceTreeModel *model,
332
                                                    const QModelIndex &index)
376
                                                    const QModelIndex &index)
333
 	: BasicDeviceConfigurationWidget(model, index),
377
 	: BasicDeviceConfigurationWidget(model, index),
334
-	channelSelector(new PhidgetChannelSelector),
378
+	channelSelector(new PhidgetChannelSelector(28)),
335
 	serialNumber(new QLineEdit),
379
 	serialNumber(new QLineEdit),
336
 	channel(new QLineEdit),
380
 	channel(new QLineEdit),
337
 	hubPort(new QLineEdit),
381
 	hubPort(new QLineEdit),
527
 	updateAttribute("hidden", value == 0 ? "false" : "true");
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
 @ The hardware communnications code provides a single class that reads the
756
 @ The hardware communnications code provides a single class that reads the
531
 saved configuration data, creates |Channel| objects for the logging view to
757
 saved configuration data, creates |Channel| objects for the logging view to
532
 connect various things to, and pushes data out on those channels. Internally,
758
 connect various things to, and pushes data out on those channels. Internally,
543
 	QString indicatorLabel;
769
 	QString indicatorLabel;
544
 	int serialNumber;
770
 	int serialNumber;
545
 	int channelNumber;
771
 	int channelNumber;
772
+	int majorType;
546
 	int channelType;
773
 	int channelType;
547
 	int hubPort;
774
 	int hubPort;
775
+	int dataInterval;
776
+	// Set for temperature channels
548
 	int tcType;
777
 	int tcType;
549
 	int rtdType;
778
 	int rtdType;
550
 	int wiring;
779
 	int wiring;
780
+	// Set for current channels
781
+	int powerSupply;
782
+	// Non-specialized
551
 	bool hidden;
783
 	bool hidden;
552
 	void *device;
784
 	void *device;
553
 };
785
 };
567
 		Q_INVOKABLE bool isChannelHidden(int channel);
799
 		Q_INVOKABLE bool isChannelHidden(int channel);
568
 		Q_INVOKABLE QString channelColumnName(int channel);
800
 		Q_INVOKABLE QString channelColumnName(int channel);
569
 		Q_INVOKABLE QString channelIndicatorText(int channel);
801
 		Q_INVOKABLE QString channelIndicatorText(int channel);
802
+		Q_INVOKABLE QString channelType(int channel);
570
 	public slots:
803
 	public slots:
571
 		void start();
804
 		void start();
572
 		void stop();
805
 		void stop();
574
 		QList<PhidgetChannelData *> channelConfiguration;
807
 		QList<PhidgetChannelData *> channelConfiguration;
575
 		QLibrary driver;
808
 		QLibrary driver;
576
 		PhidgetPointer p_createTemperatureSensor;
809
 		PhidgetPointer p_createTemperatureSensor;
810
+		PhidgetPointer p_createCurrentSensor;
577
 		PhidgetPointerIntIn p_setSerialNumber;
811
 		PhidgetPointerIntIn p_setSerialNumber;
578
 		PhidgetPointerIntIn p_setChannelNumber;
812
 		PhidgetPointerIntIn p_setChannelNumber;
579
 		PhidgetPointerIntIn p_setHubPort;
813
 		PhidgetPointerIntIn p_setHubPort;
580
 		PhidgetPointerIntIn p_setTCType;
814
 		PhidgetPointerIntIn p_setTCType;
581
 		PhidgetPointerIntIn p_setRTDType;
815
 		PhidgetPointerIntIn p_setRTDType;
582
 		PhidgetPointerIntIn p_setRTDWiring;
816
 		PhidgetPointerIntIn p_setRTDWiring;
817
+		PhidgetPointerIntIn p_setCurrentPowerSupply;
818
+		PhidgetPointerIntIn p_setCurrentDataInterval;
583
 		PhidgetPointerVCPointer p_setNewDataCallback;
819
 		PhidgetPointerVCPointer p_setNewDataCallback;
820
+		PhidgetPointerVCPointer p_setCurrentNewDataCallback;
584
 		PhidgetPointerIntIn p_open;
821
 		PhidgetPointerIntIn p_open;
585
 		PhidgetPointer p_close;
822
 		PhidgetPointer p_close;
586
 		PhidgetPointer p_delete;
823
 		PhidgetPointer p_delete;
824
+		PhidgetPointerECPointer p_setOnErrorCallback;
587
 };
825
 };
588
 
826
 
589
 @ The constructor reads the previously saved hardware configuration data and
827
 @ The constructor reads the previously saved hardware configuration data and
612
 				model->data(channelIndex, Qt::DisplayRole).toString();
850
 				model->data(channelIndex, Qt::DisplayRole).toString();
613
 			c->device = NULL;
851
 			c->device = NULL;
614
 			c->hubPort = -1;
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
 			for(int j = 0; j < channelConfigData.size(); j++)
864
 			for(int j = 0; j < channelConfigData.size(); j++)
616
 			{
865
 			{
617
 				QDomElement node = channelConfigData.at(j).toElement();
866
 				QDomElement node = channelConfigData.at(j).toElement();
639
 				{
888
 				{
640
 					c->wiring = node.attribute("value").toInt();
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
 				else if(node.attribute("name") == "hidden")
899
 				else if(node.attribute("name") == "hidden")
643
 				{
900
 				{
644
 					c->hidden = (node.attribute("value") == "true");
901
 					c->hidden = (node.attribute("value") == "true");
690
 	return channelConfiguration.at(channel)->indicatorLabel;
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
 @ Once the hardware configuration has been read and the UI has been set up, we
955
 @ Once the hardware configuration has been read and the UI has been set up, we
694
 can start talking to the hardware and start getting measurements.
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
 @<Phidget implementation@>=
964
 @<Phidget implementation@>=
697
 void Phidget22::start()
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
 	for(int i = 0; i < channelConfiguration.length(); i++)
970
 	for(int i = 0; i < channelConfiguration.length(); i++)
727
 	{
971
 	{
728
 		PhidgetChannelData *c = channelConfiguration.at(i);
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
 				break;
980
 				break;
738
-			case 33:
739
-				p_setTCType(c->device, c->tcType);
981
+			case 28:
982
+				p_createTemperatureSensor(&(c->device));
740
 				break;
983
 				break;
741
 			default:
984
 			default:
742
 				break;
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
 		if(c->hubPort >= 0)
1004
 		if(c->hubPort >= 0)
745
 		{
1005
 		{
746
 			p_setHubPort(c->device, c->hubPort);
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
 		p_open(c->device, 5000);
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
 @ New values are delivered to a callback function outside of the class, but
1075
 @ New values are delivered to a callback function outside of the class, but
754
 with a pointer to the relevant |Channel| object. This means that all the
1076
 with a pointer to the relevant |Channel| object. This means that all the
755
 callback needs to do is perform the unit conversion, assemble the |Measurement|
1077
 callback needs to do is perform the unit conversion, assemble the |Measurement|
759
 available on all channels simultaneously. Hopefully this will not be too
1081
 available on all channels simultaneously. Hopefully this will not be too
760
 problematic.
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
 @<Additional functions@>=
1097
 @<Additional functions@>=
763
 void CCONV Phidget22ValueCallback(void *, void *context, double value)
1098
 void CCONV Phidget22ValueCallback(void *, void *context, double value)
764
 {
1099
 {
768
 	channel->input(measure);
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
 @<Additional function prototypes@>=
1140
 @<Additional function prototypes@>=
774
 void CCONV Phidget22ValueCallback(void *device, void *context, double value);
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
 @ When the logging window is closed, it is important to close all open channels
1147
 @ When the logging window is closed, it is important to close all open channels
777
 and delete their handles.
1148
 and delete their handles.

+ 41
- 136
src/typica.w View File

689
     return self;
689
     return self;
690
 }
690
 }
691
 
691
 
692
-template<> QModelIndex getself(QScriptContext *context)
693
-{
694
-    QModelIndex self = context->thisObject().toVariant().value<QModelIndex>();
695
-    return self;
696
-}
697
-
698
 template<> QByteArray getself(QScriptContext *context)
692
 template<> QByteArray getself(QScriptContext *context)
699
 {
693
 {
700
     QByteArray self = context->thisObject().toVariant().toByteArray();
694
     QByteArray self = context->thisObject().toVariant().toByteArray();
3516
         {@t\1@>@/
3510
         {@t\1@>@/
3517
             case 4:@/
3511
             case 4:@/
3518
                 arg4 = argument<int>(3, context);
3512
                 arg4 = argument<int>(3, context);
3519
-                arg3 = argument<int>(2, context);
3520
-                arg2 = argument<int>(1, context);
3521
-                arg1 = argument<int>(0, context);
3522
-                break;
3523
             case 3:@/
3513
             case 3:@/
3524
                 arg3 = argument<int>(2, context);
3514
                 arg3 = argument<int>(2, context);
3525
-                arg2 = argument<int>(1, context);
3526
-                arg1 = argument<int>(0, context);
3527
-                break;
3528
             case 2:@/
3515
             case 2:@/
3529
                 arg2 = argument<int>(1, context);
3516
                 arg2 = argument<int>(1, context);
3530
                 arg1 = argument<int>(0, context);
3517
                 arg1 = argument<int>(0, context);
3531
-                break;
3532
             default:@/
3518
             default:@/
3533
                 break;@t\2@>@/
3519
                 break;@t\2@>@/
3534
         }
3520
         }
3867
         {@t\1@>@/
3853
         {@t\1@>@/
3868
             case 4:@/
3854
             case 4:@/
3869
                 arg4 = argument<int>(3, context);
3855
                 arg4 = argument<int>(3, context);
3870
-                arg3 = argument<int>(2, context);
3871
-                arg2 = argument<int>(1, context);
3872
-                arg1 = argument<int>(0, context);
3873
-                break;
3874
             case 3:@/
3856
             case 3:@/
3875
                 arg3 = argument<int>(2, context);
3857
                 arg3 = argument<int>(2, context);
3876
                 arg2 = argument<int>(1, context);
3858
                 arg2 = argument<int>(1, context);
3877
                 arg1 = argument<int>(0, context);
3859
                 arg1 = argument<int>(0, context);
3878
-                break;
3879
             default:@/
3860
             default:@/
3880
                 break;@t\2@>@/
3861
                 break;@t\2@>@/
3881
         }
3862
         }
4695
                     QStack<QLayout *> *layoutStack);
4676
                     QStack<QLayout *> *layoutStack);
4696
 void populateStackedLayout(QDomElement element, QStack<QWidget *> *widgetStack,
4677
 void populateStackedLayout(QDomElement element, QStack<QWidget *> *widgetStack,
4697
                            QStack<QLayout *> *layoutStack);
4678
                            QStack<QLayout *> *layoutStack);
4698
-void populateFormLayout(QDomElement element, QStack<QWidget *> *widgetStack,@|
4699
-                        QStack<QLayout *> *layoutStack);
4700
 void addTemperatureDisplayToSplitter(QDomElement element,@|
4679
 void addTemperatureDisplayToSplitter(QDomElement element,@|
4701
                                      QStack<QWidget *> *widgetStack,
4680
                                      QStack<QWidget *> *widgetStack,
4702
                                      QStack<QLayout *> *layoutStack);
4681
                                      QStack<QLayout *> *layoutStack);
5041
     layoutStack->push(layout);
5020
     layoutStack->push(layout);
5042
     populateStackedLayout(element, widgetStack, layoutStack);
5021
     populateStackedLayout(element, widgetStack, layoutStack);
5043
 }
5022
 }
5044
-else if(layoutType == "form")
5045
-{
5046
-    layout = new QFormLayout;
5047
-    layoutStack->push(layout);
5048
-    populateFormLayout(element, widgetStack, layoutStack);
5049
-}
5050
 if(element.hasAttribute("id"))
5023
 if(element.hasAttribute("id"))
5051
 {
5024
 {
5052
     layout->setObjectName(element.attribute("id"));
5025
     layout->setObjectName(element.attribute("id"));
5061
     layout->setContentsMargins(m, m, m, m);
5034
     layout->setContentsMargins(m, m, m, m);
5062
 }
5035
 }
5063
 
5036
 
5064
-@ Any direct child of a form layout must be a {\tt <row>} element to specify
5065
-the label for the given row. The field for the given row will always be a
5066
-|QVBoxLayout| containing whatever is specified by children of the {\tt <row>}.
5067
-
5068
-@<Functions for scripting@>=
5069
-void populateFormLayout(QDomElement element, QStack<QWidget *> *widgetStack,
5070
-                        QStack<QLayout *> *layoutStack)
5071
-{
5072
-    QDomNodeList children = element.childNodes();
5073
-    QFormLayout *layout = qobject_cast<QFormLayout *>(layoutStack->top());
5074
-    for(int i = 0; i < children.count(); i++)
5075
-    {
5076
-        QDomNode current;
5077
-        QDomElement currentElement;
5078
-        current = children.at(i);
5079
-        if(current.isElement())
5080
-        {
5081
-            currentElement = current.toElement();
5082
-            if(currentElement.tagName() == "row")
5083
-            {
5084
-                QString label = QString();
5085
-                if(currentElement.hasAttribute("label"))
5086
-                {
5087
-                    label = currentElement.attribute("label");
5088
-                }
5089
-                QVBoxLayout *childLayout = new QVBoxLayout;
5090
-                layoutStack->push(childLayout);
5091
-                populateBoxLayout(currentElement, widgetStack, layoutStack);
5092
-                if(label.isEmpty())
5093
-                {
5094
-                    layout->addRow(childLayout);
5095
-                }
5096
-                else
5097
-                {
5098
-                    layout->addRow(label, childLayout);
5099
-                }
5100
-            }
5101
-        }
5102
-    }
5103
-}
5104
-
5105
 @ Stacked layouts are a bit different from the other types. A stacked layout has
5037
 @ Stacked layouts are a bit different from the other types. A stacked layout has
5106
 an arbitrary number of {\tt <page>} children which are just a |QWidget| which
5038
 an arbitrary number of {\tt <page>} children which are just a |QWidget| which
5107
 can have the same child elements as {\tt <widget>} elements elsewhere. Only the
5039
 can have the same child elements as {\tt <widget>} elements elsewhere. Only the
5728
 
5660
 
5729
 When splitters are used as a way to hide optional features it sometimes has the
5661
 When splitters are used as a way to hide optional features it sometimes has the
5730
 effect of forcing a window to stay larger than should be required. To fix this,
5662
 effect of forcing a window to stay larger than should be required. To fix this,
5731
-it is possible to set the \tt{ignoreSizePolicy} attribute to true. While this
5663
+it is possible to set the {\tt ignoreSizePolicy} attribute to true. While this
5732
 does solve the window size issue, this technique is inconsistent with generally
5664
 does solve the window size issue, this technique is inconsistent with generally
5733
 expected behavior and its use should generally be discouraged.
5665
 expected behavior and its use should generally be discouraged.
5734
 
5666
 
6051
     {
5983
     {
6052
         view->setObjectName(element.attribute("id"));
5984
         view->setObjectName(element.attribute("id"));
6053
     }
5985
     }
6054
-    if(element.hasAttribute("editable"))
6055
-    {
6056
-        if(element.attribute("editable") == "false")
6057
-        {
6058
-            view->setEditTriggers(QAbstractItemView::NoEditTriggers);
6059
-        }
6060
-    }
6061
-    if(element.hasAttribute("selectionBehavior"))
6062
-    {
6063
-        if(element.attribute("selectionBehavior") == "items")
6064
-        {
6065
-            view->setSelectionBehavior(QAbstractItemView::SelectItems);
6066
-        }
6067
-        else if(element.attribute("selectionBehavior") == "rows")
6068
-        {
6069
-            view->setSelectionBehavior(QAbstractItemView::SelectRows);
6070
-        }
6071
-        else if(element.attribute("selectionBehavior") == "columns")
6072
-        {
6073
-            view->setSelectionBehavior(QAbstractItemView::SelectColumns);
6074
-        }
6075
-    }
6076
     if(element.hasChildNodes())
5986
     if(element.hasChildNodes())
6077
     {
5987
     {
6078
         QDomNodeList children = element.childNodes();
5988
         QDomNodeList children = element.childNodes();
6166
 {
6076
 {
6167
     widget->setDisplayColumn(currentElement.attribute("display").toInt());
6077
     widget->setDisplayColumn(currentElement.attribute("display").toInt());
6168
 }
6078
 }
6169
-if(currentElement.hasAttribute("editable"))
6170
-{
6171
-    if(currentElement.attribute("editable") == "true")
6172
-    {
6173
-        widget->setEditable(true);
6174
-    }
6175
-}
6176
 widget->addSqlOptions(currentElement.text());
6079
 widget->addSqlOptions(currentElement.text());
6177
 delegate->setWidget(widget);
6080
 delegate->setWidget(widget);
6178
 view->setItemDelegateForColumn(currentColumn, delegate);
6081
 view->setItemDelegateForColumn(currentColumn, delegate);
8283
 
8186
 
8284
 This is a specialization of |QLCDNumber|.
8187
 This is a specialization of |QLCDNumber|.
8285
 
8188
 
8189
+With the addition of 4-20mA current channels, it no longer makes sense to
8190
+restrict non-temperature measurements to whole number values. The default
8191
+behavior is kept the same to avoid breaking old configurations, however new
8192
+uses should explicitly set a desired number of decimal places for each
8193
+indicator.
8194
+
8286
 @<Class declarations@>=
8195
 @<Class declarations@>=
8287
 class TemperatureDisplay : public QLCDNumber@/
8196
 class TemperatureDisplay : public QLCDNumber@/
8288
 {@t\1@>@/
8197
 {@t\1@>@/
8289
     Q_OBJECT@;
8198
     Q_OBJECT@;
8290
     int unit;
8199
     int unit;
8291
     bool r;
8200
     bool r;
8201
+	int decimalPlaces;
8292
     public:@/
8202
     public:@/
8293
         TemperatureDisplay(QWidget *parent = NULL);
8203
         TemperatureDisplay(QWidget *parent = NULL);
8294
         ~TemperatureDisplay();@/
8204
         ~TemperatureDisplay();@/
8296
         void setValue(Measurement temperature);
8206
         void setValue(Measurement temperature);
8297
         void invalidate();
8207
         void invalidate();
8298
         void setDisplayUnits(Units::Unit scale);
8208
         void setDisplayUnits(Units::Unit scale);
8299
-        void setRelativeMode(bool relative);@t\2@>@/
8209
+        void setRelativeMode(bool relative);
8210
+		void setDecimalPlaces(int value);@t\2@>@/
8300
 };
8211
 };
8301
 
8212
 
8302
 @ Starting in version 1.6 this widget is also used for displaying a relative
8213
 @ Starting in version 1.6 this widget is also used for displaying a relative
8310
     r = relative;
8221
     r = relative;
8311
 }
8222
 }
8312
 
8223
 
8224
+@ Starting in version 1.10 it is possible to override the default number of
8225
+decimal places for different units on a per indicator basis. This allows more
8226
+sensible handling of raw non-temperature measurements.
8227
+
8228
+@<TemperatureDisplay Implementation@>=
8229
+void TemperatureDisplay::setDecimalPlaces(int value)
8230
+{
8231
+	decimalPlaces = value;
8232
+}
8233
+
8313
 @ Displaying a temperature is a simple matter of taking the temperature
8234
 @ Displaying a temperature is a simple matter of taking the temperature
8314
 component from the measurement and converting it to a string. Presently, this
8235
 component from the measurement and converting it to a string. Presently, this
8315
 code assumes that the measurements are in degrees Fahrenheit. If the code
8236
 code assumes that the measurements are in degrees Fahrenheit. If the code
8330
         case Units::Fahrenheit:
8251
         case Units::Fahrenheit:
8331
             display(QString("%1'F").
8252
             display(QString("%1'F").
8332
                 arg(number.setNum(temperature.toFahrenheit().temperature(), 'f',
8253
                 arg(number.setNum(temperature.toFahrenheit().temperature(), 'f',
8333
-                                  2)));
8254
+                                  (decimalPlaces == -1 ? 2 : decimalPlaces))));
8334
             break;
8255
             break;
8335
         case Units::Celsius:
8256
         case Units::Celsius:
8336
             if(!r) {
8257
             if(!r) {
8337
                 display(QString("%1'C").
8258
                 display(QString("%1'C").
8338
                     arg(number.setNum(temperature.toCelsius().temperature(), 'f',
8259
                     arg(number.setNum(temperature.toCelsius().temperature(), 'f',
8339
-                                    2)));
8260
+                                    (decimalPlaces == -1 ? 2 : decimalPlaces))));
8340
             } else {
8261
             } else {
8341
-                number.setNum(temperature.temperature() * (5.0/9.0), 'f', 2);
8262
+                number.setNum(temperature.temperature() * (5.0/9.0), 'f', (decimalPlaces == -1 ? 2 : decimalPlaces));
8342
                 display(QString("%1'C").arg(number));
8263
                 display(QString("%1'C").arg(number));
8343
             }
8264
             }
8344
             break;
8265
             break;
8346
             if(!r) {
8267
             if(!r) {
8347
                 display(QString("%1").
8268
                 display(QString("%1").
8348
                     arg(number.setNum(temperature.toKelvin().temperature(), 'f',
8269
                     arg(number.setNum(temperature.toKelvin().temperature(), 'f',
8349
-                                    2)));
8270
+                                    (decimalPlaces == -1 ? 2 : decimalPlaces))));
8350
             } else {
8271
             } else {
8351
-                number.setNum(temperature.temperature() * (5.0/9.0), 'f', 2);
8272
+                number.setNum(temperature.temperature() * (5.0/9.0), 'f', (decimalPlaces == -1 ? 2 : decimalPlaces));
8352
                 display(QString("%1").arg(number));
8273
                 display(QString("%1").arg(number));
8353
             }
8274
             }
8354
             break;
8275
             break;
8355
         case Units::Rankine:
8276
         case Units::Rankine:
8356
             display(QString("%1'r").
8277
             display(QString("%1'r").
8357
                 arg(number.setNum(temperature.toRankine().temperature(), 'f',
8278
                 arg(number.setNum(temperature.toRankine().temperature(), 'f',
8358
-                                  2)));
8279
+                                  (decimalPlaces == -1 ? 2 : decimalPlaces))));
8359
             break;
8280
             break;
8360
         case Units::Unitless:
8281
         case Units::Unitless:
8361
-            display(QString("%1").arg(number.setNum(temperature.temperature(), 'f', 0)));
8282
+            display(QString("%1").arg(number.setNum(temperature.temperature(), 'f', (decimalPlaces == -1 ? 0 : decimalPlaces))));
8362
             break;
8283
             break;
8363
         default:
8284
         default:
8364
             switch(temperature.scale())
8285
             switch(temperature.scale())
8365
             {
8286
             {
8366
                 case Units::Fahrenheit:
8287
                 case Units::Fahrenheit:
8367
                     display(QString("%1'F").
8288
                     display(QString("%1'F").
8368
-                        arg(number.setNum(temperature.temperature(), 'f', 2)));
8289
+                        arg(number.setNum(temperature.temperature(), 'f', (decimalPlaces == -1 ? 2 : decimalPlaces))));
8369
                     break;
8290
                     break;
8370
                 case Units::Celsius:
8291
                 case Units::Celsius:
8371
                     display(QString("%1'C").
8292
                     display(QString("%1'C").
8372
-                        arg(number.setNum(temperature.temperature(), 'f', 2)));
8293
+                        arg(number.setNum(temperature.temperature(), 'f', (decimalPlaces == -1 ? 2 : decimalPlaces))));
8373
                     break;
8294
                     break;
8374
                 case Units::Kelvin:
8295
                 case Units::Kelvin:
8375
                     display(QString("%1").
8296
                     display(QString("%1").
8376
-                        arg(number.setNum(temperature.temperature(), 'f', 2)));
8297
+                        arg(number.setNum(temperature.temperature(), 'f', (decimalPlaces == -1 ? 2 : decimalPlaces))));
8377
                     break;
8298
                     break;
8378
                 case Units::Rankine:
8299
                 case Units::Rankine:
8379
                     display(QString("%1'r").
8300
                     display(QString("%1'r").
8380
-                        arg(number.setNum(temperature.temperature(), 'f', 2)));
8301
+                        arg(number.setNum(temperature.temperature(), 'f', (decimalPlaces == -1 ? 2 : decimalPlaces))));
8381
                     break;
8302
                     break;
8382
                 case Units::Unitless:
8303
                 case Units::Unitless:
8383
-                    display(QString("%1").arg(number.setNum(temperature.temperature(), 'f', 0)));
8304
+                    display(QString("%1").arg(number.setNum(temperature.temperature(), 'f', (decimalPlaces == -1 ? 0 : decimalPlaces))));
8384
                     break;
8305
                     break;
8385
                 default:
8306
                 default:
8386
 					qDebug() << "Warning: Attempting to convert a non-temperature unit to a temperature unit";
8307
 					qDebug() << "Warning: Attempting to convert a non-temperature unit to a temperature unit";
8400
 \centerline{Figure \secno: Outline (Qt default) and Filled |QLCDNumber| Example}
8321
 \centerline{Figure \secno: Outline (Qt default) and Filled |QLCDNumber| Example}
8401
 \medskip
8322
 \medskip
8402
 
8323
 
8324
+The default initialization of decimalPlaces to -1 is used to preserve unit
8325
+specific defaults as seen above.
8326
+
8403
 @<TemperatureDisplay Implementation@>=
8327
 @<TemperatureDisplay Implementation@>=
8404
 TemperatureDisplay::TemperatureDisplay(QWidget *parent) :
8328
 TemperatureDisplay::TemperatureDisplay(QWidget *parent) :
8405
-    QLCDNumber(8, parent), unit(Units::Fahrenheit), r(false)@/
8329
+    QLCDNumber(8, parent), unit(Units::Fahrenheit), r(false),
8330
+	decimalPlaces(-1)@/
8406
 {
8331
 {
8407
     setSegmentStyle(Filled);
8332
     setSegmentStyle(Filled);
8408
     display("---.--'F");
8333
     display("---.--'F");
13392
     public:@/
13317
     public:@/
13393
         SaltModel(int columns);
13318
         SaltModel(int columns);
13394
         ~SaltModel();
13319
         ~SaltModel();
13395
-        Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const;
13320
+        int rowCount(const QModelIndex &parent = QModelIndex()) const;
13396
         int columnCount(const QModelIndex &parent = QModelIndex()) const;
13321
         int columnCount(const QModelIndex &parent = QModelIndex()) const;
13397
         bool setHeaderData(int section, Qt::Orientation@, orientation,
13322
         bool setHeaderData(int section, Qt::Orientation@, orientation,
13398
                            const QVariant &value, int role = Qt::DisplayRole);
13323
                            const QVariant &value, int role = Qt::DisplayRole);
13904
     specialNullText(tr("Unknown")), specialNullData(QVariant::String)
13829
     specialNullText(tr("Unknown")), specialNullData(QVariant::String)
13905
 {
13830
 {
13906
     view()->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
13831
     view()->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
13907
-    setMinimumContentsLength(20);
13908
 }
13832
 }
13909
 
13833
 
13910
 SqlComboBox::~SqlComboBox()
13834
 SqlComboBox::~SqlComboBox()
13952
                                            const QStyleOptionViewItem &,
13876
                                            const QStyleOptionViewItem &,
13953
                                            const QModelIndex &) const
13877
                                            const QModelIndex &) const
13954
 {
13878
 {
13955
-    SqlComboBox *retval = delegate->clone(parent);
13956
-    if(delegate->isEditable())
13957
-    {
13958
-        retval->setEditable(true);
13959
-    }
13960
-    return retval;
13879
+    return delegate->clone(parent);
13961
 }
13880
 }
13962
 
13881
 
13963
 @ To set the appropriate editor data, we check the value in the model and
13882
 @ To set the appropriate editor data, we check the value in the model and
13982
                                        const QModelIndex &index) const
13901
                                        const QModelIndex &index) const
13983
 {
13902
 {
13984
     SqlComboBox *self = qobject_cast<SqlComboBox *>(editor);
13903
     SqlComboBox *self = qobject_cast<SqlComboBox *>(editor);
13985
-    model->setData(index, self->currentText(), Qt::DisplayRole);
13986
     model->setData(index, self->itemData(self->currentIndex(), Qt::UserRole),
13904
     model->setData(index, self->itemData(self->currentIndex(), Qt::UserRole),
13987
                    Qt::UserRole);
13905
                    Qt::UserRole);
13906
+    model->setData(index, self->currentText(), Qt::DisplayRole);
13988
 }
13907
 }
13989
 
13908
 
13990
 @ This is needed to play nicely with the model view architecture.
13909
 @ This is needed to play nicely with the model view architecture.
16466
 Q_DECLARE_METATYPE(QModelIndex)
16385
 Q_DECLARE_METATYPE(QModelIndex)
16467
 
16386
 
16468
 @ Next we need a pair of functions to convert |QModelIndex| to and from script
16387
 @ Next we need a pair of functions to convert |QModelIndex| to and from script
16469
-values. Some |QModelIndex| methods are also exposed to the host environment.
16388
+values.
16470
 
16389
 
16471
 @<Function prototypes for scripting@>=
16390
 @<Function prototypes for scripting@>=
16472
 QScriptValue QModelIndex_toScriptValue(QScriptEngine *engine, const QModelIndex &index);
16391
 QScriptValue QModelIndex_toScriptValue(QScriptEngine *engine, const QModelIndex &index);
16473
 void QModelIndex_fromScriptValue(const QScriptValue &value, QModelIndex &index);
16392
 void QModelIndex_fromScriptValue(const QScriptValue &value, QModelIndex &index);
16474
-void setQModelIndexProperties(QScriptValue value, QScriptEngine *engine);
16475
-QScriptValue QModelIndex_row(QScriptContext *context, QScriptEngine *engine);
16476
 
16393
 
16477
 @ These are implemented thusly.
16394
 @ These are implemented thusly.
16478
 
16395
 
16482
     QVariant var;
16399
     QVariant var;
16483
     var.setValue(index);
16400
     var.setValue(index);
16484
     QScriptValue object = engine->newVariant(var);
16401
     QScriptValue object = engine->newVariant(var);
16485
-    setQModelIndexProperties(object, engine);
16486
     return object;
16402
     return object;
16487
 }
16403
 }
16488
 
16404
 
16491
     index = value.toVariant().value<QModelIndex>();
16407
     index = value.toVariant().value<QModelIndex>();
16492
 }
16408
 }
16493
 
16409
 
16494
-void setQModelIndexProperties(QScriptValue value, QScriptEngine *engine)
16495
-{
16496
-    value.setProperty("row", engine->newFunction(QModelIndex_row));
16497
-}
16498
-
16499
-QScriptValue QModelIndex_row(QScriptContext *context, QScriptEngine *engine)
16500
-{
16501
-    QModelIndex self = getself<QModelIndex>(context);
16502
-    return QScriptValue(engine, self.row());
16503
-}
16504
-
16505
 @ Finally we register this with the engine.
16410
 @ Finally we register this with the engine.
16506
 
16411
 
16507
 @<Set up the scripting engine@>=
16412
 @<Set up the scripting engine@>=

Loading…
Cancel
Save