|
@@ -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
|
+}
|