File indexing completed on 2024-12-22 04:17:19

0001 /***************************************************************************
0002                           datavector.cpp  -  a vector which gets its data from
0003                           a datasource.
0004                              -------------------
0005     begin                : Fri Sep 22 2000
0006     copyright            : (C) 2000-2015 by C. Barth Netterfield
0007     email                : netterfield@astro.utoronto.ca
0008  ***************************************************************************/
0009 
0010 /***************************************************************************
0011  *                                                                         *
0012  *   This program is free software; you can redistribute it and/or modify  *
0013  *   it under the terms of the GNU General Public License as published by  *
0014  *   the Free Software Foundation; either version 2 of the License, or     *
0015  *   (at your option) any later version.                                   *
0016  *   Permission is granted to link with any opensource library             *
0017  *                                                                         *
0018  ***************************************************************************/
0019 #include "datavector.h"
0020 
0021 #include <assert.h>
0022 #include <math.h>
0023 #include <stdlib.h>
0024 
0025 #include <QDebug>
0026 #include <QXmlStreamWriter>
0027 
0028 #include "datacollection.h"
0029 #include "debug.h"
0030 #include "datasource.h"
0031 #include "math_kst.h"
0032 #include "objectstore.h"
0033 #include "updatemanager.h"
0034 #include "vectorscriptinterface.h"
0035 
0036 // ReqNF <=0 means read from ReqF0 to end of File
0037 // ReqF0 < means start at EndOfFile-ReqNF.
0038 //
0039 // ReqNF      ReqF0      Action
0040 //  < 1        >=0       read from ReqF0 to end of file
0041 //  < 1        < 0       illegal: fixed in checkIntegrity
0042 //    1         ??       illegal: fixed in checkIntegrity
0043 //  > 1        < 0       read the last ReqNF frames from the file
0044 //  > 1        >=0       Read ReqNF frames starting at frame ReqF0
0045 
0046 namespace Kst {
0047 
0048 const QString DataVector::staticTypeString = "Data Vector";
0049 const QString DataVector::staticTypeTag = "datavector";
0050 
0051 const int INVALIDS_PER_RESET = 5;
0052 
0053 DataVector::DataInfo::DataInfo() :
0054     frameCount(-1),
0055     samplesPerFrame(-1)
0056 {
0057 }
0058 
0059 
0060 DataVector::DataInfo::DataInfo(int fc, int spf) :
0061     frameCount(fc),
0062     samplesPerFrame(spf)
0063 {
0064 }
0065 
0066 /** Create a DataVector: raw data from a file */
0067 DataVector::DataVector(ObjectStore *store)
0068 : Vector(store), DataPrimitive(this) {
0069 
0070   _saveable = true;
0071   _numSamples = 0;
0072   _scalars["sum"]->setValue(0.0);
0073   _scalars["sumsquared"]->setValue(0.0);
0074   F0 = NF = 0; // nothing read yet
0075 
0076   N_AveReadBuf = 0;
0077   AveReadBuf = 0L;
0078 
0079   ReqF0 = 0;
0080   ReqNF = -1;
0081   Skip = 1;
0082   DoSkip = false;
0083   DoAve = false;
0084   _invalidCount = 0;
0085 }
0086 
0087 
0088 const QString& DataVector::typeString() const {
0089   return staticTypeString;
0090 }
0091 
0092 
0093 /** return true if it has a valid file and field, or false otherwise */
0094 bool DataVector::isValid() const {
0095   if (dataSource()) {
0096     dataSource()->readLock();
0097     bool rc = dataSource()->vector().isValid(_field);
0098     dataSource()->unlock();
0099     return rc;
0100   }
0101   return false;
0102 }
0103 
0104 
0105 ScriptInterface* DataVector::createScriptInterface() {
0106   return new DataVectorSI(this);
0107 }
0108 
0109 
0110 bool DataVector::checkValidity(const DataSourcePtr& ds) const {
0111   if (ds) {
0112     ds->readLock();
0113     bool rc = ds->vector().isValid(_field);
0114     ds->unlock();
0115     return rc;
0116   }
0117   return false;
0118 }
0119 
0120 void DataVector::change(DataSourcePtr in_file, const QString &in_field,
0121                         int in_f0, int in_n,
0122                         int in_skip, bool in_DoSkip,
0123                         bool in_DoAve) {
0124   Q_ASSERT(myLockStatus() == KstRWLock::WRITELOCKED);
0125 
0126   Skip = in_skip;
0127   DoSkip = in_DoSkip;
0128   DoAve = in_DoAve;
0129   if (DoSkip && Skip < 1) {
0130     Skip = 1;
0131   }
0132 
0133   setDataSource(in_file);
0134   ReqF0 = in_f0;
0135   ReqNF = in_n;
0136   _field = in_field;
0137 
0138   if (dataSource()) {
0139     dataSource()->writeLock();
0140   }
0141   reset();
0142   if (dataSource()) {
0143     dataSource()->unlock();
0144   }
0145 
0146   if (ReqNF <= 0 && ReqF0 < 0) {
0147     ReqF0 = 0;
0148   }
0149   registerChange();
0150 }
0151 
0152 qint64 DataVector::minInputSerial() const {
0153   if (dataSource()) {
0154     return (dataSource()->serial());
0155   }
0156   return LLONG_MAX;
0157 }
0158 
0159 qint64 DataVector::maxInputSerialOfLastChange() const {
0160   if (dataSource()) {
0161     return (dataSource()->serialOfLastChange());
0162   }
0163   return NoInputs;
0164 }
0165 
0166 
0167 void DataVector::changeFile(DataSourcePtr in_file) {
0168   Q_ASSERT(myLockStatus() == KstRWLock::WRITELOCKED);
0169 
0170   if (!in_file) {
0171     Debug::self()->log(tr("Data file for vector %1 was not opened.").arg(Name()), Debug::Warning);
0172   }
0173   setDataSource(in_file);
0174   if (dataSource()) {
0175     dataSource()->writeLock();
0176   }
0177   reset();
0178   if (dataSource()) {
0179     dataSource()->unlock();
0180   }
0181   registerChange();
0182 }
0183 
0184 
0185 void DataVector::changeFrames(int in_f0, int in_n,
0186                               int in_skip, bool in_DoSkip,
0187                               bool in_DoAve) {
0188 
0189   Q_ASSERT(myLockStatus() == KstRWLock::WRITELOCKED);
0190 
0191   if (dataSource()) {
0192     dataSource()->writeLock();
0193   }
0194   reset();
0195   if (dataSource()) {
0196     dataSource()->unlock();
0197   }
0198   Skip = in_skip;
0199   DoSkip = in_DoSkip;
0200   DoAve = in_DoAve;
0201   if (DoSkip && Skip < 1) {
0202     Skip = 1;
0203   }
0204 
0205   ReqF0 = in_f0;
0206   ReqNF = in_n;
0207 
0208   if (ReqNF <= 0 && ReqF0 < 0) {
0209     ReqF0 = 0;
0210   }
0211   registerChange();
0212 }
0213 
0214 
0215 void DataVector::setFromEnd() {
0216   Q_ASSERT(myLockStatus() == KstRWLock::WRITELOCKED);
0217 
0218   ReqF0 = -1;
0219   if (ReqNF < 2) {
0220     ReqNF = numFrames();
0221     if (ReqNF < 2) {
0222       ReqF0 = 0;
0223     }
0224   }
0225   registerChange();
0226 }
0227 
0228 
0229 DataVector::~DataVector() {
0230   if (AveReadBuf) {
0231     free(AveReadBuf);
0232     AveReadBuf = 0L;
0233   }
0234 }
0235 
0236 
0237 bool DataVector::readToEOF() const {
0238   return ReqNF <= 0;
0239 }
0240 
0241 
0242 bool DataVector::countFromEOF() const {
0243   return ReqF0 < 0;
0244 }
0245 
0246 
0247 /** Return Starting Frame of Vector */
0248 int DataVector::startFrame() const {
0249   return F0;
0250 }
0251 
0252 
0253 /** Return frames per skip to read */
0254 int DataVector::skip() const {
0255   return DoSkip ? Skip : 0;
0256 }
0257 
0258 
0259 bool DataVector::doSkip() const {
0260   return DoSkip;
0261 }
0262 
0263 
0264 bool DataVector::doAve() const {
0265   return DoAve;
0266 }
0267 
0268 
0269 /** Return frames held in Vector */
0270 int DataVector::numFrames() const {
0271   return NF;
0272 }
0273 
0274 
0275 int DataVector::reqNumFrames() const {
0276   return ReqNF;
0277 }
0278 
0279 
0280 int DataVector::reqStartFrame() const {
0281   return ReqF0;
0282 }
0283 
0284 
0285 /** Save vector information */
0286 void DataVector::save(QXmlStreamWriter &s) {
0287   if (dataSource()) {
0288     s.writeStartElement("datavector");
0289     saveFilename(s);
0290     s.writeAttribute("field", _field);
0291 
0292     s.writeAttribute("start", QString::number(ReqF0));
0293     s.writeAttribute("count", QString::number(ReqNF));
0294 
0295     if (doSkip()) {
0296       s.writeAttribute("skip", QString::number(Skip));
0297       if (doAve()) {
0298         s.writeAttribute("doAve", "true");
0299       }
0300     } else {
0301       s.writeAttribute("skip", QString::number(-1));
0302       s.writeAttribute("doAve", "false");
0303     }
0304 
0305     s.writeAttribute("startUnits", startUnits());
0306     s.writeAttribute("rangeUnits", rangeUnits());
0307 
0308     saveNameInfo(s, VECTORNUM|SCALARNUM);
0309     s.writeEndElement();
0310   }
0311 }
0312 
0313 
0314 /**
0315  * @brief Generate default label info for axis associated with this vector.
0316  * Use meta-scalars "units" or "quantity" if they are defined.
0317  * Escape special characters in the field name.
0318  *
0319  * @return LabelInfo
0320  **/
0321 
0322 LabelInfo DataVector::labelInfo() const {
0323   LabelInfo label_info;
0324 
0325   if (_fieldStrings.contains("quantity")) {
0326     label_info.quantity = _fieldStrings.value("quantity")->value();
0327     label_info.quantity.replace('[', "\\[").replace(']', "\\]");
0328   } else {
0329     label_info.quantity.clear();
0330   }
0331 
0332   if (_fieldStrings.contains("units")) {
0333     label_info.units = _fieldStrings.value("units")->value();
0334     label_info.units.replace('[', "\\[").replace(']', "\\]");
0335   } else {
0336     label_info.units.clear();
0337   }
0338 
0339   label_info.name = descriptiveName();// _field;
0340 
0341   label_info.file = filename();
0342 
0343   return label_info;
0344 }
0345 
0346 
0347 void DataVector::reset() { // must be called with a lock
0348   Q_ASSERT(myLockStatus() == KstRWLock::WRITELOCKED);
0349 
0350   if (dataSource()) {
0351     SPF = dataInfo(_field).samplesPerFrame;
0352   }
0353   F0 = NF = 0;
0354   resize(0);
0355   _numSamples = 0;
0356   _dirty = true;
0357   _resetFieldMetadata();
0358 
0359   Object::reset();
0360 }
0361 
0362 
0363 bool DataVector::checkIntegrity() {
0364   if (DoSkip && Skip < 1) {
0365     Skip = 1;
0366   }
0367 
0368   if (_dirty) {
0369     reset();
0370   }
0371 
0372   // if the file seems to have shrunk/changed, return false.
0373   // if it has happened several times in a row, assume the
0374   // file has been over-written, and re-read it.
0375   // this is a hack to handle glitchy file system situations.
0376   // TODO: there has to be a better way.
0377   const DataInfo info = dataInfo(_field);
0378   if (dataSource() && (SPF != info.samplesPerFrame || info.frameCount < NF)) {
0379     _invalidCount++;
0380     if (_invalidCount>INVALIDS_PER_RESET) {
0381 
0382       reset();
0383       _invalidCount=0;
0384     }
0385     return false;
0386   }
0387 
0388   // check for illegal NF and F0 values
0389   if (ReqNF < 1 && ReqF0 < 0) {
0390     ReqF0 = 0; // for this illegal case, read the whole file
0391   }
0392 
0393   if (ReqNF == 1) {
0394     ReqNF = 2;
0395   }
0396 
0397   _invalidCount = 0;
0398   return true;
0399 }
0400 
0401 // Some things to consider about the following routine...
0402 // Frames:
0403 //    Some data sources have data divided into frames.  Each field
0404 //    has a fixed number of samples per frame.  For some (eg, ascii files)
0405 //    each frame has 1 sample.  For others (eg, dirfiles) you may have more.
0406 //    Different fields in the same data source may have different samples per frame.
0407 //    Within a data source, it is assumed that the first sample of each frame is
0408 //    simultaneous between fields.
0409 // Last Frame Read:
0410 //    Only read the first sample of the last frame read, in cases where there are more
0411 //    than one sample per frame.   This allows for more sensible association of vectors
0412 //    into curves, when the X and Y vectors have different numbers of samples per frame.
0413 //    The rule is that we assume is that the first sample of each frame is simultaneous.
0414 // Skip reading:  
0415 //    -'Skip' means read 1 sample each 'Skip' frames (not read one sample,
0416 //     then skip 'Skip' samples or something else).
0417 //    -In order that the data are not re-drawn each time a new sample arrives, and to
0418 //     ensure the re-usability (via shifting) of previously read data, and to make a
0419 //     region of data look the same regardless of the chouse of f0, all samples
0420 //     read with skip enabled are read on 'skip boundries'... ie, the first samples of
0421 //     frame 0, Skip, 2*Skip... N*skip, and never M*Skip+1.
0422 
0423 void DataVector::internalUpdate() {
0424   int i, k, shift, n_read=0;
0425   int ave_nread;
0426   int new_f0, new_nf;
0427   bool start_past_eof = false;
0428 
0429   if (dataSource()) {
0430     dataSource()->writeLock();
0431   } else {
0432     return;
0433   }
0434 
0435   const DataInfo info = dataInfo(_field);
0436   if (!checkIntegrity()) {
0437     if (dataSource()) {
0438       dataSource()->unlock();
0439     }
0440     return;
0441   }
0442 
0443   if (DoSkip && Skip < 2 && SPF == 1) {
0444     DoSkip = false;
0445   }
0446 
0447 
0448   // set new_nf and new_f0
0449   int fc = info.frameCount;
0450   if (ReqNF < 1) { // read to end of file
0451     new_f0 = ReqF0;
0452     new_nf = fc - new_f0;
0453   } else if (ReqF0 < 0) { // count back from end of file
0454     new_nf = fc;
0455     if (new_nf > ReqNF) {
0456       new_nf = ReqNF;
0457     }
0458     new_f0 = fc - new_nf;
0459   } else {
0460     new_f0 = ReqF0;
0461     new_nf = ReqNF;
0462     if (new_f0 + new_nf > fc) {
0463       new_nf = fc - new_f0;
0464     }
0465     if (new_nf <= 0) {
0466       // Tried to read starting past the end.
0467       new_f0 = 0;
0468       new_nf = 1;
0469       start_past_eof = true;
0470     }
0471   }
0472 
0473   if (DoSkip) {
0474     // in count from end mode, change new_f0 and new_nf so they both lie on skip boundaries
0475     if ((new_f0 != 0) && (ReqF0<0)) {
0476       new_f0 = ((new_f0-1)/Skip+1)*Skip;
0477     }
0478     new_nf = (new_nf/Skip)*Skip;
0479   }
0480 
0481   // shift vector if necessary
0482   if (new_f0 < F0 || new_f0 >= F0 + NF) { // No useful data around.
0483     reset();
0484   } else { // shift stuff rather than re-read
0485     if (DoSkip) {
0486       shift = (new_f0 - F0)/Skip;
0487       NF -= (new_f0 - F0);
0488       _numSamples = NF/Skip;
0489     } else {
0490       shift = SPF*(new_f0 - F0);
0491       NF -= (new_f0 - F0);
0492       //_numSamples = (NF-1)*SPF;
0493       if (shift > 0) {
0494         if (SPF == 1) {
0495           _numSamples = NF;
0496         } else {
0497           _numSamples = (NF-1)*SPF;
0498         }
0499       }
0500     }
0501 
0502     memmove(_v_raw, _v_raw+shift, _numSamples*sizeof(double));
0503   }
0504 
0505   if (DoSkip) {
0506     // reallocate V if necessary
0507     if (new_nf / Skip != _size) {
0508       if (! resize(new_nf/Skip)) {
0509         // TODO: Is aborting all we can do?
0510         fatalError("Not enough memory for vector data");
0511         return;
0512       }
0513     }
0514     n_read = 0;
0515     /** read each sample from the File */
0516     double *t = _v_raw + _numSamples;
0517     int new_nf_Skip = new_nf - Skip;
0518     if (DoAve) {
0519       for (i = NF; new_nf_Skip >= i; i += Skip) {
0520         /* enlarge AveReadBuf if necessary */
0521         if (N_AveReadBuf < Skip*SPF) {
0522           N_AveReadBuf = Skip*SPF;
0523           if (!kstrealloc(AveReadBuf, N_AveReadBuf*sizeof(double))) {
0524             qCritical() << "Vector resize failed";
0525           }
0526           if (!AveReadBuf) {
0527             // FIXME: handle failed resize
0528           }
0529         }
0530         ave_nread = readField(AveReadBuf, _field, new_f0+i, Skip);
0531         for (k = 1; k < ave_nread; ++k) {
0532           AveReadBuf[0] += AveReadBuf[k];
0533         }
0534         if (ave_nread > 0) {
0535           *t = AveReadBuf[0]/double(ave_nread);
0536           n_read++;
0537         }
0538         ++t;
0539       }
0540     } else {
0541       for (i = NF; new_nf_Skip >= i; i += Skip) {
0542         n_read += readField(t++, _field, new_f0 + i, -1);
0543       }
0544     }
0545   } else {
0546     // reallocate V if necessary
0547     if ((new_nf - 1)*SPF + 1 != _size) {
0548       if (!resize((new_nf - 1)*SPF + 1)) {
0549         // TODO: Is aborting all we can do?
0550         fatalError("Not enough memory for vector data");
0551         return;
0552       }
0553     }
0554 
0555     // read the new data from file
0556     if (start_past_eof) {
0557       _v_raw[0] = NOPOINT;
0558       n_read = 1;
0559     } else if (info.samplesPerFrame > 1) {
0560       if (NF>0) {
0561         NF--;  /* last frame read was only partially read... */
0562       }
0563       int safe_nf = (new_nf>0 ? new_nf : 0);
0564 
0565       assert(new_f0 + NF >= 0);
0566       //assert(new_f0 + safe_nf - 1 >= 0);
0567       if ((new_f0 + safe_nf - 1 >= 0) && (safe_nf - NF > 1)) {
0568         n_read = readField(_v_raw+NF*SPF, _field, new_f0 + NF, safe_nf - NF - 1);
0569         n_read += readField(_v_raw+(safe_nf-1)*SPF, _field, new_f0 + safe_nf - 1, -1);
0570       }
0571     } else {
0572       assert(new_f0 + NF >= 0);
0573       if (new_nf - NF > 0 || new_nf - NF == -1) {
0574         n_read = readField(_v_raw+NF*SPF, _field, new_f0 + NF, new_nf - NF);
0575       }
0576     }
0577   }
0578   _numNew = _size - _numSamples;
0579   NF = new_nf;
0580   F0 = new_f0;
0581   _numSamples += n_read;
0582 
0583   // if for some reason (eg, realtime reading an nfs mounted
0584   // dirfile) not all of the data was read, the data will never
0585   // be read; the samples will be filled in with the last data
0586   // point read, and a complete reset will be requested.
0587   // This is bad - I think it will be worthwhile
0588   // to add blocking w/ timeout to KstFile.
0589   // As a first fix, mount all nsf mounted dirfiles with "-o noac"
0590   _dirty = false;
0591   if (_numSamples != _size && !(_numSamples == 0 && _size == 1)) {
0592     _dirty = true;
0593     for (i = _numSamples; i < _size; ++i) {
0594       _v_raw[i] = _v_raw[0];
0595     }
0596   }
0597 
0598   if (_numNew > _size) {
0599     _numNew = _size;
0600   }
0601   if (_numShifted > _size) {
0602     _numShifted = _size;
0603   }
0604 
0605   if (dataSource()) {
0606     dataSource()->unlock();
0607   }
0608 
0609   if (n_read>0) {
0610     Vector::internalUpdate();
0611   }
0612 }
0613 
0614 QByteArray DataVector::scriptInterface(QList<QByteArray> &c)
0615 {
0616   Q_ASSERT(c.size());
0617   if(c[0]=="changeFrames") {
0618     if(c.size()!=6) {
0619       return QByteArray("Bad parameter count (!=5).");
0620     }
0621     changeFrames(c[1].toInt(),c[2].toInt(),c[3].toInt(),(c[4]=="true")?1:0,(c[5]=="true")?1:0);
0622     return QByteArray("Done.");
0623   } else if(c[0]=="numFrames") {
0624     return QByteArray::number(numFrames());
0625   } else if(c[0]=="startFrame") {
0626     return QByteArray::number(startFrame());
0627   } else if(c[0]=="doSkip") {
0628     return QByteArray(doSkip()?"true":"false");
0629   } else if(c[0]=="doAve") {
0630     return QByteArray(doAve()?"true":"false");
0631   } else if(c[0]=="skip") {
0632     return QByteArray::number(skip());
0633   } else if(c[0]=="reload") {
0634     reload();
0635     return QByteArray("Done");
0636   } else if(c[0]=="samplesPerFrame") {
0637     return QByteArray::number(samplesPerFrame());
0638   } else if(c[0]=="fileLength") {
0639     return QByteArray::number(fileLength());
0640   } else if(c[0]=="readToEOF") {
0641     return QByteArray(readToEOF()?"true":"false");
0642   } else if(c[0]=="countFromEOF") {
0643     return QByteArray(countFromEOF()?"true":"false");
0644   } else if(c[0]=="descriptionTip") {
0645     return QByteArray(descriptionTip().toLatin1());
0646   } else if(c[0]=="isValid") {
0647     return isValid()?"true":"false";
0648   }
0649 
0650   return "No such command...";
0651 }
0652 
0653 
0654 /** Returns intrinsic samples per frame */
0655 int DataVector::samplesPerFrame() const {
0656   return SPF;
0657 }
0658 
0659 
0660 int DataVector::fileLength() const {
0661 
0662   if (dataSource()) {
0663     int rc = dataInfo(_field).frameCount;
0664 
0665     return rc;
0666   }
0667 
0668   return 0;
0669 }
0670 
0671 
0672 void DataVector::reload() {
0673   Q_ASSERT(myLockStatus() == KstRWLock::WRITELOCKED);
0674 
0675   if (dataSource()) {
0676     dataSource()->writeLock();
0677     dataSource()->reset();
0678     dataSource()->unlock();
0679     reset();
0680     _resetFieldMetadata();
0681     registerChange();
0682   }
0683 }
0684 
0685 void DataVector::_resetFieldMetadata() {
0686   _resetFieldScalars();
0687   _resetFieldStrings();
0688 }
0689 
0690 void DataVector::_resetFieldStrings() {
0691   const QMap<QString, QString> meta_strings = dataSource()->vector().metaStrings(_field);
0692   
0693   QStringList fieldStringKeys = _fieldStrings.keys();
0694   // remove field strings that no longer need to exist
0695   readLock();
0696   for (int i=0; i<fieldStringKeys.count(); ++i) {
0697     QString key = fieldStringKeys.at(i);
0698     if (!meta_strings.contains(key)) {
0699       StringPtr sp = _fieldStrings[key];
0700       _strings.remove(key);
0701       _fieldStrings.remove(key);
0702       sp = 0L;
0703     }
0704   }
0705   // find or insert strings, to set their value
0706   QMapIterator<QString, QString> it(meta_strings);
0707   while (it.hasNext()) {
0708     it.next();
0709     QString key = it.key();
0710     StringPtr sp;
0711     if (!_fieldStrings.contains(key)) { // insert a new one
0712       _strings.insert(key, sp = store()->createObject<String>());
0713       _fieldStrings.insert(key, sp);
0714       sp->setProvider(this);
0715       sp->setSlaveName(key);
0716     } else {  // find it
0717       sp = _fieldStrings[key];
0718     }
0719     sp->setValue(it.value());
0720   }
0721   unlock();
0722 }
0723 
0724 
0725 void DataVector::_resetFieldScalars() {
0726   const QMap<QString, double> meta_scalars = dataSource()->vector().metaScalars(_field);
0727 
0728 
0729   QStringList fieldScalarKeys = _fieldScalars.keys();
0730   // remove field scalars that no longer need to exist
0731   readLock();
0732   for (int i=0; i<fieldScalarKeys.count(); ++i) {
0733     QString key = fieldScalarKeys.at(i);
0734     if (!meta_scalars.contains(key)) {
0735       ScalarPtr sp = _fieldScalars[key];
0736       _scalars.remove(key);
0737       _fieldScalars.remove(key);
0738       sp = 0L;
0739     }
0740   }
0741   // find or insert scalars, to set their value
0742   QMapIterator<QString, double> it(meta_scalars);
0743   while (it.hasNext()) {
0744     it.next();
0745     QString key = it.key();
0746     ScalarPtr sp;
0747     if (!_fieldScalars.contains(key)) { // insert a new one
0748       _scalars.insert(key, sp = store()->createObject<Scalar>());
0749       _fieldScalars.insert(key, sp);
0750       sp->setProvider(this);
0751       sp->setSlaveName(key);
0752     } else {  // find it
0753       sp = _fieldScalars[key];
0754     }
0755     sp->setValue(it.value());
0756   }
0757   unlock();
0758 }
0759 
0760 
0761 PrimitivePtr DataVector::makeDuplicate() const {
0762   Q_ASSERT(store());
0763   DataVectorPtr vector = store()->createObject<DataVector>();
0764 
0765   vector->writeLock();
0766   vector->change(dataSource(), _field, ReqF0, ReqNF, Skip, DoSkip, DoAve);
0767   if (descriptiveNameIsManual()) {
0768     vector->setDescriptiveName(descriptiveName());
0769   }
0770 
0771   vector->registerChange();
0772   vector->unlock();
0773 
0774   return kst_cast<Primitive>(vector);
0775 }
0776 
0777 QString DataVector::_automaticDescriptiveName() const {
0778   QString name;
0779   name = _field;
0780   // un-escape escaped special characters so they aren't escaped 2x.
0781   name.replace("\\_", "_").replace("\\^","^").replace("\\[", "[").replace("\\]", "]");
0782   // now escape the special characters.
0783   name.replace('_', "\\_").replace('^', "\\^").replace('[', "\\[").replace(']', "\\]");
0784   return name;
0785 }
0786 
0787 QString DataVector::descriptionTip() const {
0788   QString IDstring;
0789   //QString range_string;
0790 
0791   if (dataSource()) {
0792     IDstring = tr(
0793                  "Data Vector: %1\n"
0794                  "  %2\n"
0795                  "  Field: %3"
0796                  ).arg(Name()).arg(dataSource()->fileName()).arg(_field);
0797     if (countFromEOF()) {
0798       IDstring += tr("\n  Last %1 frames.").arg(numFrames());
0799     } else if (readToEOF()) {
0800       IDstring += tr("\n  Frame %1 to end.").arg(startFrame());
0801     } else {
0802       IDstring += tr("\n  %1 Frames starting at %2.").arg(numFrames()).arg(startFrame());
0803     }
0804     if (skip()) {
0805       if (!doAve()) {
0806         IDstring+=tr("\n  Read 1 sample per %1 frames.").arg(skip());
0807       } else {
0808         IDstring+=tr("\n  Average each %1 frames.").arg(skip());
0809       }
0810     }
0811   } else{
0812     IDstring.clear();
0813   }
0814 
0815   return IDstring;
0816 }
0817 
0818 QString DataVector::propertyString() const {
0819   return tr("%2 F0: %3 N: %4 of %1", "%2 is a variable name.  F0 is short for the first element.  N is the number of elements").arg(dataSource()->fileName()).arg(_field).arg(startFrame()).arg(numFrames());
0820 }
0821 
0822 
0823 int DataVector::readField(double *v, const QString& field, int s, int n, int skip)
0824 {
0825   ReadInfo par = {v, s, n, skip};
0826   return dataSource()->vector().read(field, par);
0827 }
0828 
0829 const DataVector::DataInfo DataVector::dataInfo(const QString& field) const
0830 {
0831   dataSource()->readLock();
0832   const DataInfo info = dataSource()->vector().dataInfo(field);
0833   dataSource()->unlock();
0834   return info;
0835 }
0836 
0837 bool DataVector::isTime() const {
0838   return dataSource()->isTime(_field);
0839 }
0840 
0841 }
0842 // vim: ts=2 sw=2 et