Browse Source

Finish libphidget22 support

Neal Wilson 6 years ago
parent
commit
d23c04e6b4
2 changed files with 324 additions and 1 deletions
  1. 21
    0
      config/Windows/productionroaster.xml
  2. 303
    1
      src/phidget22.w

+ 21
- 0
config/Windows/productionroaster.xml View File

@@ -460,6 +460,27 @@
460 460
 					device.start();
461 461
 					pdevices.push(device);
462 462
 				}
463
+				else if(driverReference.driver == "phidget22")
464
+				{
465
+					var device = new Phidget22(driverIndex);
466
+					for(var j = 0; j < device.channelCount(); j++) {
467
+						channels.push(device.getChannel(j));
468
+						columnNames.push(device.channelColumnName(j));
469
+						channelType.push("T");
470
+						if(device.isChannelHidden(j)) {
471
+							channelVisibility.push(false);
472
+						} else {
473
+							channelVisibility.push(true);
474
+							var indicator = new TemperatureDisplay;
475
+							temperatureDisplays.push(indicator);
476
+							var decorator = new WidgetDecorator(indicator, device.channelIndicatorText(j), 2);
477
+							device.getChannel(j).newData.connect(indicator.setValue);
478
+							indicatorPanel.addWidget(decorator);
479
+						}
480
+					}
481
+					device.start();
482
+					pdevices.push(device);
483
+				}
463 484
 				else if(driverReference.driver == "annotationbutton")
464 485
 				{
465 486
 					var button = new AnnotationButton(driverReference.buttontext);

+ 303
- 1
src/phidget22.w View File

@@ -126,9 +126,14 @@ typedef int (CCONV *PhidgetPointer)(void *);
126 126
 typedef int (CCONV *PhidgetPointerStringOut)(void *, char **);
127 127
 typedef int (CCONV *PhidgetPointerIntOut)(void *, int *);
128 128
 typedef void (CCONV *PhidgetManagerCallback)(void *, void *, void *);
129
+typedef void (CCONV *PhidgetValueCallback)(void *, void *, double);
129 130
 typedef int (CCONV *PhidgetPointerCallbackPointer)(void *,
130 131
                                                    PhidgetManagerCallback,
131 132
                                                    void *);
133
+typedef int (CCONV *PhidgetPointerVCPointer)(void *,
134
+                                             PhidgetValueCallback,
135
+                                             void *);
136
+typedef int (CCONV *PhidgetPointerIntIn)(void *, int);
132 137
 
133 138
 @ These are used to define function pointers that will be used to
134 139
 communicate with the library.
@@ -478,7 +483,304 @@ void PhidgetChannelConfWidget::updateRTDWiring(int value)
478 483
 	updateAttribute("rtdwiring", rtdwiring->itemData(value).toString());
479 484
 }
480 485
 
486
+@ The hardware communnications code provides a single class that reads the
487
+saved configuration data, creates |Channel| objects for the logging view to
488
+connect various things to, and pushes data out on those channels. Internally,
489
+there is more variability in how these channels must be set up, so rather than
490
+just having a bunch of lists for the various properties, not all of which might
491
+be relevant, instead, the channel configuration data will all be kept in a
492
+structure.
493
+
494
+@<Class declarations@>=
495
+struct PhidgetChannelData
496
+{
497
+	Channel *channel;
498
+	QString columnName;
499
+	QString indicatorLabel;
500
+	int serialNumber;
501
+	int channelNumber;
502
+	int channelType;
503
+	int tcType;
504
+	int rtdType;
505
+	int wiring;
506
+	bool hidden;
507
+	void *device;
508
+};
509
+
510
+@ The host environment requires a class that handles communication with the
511
+hardware. The public interface has been kept the same as the phidget21 code to
512
+minimize changes required in the configuration files.
513
+
514
+@<Class declarations@>=
515
+class Phidget22 : public QObject
516
+{
517
+	Q_OBJECT
518
+	public:
519
+		Q_INVOKABLE Phidget22(const QModelIndex &deviceIndex);
520
+		Q_INVOKABLE int channelCount();
521
+		Channel* getChannel(int channel);
522
+		Q_INVOKABLE bool isChannelHidden(int channel);
523
+		Q_INVOKABLE QString channelColumnName(int channel);
524
+		Q_INVOKABLE QString channelIndicatorText(int channel);
525
+	public slots:
526
+		void start();
527
+		void stop();
528
+	private:
529
+		QList<PhidgetChannelData *> channelConfiguration;
530
+		QLibrary driver;
531
+		PhidgetPointer p_createTemperatureSensor;
532
+		PhidgetPointerIntIn p_setSerialNumber;
533
+		PhidgetPointerIntIn p_setChannelNumber;
534
+		PhidgetPointerIntIn p_setTCType;
535
+		PhidgetPointerIntIn p_setRTDType;
536
+		PhidgetPointerIntIn p_setRTDWiring;
537
+		PhidgetPointerVCPointer p_setNewDataCallback;
538
+		PhidgetPointerIntIn p_open;
539
+		PhidgetPointer p_close;
540
+		PhidgetPointer p_delete;
541
+};
542
+
543
+@ The constructor reads the previously saved hardware configuration data and
544
+uses that to create the relevant channels. The channels are not initialized
545
+until the device is started.
546
+
547
+@<Phidget implementation@>=
548
+Phidget22::Phidget22(const QModelIndex &index) : QObject(NULL)
549
+{
550
+	DeviceTreeModel *model = (DeviceTreeModel *)(index.model());
551
+	if(model->hasChildren(index))
552
+	{
553
+		for(int i = 0; i < model->rowCount(index); i++)
554
+		{
555
+			QModelIndex channelIndex = model->index(i, 0, index);
556
+			QDomElement channelReference = model->
557
+				referenceElement(model->data(channelIndex, 32).toString());
558
+			QDomElement channelReferenceElement = model->
559
+				referenceElement(model->
560
+					data(channelIndex, Qt::UserRole).toString());
561
+			QDomNodeList channelConfigData =
562
+				channelReferenceElement.elementsByTagName("attribute");
563
+			PhidgetChannelData *c = new PhidgetChannelData;
564
+			c->channel = new Channel;
565
+			c->indicatorLabel =
566
+				model->data(channelIndex, Qt::DisplayRole).toString();
567
+			c->device = NULL;
568
+			for(int j = 0; j < channelConfigData.size(); j++)
569
+			{
570
+				QDomElement node = channelConfigData.at(j).toElement();
571
+				if(node.attribute("name") == "serialnumber")
572
+				{
573
+					c->serialNumber = node.attribute("value").toInt();
574
+				}
575
+				else if(node.attribute("name") == "channel")
576
+				{
577
+					c->channelNumber = node.attribute("value").toInt();
578
+				}
579
+				else if(node.attribute("name") == "channeltype")
580
+				{
581
+					c->channelType = node.attribute("value").toInt();
582
+				}
583
+				else if(node.attribute("name") == "tctype")
584
+				{
585
+					c->tcType = node.attribute("value").toInt();
586
+				}
587
+				else if(node.attribute("name") == "rtdtype")
588
+				{
589
+					c->rtdType = node.attribute("value").toInt();
590
+				}
591
+				else if(node.attribute("name") == "rtdwiring")
592
+				{
593
+					c->wiring = node.attribute("value").toInt();
594
+				}
595
+				else if(node.attribute("name") == "hidden")
596
+				{
597
+					c->hidden = (node.attribute("value") == "true");
598
+				}
599
+				else if(node.attribute("name") == "columnname")
600
+				{
601
+					c->columnName = node.attribute("value");
602
+				}
603
+			}
604
+			channelConfiguration.append(c);
605
+		}
606
+	}
607
+}
608
+
609
+@ A bit of glue is needed to get the |Channel| objects out to the host
610
+environment.
611
+
612
+@<Phidget implementation@>=
613
+int Phidget22::channelCount()
614
+{
615
+	return channelConfiguration.length();
616
+}
617
+
618
+Channel* Phidget22::getChannel(int channel)
619
+{
620
+	return channelConfiguration.at(channel)->channel;
621
+}
622
+
623
+@ A little more glue allows the host environment to properly set up UI
624
+elements.
625
+
626
+TODO: Provide a configuration control for channel hiding and then change this
627
+to return whatever has been configured rather than assume all channels must be
628
+visible.
629
+
630
+@<Phidget implementation@>=
631
+bool Phidget22::isChannelHidden(int channel)
632
+{
633
+	// return channelConiguration.at(channel)->hidden;
634
+	return false;
635
+}
636
+
637
+QString Phidget22::channelColumnName(int channel)
638
+{
639
+	return channelConfiguration.at(channel)->columnName;
640
+}
641
+
642
+QString Phidget22::channelIndicatorText(int channel)
643
+{
644
+	return channelConfiguration.at(channel)->indicatorLabel;
645
+}
646
+
647
+@ Once the hardware configuration has been read and the UI has been set up, we
648
+can start talking to the hardware and start getting measurements.
649
+
650
+@<Phidget implementation@>=
651
+void Phidget22::start()
652
+{
653
+#if __APPLE__
654
+	driver.setFileName("Phidget22.framework/Phidget22");
655
+#else
656
+	driver.setFileName("phidget22");
657
+#endif
658
+	if(!driver.load())
659
+	{
660
+		QMessageBox::critical(NULL, tr("Typica: Driver not found"),
661
+		                      tr("Failed to find phidget22. Please install it."));
662
+		return;
663
+	}
664
+	if((p_createTemperatureSensor = (PhidgetPointer)driver.resolve("PhidgetTemperatureSensor_create")) == 0 ||
665
+		(p_setSerialNumber = (PhidgetPointerIntIn)driver.resolve("Phidget_setDeviceSerialNumber")) == 0 ||
666
+		(p_setChannelNumber = (PhidgetPointerIntIn)driver.resolve("Phidget_setChannel")) == 0 ||
667
+		(p_setTCType = (PhidgetPointerIntIn)driver.resolve("PhidgetTemperatureSensor_setThermocoupleType")) == 0 ||
668
+		(p_setRTDType = (PhidgetPointerIntIn)driver.resolve("PhidgetTemperatureSensor_setRTDType")) == 0 ||
669
+		(p_setRTDWiring = (PhidgetPointerIntIn)driver.resolve("PhidgetTemperatureSensor_setRTDWireSetup")) == 0 ||
670
+		(p_setNewDataCallback = (PhidgetPointerVCPointer)driver.resolve("PhidgetTemperatureSensor_setOnTemperatureChangeHandler")) == 0 ||
671
+		(p_open = (PhidgetPointerIntIn)driver.resolve("Phidget_openWaitForAttachment")) == 0 ||
672
+		(p_close = (PhidgetPointer)driver.resolve("Phidget_close")) == 0 ||
673
+		(p_delete = (PhidgetPointer)driver.resolve("PhidgetTemperatureSensor_delete")) == 0)
674
+	{
675
+		QMessageBox::critical(NULL, tr("Typica: Link error"),
676
+		                      tr("Failed to link a required symbol in phidget22."));
677
+		return;
678
+	}
679
+	for(int i = 0; i < channelConfiguration.length(); i++)
680
+	{
681
+		PhidgetChannelData *c = channelConfiguration.at(i);
682
+		p_createTemperatureSensor(&(c->device));
683
+		p_setSerialNumber(c->device, c->serialNumber);
684
+		p_setChannelNumber(c->device, c->channelNumber);
685
+		switch(c->channelType)
686
+		{
687
+			case 32:
688
+				p_setRTDType(c->device, c->rtdType);
689
+				p_setRTDWiring(c->device, c->wiring);
690
+				break;
691
+			case 33:
692
+				p_setTCType(c->device, c->tcType);
693
+				break;
694
+			default:
695
+				break;
696
+		}
697
+		p_setNewDataCallback(c->device, Phidget22ValueCallback, c->channel);
698
+		p_open(c->device, 5000);
699
+	}
700
+}
701
+
702
+@ New values are delivered to a callback function outside of the class, but
703
+with a pointer to the relevant |Channel| object. This means that all the
704
+callback needs to do is perform the unit conversion, assemble the |Measurement|
705
+and send that out.
706
+
707
+Unfortunately, there can be no guarantee that new measurements will be
708
+available on all channels simultaneously. Hopefully this will not be too
709
+problematic.
710
+
711
+@<Additional functions@>=
712
+void CCONV Phidget22ValueCallback(void *, void *context, double value)
713
+{
714
+	Channel *channel = (Channel*)context;
715
+	QTime time = QTime::currentTime();
716
+	Measurement measure(value * 9.0 / 5.0 + 32.0, time);
717
+	channel->input(measure);
718
+}
719
+
720
+@ A function prototype is provided.
721
+
722
+@<Additional function prototypes@>=
723
+void CCONV Phidget22ValueCallback(void *device, void *context, double value);
724
+
725
+@ When the logging window is closed, it is important to close all open channels
726
+and delete their handles.
727
+
728
+@<Phidget implementation@>=
729
+void Phidget22::stop()
730
+{
731
+	for(int i = 0; i < channelConfiguration.length(); i++)
732
+	{
733
+		PhidgetChannelData *c = channelConfiguration.at(i);
734
+		p_close(c->device);
735
+		p_delete(&(c->device));
736
+	}
737
+}
738
+
481 739
 @ Class implementations are currently folded into typica.cpp.
482 740
 
483 741
 @<Class implementations@>=
484
-@<Phidget implementation@>@;
742
+@<Phidget implementation@>@;
743
+
744
+@ The hardware communications class needs to be available from the host
745
+environment.
746
+
747
+@<Set up the scripting engine@>=
748
+constructor = engine->newFunction(constructPhidget22);
749
+value = engine->newQMetaObject(&Phidget22::staticMetaObject, constructor);
750
+engine->globalObject().setProperty("Phidget22", value);
751
+
752
+@ Two function prototypes are needed.
753
+
754
+@<Function prototypes for scripting@>=
755
+QScriptValue constructPhidget22(QScriptContext *context, QScriptEngine *engine);
756
+QScriptValue Phidget22_getChannel(QScriptContext *context, QScriptEngine *engine);
757
+
758
+@ The constructor is trivial.
759
+
760
+@<Functions for scripting@>=
761
+QScriptValue constructPhidget22(QScriptContext *context, QScriptEngine *engine)
762
+{
763
+	if(context->argumentCount() != 1)
764
+	{
765
+		context->throwError("Incorrect number of arguments");
766
+	}
767
+	QScriptValue object = engine->newQObject(new Phidget22(argument<QModelIndex>(0, context)), QScriptEngine::ScriptOwnership);
768
+	setQObjectProperties(object, engine);
769
+	object.setProperty("getChannel", engine->newFunction(Phidget22_getChannel));
770
+	return object;
771
+}
772
+
773
+@ A wrapper is used for getting channels.
774
+
775
+@<Functions for scripting@>=
776
+QScriptValue Phidget22_getChannel(QScriptContext *context, QScriptEngine *engine)
777
+{
778
+	Phidget22 *self = getself<Phidget22 *>(context);
779
+	QScriptValue object;
780
+	if(self)
781
+	{
782
+		object = engine->newQObject(self->getChannel(argument<int>(0, context)));
783
+		setChannelProperties(object, engine);
784
+	}
785
+	return object;
786
+}

Loading…
Cancel
Save