Writing NeXus files with dynamic components in C++

A similar program can be also written in C++. The main program selclient.cc:

#include <tango.h>
#include <string>
#include <jsoncpp/json/json.h>

#include "nxselwriter.hh"

int main(int argc, char ** argv){

  // tango selector server name
  std::string selectorName("p09/nxsrecselector/hastodt");

  // file name
  std::string fileName("/tmp/selector_3.nxs");

  // timeout for servers in msec
  int timeout = 24000;

  try{
    // create writer
    NXSelWriter wr(selectorName, timeout);
    // open NeXus file
    wr.openFile(fileName);
    // open scan entry
    wr.openEntry();

    // experimental loop
    for(int i =0 ;i<10; i++){
      Json::Value record;
      record["point_nb"] = i;
      wr.loopStep(record);
    }

    // close entry
    wr.closeEntry();
    // close NeXus file
    wr.closeFile();

  }
  catch(Tango::DevFailed &e){
    Tango::Except::print_exception(e);
  }
  catch(std::string &e){
    std::cerr << "ERROR: " << e << std::endl;
  }
}
where we create the file configuration according to the Selector Server setting and writer the NeXus file using the NeXus Writer.

The detail code was closed in the NXSelWriter class. Its header is in the nxselwriter.hh file:

#ifndef NXSELWRITER_HH
#define NXSELWRITER_HH

#include <string>
#include <tango.h>
#include <jsoncpp/json/json.h>

#include <boost/property_tree/ptree.hpp>


class NXSelWriter{

public:
  NXSelWriter(const std::string& selectorName);
  std::string createConfiguration(int timeout=25000);
  void openFile(const std::string & fileName);
  void openEntry(const std::string & xmlconf);
  void loopStep(Json::Value ldata = Json::objectValue);
  void closeEntry();
  void closeFile() const;
  ~NXSelWriter();

protected:
  std::vector<std::string> availableComponents() const;
  std::vector<std::string> selectedComponents() const;
  std::vector<std::string> selectedDataSources() const;
  std::string clientDataSources(const std::vector<std::string>& components) const;
  std::vector<std::string> automaticComponents() const;
  std::vector<std::string> availableDataSources() const;
  Json::Value getValue(std::string device) const;
  bool checkServer(Tango::DeviceProxy & proxy) const;
  void printList(const std::string& label, const std::vector<std::string>& list) const;

  void init(int timeout=25000);
  std::string fetchConfigVariables();
  void removeDynamicComponent();
  void fetchSelection();
  void updateDataSources(const Json::Value mjsds);

private:
  std::string _isoformat = "%Y-%m-%dT%H:%M:%S.%%06u%z";
  std::set <std::string>  _clinitdv;
  std::map <std::string, std::string>  _clstepds;
  std::set <std::string>  _clfinaldv;
  std::string _configName;
  std::string _writerName;
  std::string _selectorName;
  std::string _dynamicCP;
  std::set<std::string> _components;
  Json::Value _dataRecord;
  Json::Reader _jsonReader;
  Json::StyledWriter _jsonWriter;
  Tango::DeviceProxy * _configProxy = 0;
  Tango::DeviceProxy * _writerProxy = 0;
  Tango::DeviceProxy * _selectorProxy = 0;
  std::string _fileName;
  std::string _xml;
  NXSelWriter(const NXSelWriter&);
  NXSelWriter& operator=(const NXSelWriter&);
};

#endif
while an implementation of its methods is in the nxselwriter.cc file:

#include <string>
#include <tango.h>
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>

#include <boost/algorithm/string.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/property_tree/exceptions.hpp>
#include <boost/foreach.hpp>

#include <jsoncpp/json/json.h>

#include "nxselwriter.hh"
#include <tango.h>

using boost::property_tree::ptree;
using boost::property_tree::ptree_error;



NXSelWriter::NXSelWriter(const std::string& selectorName, int timeout):
  _selectorName(selectorName), _timeout(timeout){
}

NXSelWriter::~NXSelWriter(){
  if(_writerProxy){
    delete _writerProxy;
    _writerProxy = NULL;
  }
  if(_selectorProxy){
    delete _writerProxy;
    _writerProxy = NULL;
  }
}


bool NXSelWriter::checkServer(Tango::DeviceProxy& proxy) const{
  bool found = false;
  int cnt = 0;
  int cntmax = 1000;

  Tango::DevState state;
  while (!found && cnt < cntmax){
    if(cnt >1)
      usleep(10);
    try{
      Tango::DeviceData stat = proxy.command_inout("State");
      stat >> state;
      if (state != Tango::RUNNING){
	found = true;
      }
    }
    catch(Tango::DevFailed &e){
      found = false;
      if (cnt == cntmax-1){
      	Tango::Except::print_exception(e);
      }
    }
    catch(...){
      found = false;
    }
  }
  return found;
}


void NXSelWriter::removeDynamicComponent(){
  Tango::DeviceData ndata;
  ndata << _dynamicCP;
  _selectorProxy->command_inout("RemoveDynamicComponent", ndata);
}


std::string NXSelWriter::createConfiguration(){
  Tango::DeviceData arg;
  std::vector<std::string> empty;
  arg << empty;
  Tango::DeviceData ret = _selectorProxy->command_inout("CreateDynamicComponent", arg);
  ret >> _dynamicCP;
  fetchSelection();
  removeDynamicComponent();

  Tango::DeviceData ret1 = _selectorProxy->command_inout("CreateDynamicComponent", arg);
  ret1 >> _dynamicCP;
  _selectorProxy->command_inout("UpdateConfigVariables");

  if(_dynamicCP.size())
    _components.insert(_dynamicCP);
  std::vector<std::string> components = availableComponents();

  // checks if componentNames in ConfigServer components
  for(std::set<std::string>::const_iterator  it = _components.begin();
      it != _components.end(); ++it) {
    if (std::find(components.begin(), components.end(), *it) == components.end()){
      std::stringstream sstream;
      sstream << "Component "<< *it
	      << " not stored in configuration server" << std::endl;
      throw sstream.str();
    }
  }

  Tango::DeviceData mycmps;
  std::vector<std::string> vcomponents(_components.begin(), _components.end());
  mycmps << vcomponents;

  std::string xmlconf;
  Tango::DeviceData xmlAttr = _selectorProxy->command_inout("CreateWriterConfiguration", mycmps);
  xmlAttr >> xmlconf;

  std::cout << "CONFIGURATION: " << xmlconf << std::endl;

  removeDynamicComponent();
  return xmlconf;
}


void NXSelWriter::init(int timeout){

  _selectorProxy = new Tango::DeviceProxy(_selectorName);
  if(!checkServer(*_selectorProxy)){
    std::stringstream sstream;
    sstream  << "Error: Setting up "<< _configName
	     << " takes too long" << std::endl;
    throw sstream.str();
  }
  _selectorProxy->set_timeout_millis(timeout);

  Tango::DeviceAttribute attr;
  attr = _selectorProxy->read_attribute("WriterDevice");
  attr >> _writerName;

  _writerProxy = new Tango::DeviceProxy(_writerName);
  if(!checkServer(*_writerProxy)){
    std::stringstream sstream;
    sstream  << "Error: Setting up "<< _writerName
	     << " takes too long" << std::endl;
    throw sstream.str();
  }
  _writerProxy->set_timeout_millis(timeout);
  _writerProxy->command_inout("Init");

}


void NXSelWriter::printList(const std::string& label,
			    const std::vector<std::string>& list) const{
  std::cout << label;
  for(std::vector<std::string>::const_iterator it = list.begin();
      it != list.end(); ++it) {
    std::cout << (*it) << ' ';
  }
  std::cout << std::endl;

}


void NXSelWriter::fetchSelection(){
  std::vector<std::string> detectors = selectedComponents();
  // detector components
  printList("DETECTORS: ", detectors);


  _components = std::set<std::string>(detectors.begin(), detectors.end());

  std::string datarecord;
  Tango::DeviceAttribute attr;
  attr = _selectorProxy->read_attribute("UserData");
  attr >> datarecord;
  _jsonReader.parse(datarecord, _dataRecord);

  std::vector<std::string> comps;
  std::string jsds = clientDataSources(comps);
  Json::Value mjsds;
  Json::Value mjsds2= Json::arrayValue;
  _jsonReader.parse(jsds, mjsds);

  std::cout <<"DYNAMIC COMPONENT: " << _dynamicCP << std::endl;
  if(_dynamicCP.size()){
    comps.push_back(_dynamicCP);
    jsds = clientDataSources(comps);
    _jsonReader.parse(jsds, mjsds2);
  }
  updateDataSources(mjsds);
  updateDataSources(mjsds2);
}


void NXSelWriter::updateDataSources(const Json::Value mjsds){
  for(Json::ValueIterator itv = mjsds.begin();
       itv != mjsds.end(); ++itv){
    std::string mode = (*itv)["strategy"].asString();
    if(mode == "STEP"){
      _clstepds[(*itv)["record"].asString()] = (*itv)["dsname"].asString();
    }
    else if (mode == "INIT"){
      _clinitdv.insert((*itv)["record"].asString() );
    }
    else if (mode == "FINAL"){
      _clfinaldv.insert((*itv)["record"].asString() );
    }
    std::cout << "STRATEGY: " << (*itv)["strategy"].asString()
      	      << " RECORD: " << (*itv)["record"].asString()
	      << " DS NAME: " << (*itv)["dsname"].asString()  << std::endl;
  }
}


void NXSelWriter::openFile(const std::string & fileName){

  _fileName = fileName;
  init(_timeout);
  Tango::DeviceAttribute da("FileName", _fileName);
  _writerProxy->write_attribute(da);
  _writerProxy->command_inout("OpenFile");
}


std::vector<std::string> NXSelWriter::selectedComponents() const{
  std::vector<std::string> components;
  Tango::DeviceAttribute attr;
  attr = _selectorProxy->read_attribute("Components");
  attr >> components;
  return components;
}


std::string NXSelWriter::clientDataSources(const std::vector<std::string>& components) const{
  std::string datasources;
  Tango::DeviceData comps;
  comps << const_cast< std::vector<std::string>& >(components);
  Tango::DeviceData csources = _selectorProxy->command_inout("ComponentClientSources", comps);
  csources >> datasources;
  return datasources;
}


std::vector<std::string> NXSelWriter::getDataSources() const{
  std::vector<std::string> datasources;
  Tango::DeviceAttribute attr;
  attr = _selectorProxy->read_attribute("DataSources");
  attr >> datasources;
  return datasources;
}


std::vector<std::string> NXSelWriter::availableComponents() const{
  std::vector<std::string> components;
  Tango::DeviceData cmps = _selectorProxy->command_inout("AvailableComponents");
  cmps >> components;
  return components;
}


std::vector<std::string> NXSelWriter::availableDataSources() const{
  std::vector<std::string> datasources;
  Tango::DeviceData dss = _selectorProxy->command_inout("AvailableDataSources");
  dss >> datasources;
  return datasources;
}


void NXSelWriter::openEntry(){
  _xml = createConfiguration();
  Tango::DeviceAttribute xmlSetting("XMLSettings", _xml);
  _writerProxy->write_attribute(xmlSetting);

  char fmt[64], buf[64];
  struct timeval tv;
  struct tm *tm;

  gettimeofday(&tv, NULL);
  if((tm = localtime(&tv.tv_sec)) != NULL){
    std::strftime(fmt, sizeof(fmt), _isoformat.c_str(), tm);
    std::snprintf(buf, sizeof(buf), fmt, tv.tv_usec);
  }

  Json::Value record;
  record["data"] = _dataRecord;
  record["data"]["start_time"] = std::string(buf);

  std::vector<std::string> recordNames = record["data"].getMemberNames();
  std::set<std::string> missing;
  std::set_difference(_clinitdv.begin(), _clinitdv.end(),
		      recordNames.begin(), recordNames.end(),
		      std::inserter(missing, missing.end()));
  if(missing.size()){
      std::stringstream sstream;
      sstream << "Missing INIT CLIENT data: ";
      for(std::set<std::string>::const_iterator it = missing.begin();
	  it != missing.end(); ++it )
	sstream << (*it) << " ";
      throw sstream.str();
  }

  std::string drecord = _jsonWriter.write(record);
  Tango::DeviceAttribute jsonRecord("JSONRecord", drecord);

  std::cout << "GLOBAL CLIENT DATA: " << drecord << std::endl;
  _writerProxy->write_attribute(jsonRecord);

  _writerProxy->command_inout("OpenEntry");

}


Json::Value NXSelWriter::getValue(std::string device) const{
      Tango::DeviceProxy *dp = new Tango::DeviceProxy(device);
      Tango::DeviceAttribute attr;
      attr = dp->read_attribute("Value");
      Tango::AttributeInfoEx info=  dp->get_attribute_config("Value");
      Tango::AttrDataFormat format = info.data_format;
      int ttype = info.data_type;
      Json::Value vl;

      double db;
      float fl;
      bool bl;
      long lg;
      std::string st;
      unsigned long ul;
      std::vector<double> vdb;
      std::vector<float> vfl;
      std::vector<long> vlg;
      std::vector<unsigned long> vul;
      std::vector<std::string> vst;

      if(format == 0){
	if(ttype == Tango::DEV_DOUBLE || ttype == Tango::DEVVAR_DOUBLEARRAY){
	  attr >> db; vl = db;
	}
	else if(ttype == Tango::DEV_FLOAT || ttype == Tango::DEVVAR_FLOATARRAY){
	  attr >> fl; vl = fl;
	}
	else if(ttype == Tango::DEV_BOOLEAN || ttype == Tango::DEVVAR_BOOLEANARRAY){
	  attr >> bl; vl = bl;
	}
	else if(ttype == Tango::DEV_SHORT || ttype == Tango::DEVVAR_SHORTARRAY){
	  attr >> lg; vl = static_cast<short>(lg);
	}
	else if(ttype == Tango::DEV_LONG || ttype == Tango::DEVVAR_LONGARRAY){
	  attr >> lg; vl = static_cast<int>(lg);
	}
	else if(ttype == Tango::DEV_LONG64 || ttype == Tango::DEVVAR_LONG64ARRAY){
	  attr >> lg; vl = static_cast<long long>(lg);
	}
	else if(ttype == Tango::DEV_STRING || ttype == Tango::DEVVAR_STRINGARRAY){
	  attr >> st; vl = st;
	}
	else if(ttype == Tango::DEV_USHORT || ttype == Tango::DEVVAR_USHORTARRAY){
	  attr >> lg; vl = static_cast<unsigned short>(lg);
	}
	else if(ttype == Tango::DEV_ULONG || ttype == Tango::DEVVAR_ULONGARRAY){
	  attr >> lg; vl = static_cast<unsigned int>(lg);
	}
	else if(ttype == Tango::DEV_ULONG64 || ttype == Tango::DEVVAR_ULONG64ARRAY){
	  attr >> ul; vl = static_cast<unsigned long long>(ul);
	}
      }
      else if(format == 1){
	if(ttype == Tango::DEV_DOUBLE || ttype == Tango::DEVVAR_DOUBLEARRAY){
	  attr >> vdb;
	  vl = Json::arrayValue;
	  for(size_t i = 0; i<vdb.size(); i++)
	    vl[int(i)] = vdb[i];
	}
	else if(ttype == Tango::DEV_FLOAT || ttype == Tango::DEVVAR_FLOATARRAY){
	  attr >> vfl;
	  vl = Json::arrayValue;
	  for(size_t i = 0; i<vfl.size(); i++)
	    vl[int(i)] = vfl[i];
	}
	else if(ttype == Tango::DEV_BOOLEAN || ttype == Tango::DEVVAR_BOOLEANARRAY){
	  attr >> vlg;
	  vl = Json::arrayValue;
	  for(size_t i = 0; i<vlg.size(); i++)
	    vl[int(i)] = static_cast<bool>(vlg[i]);
	}
	else if(ttype == Tango::DEV_SHORT || ttype == Tango::DEVVAR_SHORTARRAY){
	  attr >> vlg;
	  vl = Json::arrayValue;
	  for(size_t i = 0; i<vlg.size(); i++)
	    vl[int(i)] = static_cast<short>(vlg[i]);
	}
	else if(ttype == Tango::DEV_LONG || ttype == Tango::DEVVAR_LONGARRAY){
	  attr >> vlg;
	  vl = Json::arrayValue;
	  for(size_t i = 0; i<vlg.size(); i++)
	    vl[int(i)] = static_cast<int>(vlg[i]);
	}
	else if(ttype == Tango::DEV_LONG64 || ttype == Tango::DEVVAR_LONG64ARRAY){
	  attr >> vlg;
	  vl = Json::arrayValue;
	  for(size_t i = 0; i<vlg.size(); i++)
	    vl[int(i)] = static_cast<long long>(vlg[i]);
	}
	else if(ttype == Tango::DEV_STRING || ttype == Tango::DEVVAR_STRINGARRAY){
	  attr >> vst;
	  vl = Json::arrayValue;
	  for(size_t i = 0; i<vst.size(); i++)
	    vl[int(i)] = vst[i];
	}
	else if(ttype == Tango::DEV_USHORT || ttype == Tango::DEVVAR_USHORTARRAY){
	  attr >> vlg;
	  vl = Json::arrayValue;
	  for(size_t i = 0; i<vlg.size(); i++)
	    vl[int(i)] = static_cast<unsigned short>(vlg[i]);
	}
	else if(ttype == Tango::DEV_LONG || ttype == Tango::DEVVAR_ULONGARRAY){
	  attr >> vlg;
	  vl = Json::arrayValue;
	  for(size_t i = 0; i<vlg.size(); i++)
	    vl[int(i)] = static_cast<unsigned int>(vlg[i]);
	}
	else if(ttype == Tango::DEV_ULONG64 || ttype == Tango::DEVVAR_ULONG64ARRAY){
	  attr >> vul;
	  vl = Json::arrayValue;
	  for(size_t i = 0; i<vul.size(); i++)
	    vl[int(i)] = static_cast<unsigned long long>(vul[i]);
	}
      }

      return vl;
}


void NXSelWriter::loopStep(Json::Value ldata){
  Json::Value srecord;
  Json::Value record;
  record["data"] = _dataRecord;
  srecord["data"] = ldata;

  std::vector<std::string> recordNames = record["data"].getMemberNames();
  std::vector<std::string> srecordNames = srecord["data"].getMemberNames();
  std::set<std::string> tmp;
  std::set<std::string> missing;
  for (std::map <std::string, std::string>::iterator mi = _clstepds.begin();
       mi != _clstepds.end(); ++mi)
    missing.insert(mi->first);
  tmp.clear();
  std::set_difference(missing.begin(), missing.end(),
  		      recordNames.begin(), recordNames.end(),
  		      std::inserter(tmp, tmp.end()));
  missing.clear();
  std::set_difference(tmp.begin(), tmp.end(),
  		      srecordNames.begin(), srecordNames.end(),
  		      std::inserter(missing, missing.end()));
  for(std::set<std::string>::const_iterator it = missing.begin();
      it != missing.end(); ++it){
    try{
      Json::Value value = getValue(*it);
      srecord["data"][*it] = value;
    }
    catch(...){
    }
  }

  std::string drecord = _jsonWriter.write(srecord);
  Tango::DeviceAttribute jsonRecord("JSONRecord", drecord);

  std::cout << "LOCAL CLIENT DATA: " << drecord << std::endl;
  _writerProxy->write_attribute(jsonRecord);

  Tango::DeviceData tdata;
  tdata << drecord;
  _writerProxy->command_inout("Record", tdata);
}


void NXSelWriter::closeEntry(){
  char fmt[64], buf[64];
  struct timeval tv;
  struct tm *tm;
  Json::Value record;
  record["data"] = _dataRecord;

  gettimeofday(&tv, NULL);
  if((tm = localtime(&tv.tv_sec)) != NULL){
      std::strftime(fmt, sizeof(fmt), _isoformat.c_str(), tm);
      std::snprintf(buf, sizeof(buf), fmt, tv.tv_usec);
  }
  record["data"]["end_time"] = std::string(buf);

  std::vector<std::string> recordNames = record["data"].getMemberNames();
  std::set<std::string> missing;
  std::set_difference(_clfinaldv.begin(), _clfinaldv.end(),
		      recordNames.begin(), recordNames.end(),
		      std::inserter(missing, missing.end()));
  if(missing.size()){
      std::stringstream sstream;
      sstream << "Missing INIT CLIENT data: ";
      for(std::set<std::string>::const_iterator it = missing.begin();
	  it != missing.end(); ++it)
	sstream << (*it) << " ";
      throw sstream.str();
  }

  std::string drecord = _jsonWriter.write(record);
  Tango::DeviceAttribute jsonRecord("JSONRecord", drecord);

  std::cout << "GLOBAL CLIENT DATA: " << drecord << std::endl;
  _writerProxy->write_attribute(jsonRecord);

  _writerProxy->command_inout("CloseEntry");

}

void NXSelWriter::closeFile() const{
    _writerProxy->command_inout("CloseFile");
}

To compile the program one performs

haspp09# g++ -std=c++0x -I /usr/include/tango/ nxselwriter.cc selclient.cc -ltango -lomniORB4 -lomniDynamic4 -lomnithread -ljsoncpp -o selclient



2019-11-13