ISIS Logo
lvDCOM
An EPICS support module to export LabVIEW values as process variables
lvDCOMInterface.cpp
Go to the documentation of this file.
1 /*************************************************************************\
2 * Copyright (c) 2013 Science and Technology Facilities Council (STFC), GB.
3 * All rights reverved.
4 * This file is distributed subject to a Software License Agreement found
5 * in the file LICENSE.txt that is included with this distribution.
6 \*************************************************************************/
7 
10 
16 
21 
28 
31 
35 
38 
41 
44 
47 
50 
53 
56 
59 
60 #include <stdio.h>
61 
62 //#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
63 #include <windows.h>
64 
65 #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit
66 #include <atlbase.h>
67 #include <atlstr.h>
68 #include <atlcom.h>
69 #include <atlwin.h>
70 #include <atltypes.h>
71 #include <atlctl.h>
72 #include <atlhost.h>
73 #include <atlconv.h>
74 #include <atlsafe.h>
75 #include <comdef.h>
76 #include <tlhelp32.h>
77 
78 #include <string>
79 #include <vector>
80 #include <map>
81 #include <list>
82 #include <stdexcept>
83 #include <sstream>
84 #include <fstream>
85 #include <iostream>
86 #include <algorithm>
87 
88 #include "lvDCOMInterface.h"
89 #include "variant_utils.h"
90 
91 #include <macLib.h>
92 #include <epicsGuard.h>
93 #include <cantProceed.h>
94 #include <errlog.h>
95 
96 #if WITH_PCRE
97 #include <pcrecpp.h>
98 #else
99 namespace pcrecpp
101 {
102  struct RE
103  {
104  RE(const char*) { }
105  bool FullMatch(const char*) { return true; }
106  bool GlobalReplace(const std::string, std::string*) { return true; }
107  };
108 }
109 #endif
110 
111 // replace characters invalid in XML
112 static std::string replaceWithEntities(const std::string& str)
113 {
114  std::string res(str);
115  pcrecpp::RE("&").GlobalReplace("&amp;", &res);
116  pcrecpp::RE("<").GlobalReplace("&lt;", &res);
117  pcrecpp::RE(">").GlobalReplace("&gt;", &res);
118  pcrecpp::RE("\"").GlobalReplace("&quot;", &res);
119  pcrecpp::RE("'").GlobalReplace("&apos;", &res);
120  return res;
121 }
122 
123 #define MAX_PATH_LEN 256
124 
125 static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT;
126 
128 static void __stdcall my_com_raise_error(HRESULT hr, IErrorInfo* perrinfo)
129 {
130  _com_error com_error(hr, perrinfo);
131  // std::string message = "(" + com_error.Source() + ") " + com_error.Description();
132  _bstr_t desc = com_error.Description();
133  std::string message = (desc.GetBSTR() != NULL ? desc : ""); // for LabVIEW generated messages, Description() already includes Source()
134  throw COMexception(message, hr);
135 }
136 
137 static void initCOM(void*)
138 {
139  CoInitializeEx(NULL, COINIT_MULTITHREADED);
140  _set_com_error_handler(my_com_raise_error); // replace default _com_raise_error
141 }
142 
145 char* lvDCOMInterface::envExpand(const char *str)
146 {
147  long destCapacity = 128;
148  char *dest = NULL;
149  int n;
150  do {
151  destCapacity *= 2;
152  /*
153  * Use free/malloc rather than realloc since there's no need to
154  * keep the original contents.
155  */
156  free(dest);
157  dest = static_cast<char*>(mallocMustSucceed(destCapacity, "lvDCOMInterface::envExpand"));
158  n = macExpandString(m_mac_env, str, dest, destCapacity);
159  } while (n >= (destCapacity - 1));
160  if (n < 0) {
161  free(dest);
162  dest = NULL;
163  } else {
164  size_t unused = destCapacity - ++n;
165 
166  if (unused >= 20)
167  dest = static_cast<char*>(realloc(dest, n));
168  }
169  return dest;
170 }
171 
172 // return "" if no value at path
173 std::string lvDCOMInterface::doXPATH(const std::string& xpath)
174 {
175  if (m_pxmldom == NULL)
176  {
177  throw std::runtime_error("m_pxmldom is NULL");
178  }
179  epicsGuard<epicsMutex> _lock(m_lock);
180  std::map<std::string,std::string>::const_iterator it = m_xpath_map.find(xpath);
181  if (it != m_xpath_map.end())
182  {
183  return it->second;
184  }
185  IXMLDOMNode *pNode = NULL;
186  std::string S_res;
187  BSTR bstrValue = NULL;
188  HRESULT hr = m_pxmldom->selectSingleNode(_bstr_t(xpath.c_str()), &pNode);
189  if (SUCCEEDED(hr) && pNode != NULL)
190  {
191  hr=pNode->get_text(&bstrValue);
192  if (SUCCEEDED(hr))
193  {
194  S_res = envExpand(CW2CT(bstrValue));
195  SysFreeString(bstrValue);
196  }
197  pNode->Release();
198  }
199  // else
200  // {
201  // throw std::runtime_error("doXPATH: cannot find " + xpath);
202  // }
203  m_xpath_map[xpath] = S_res;
204  return S_res;
205 }
206 
207 bool lvDCOMInterface::doXPATHbool(const std::string& xpath)
208 {
209  if (m_pxmldom == NULL)
210  {
211  throw std::runtime_error("m_pxmldom is NULL");
212  }
213  epicsGuard<epicsMutex> _lock(m_lock);
214  std::map<std::string,bool>::const_iterator it = m_xpath_bool_map.find(xpath);
215  if (it != m_xpath_bool_map.end())
216  {
217  return it->second;
218  }
219  IXMLDOMNode *pNode = NULL;
220  bool res = false;
221  BSTR bstrValue = NULL;
222  std::string bool_str;
223  HRESULT hr = m_pxmldom->selectSingleNode(_bstr_t(xpath.c_str()), &pNode);
224  if (SUCCEEDED(hr) && pNode != NULL)
225  {
226  hr=pNode->get_text(&bstrValue);
227  if (SUCCEEDED(hr))
228  {
229  bool_str = envExpand(CW2CT(bstrValue));
230  if (bool_str.size() == 0)
231  {
232  res = false;
233  }
234  // allow true / yes / non_zero_number
235  // note: atol() returns 0 for non numeric strings, so OK in a test for "true"
236  else if ( (bool_str[0] == 't') || (bool_str[0] == 'T') || (bool_str[0] == 'y') || (bool_str[0] == 'Y') || (atol(bool_str.c_str()) != 0) )
237  {
238  res = true;
239  }
240  else
241  {
242  res = false;
243  }
244  SysFreeString(bstrValue);
245  }
246  pNode->Release();
247  }
248  // else
249  // {
250  // throw std::runtime_error("doXPATHbool: cannot find " + xpath);
251  // }
252  m_xpath_bool_map[xpath] = res;
253  return res;
254 }
255 
256 #if 0
257 
258 std::string lvDCOMInterface::doXPATH_old(const std::string& xpath)
259 {
260  if (m_doc == NULL)
261  {
262  throw std::runtime_error("m_cfg is NULL");
263  }
264  std::map<std::string,std::string>::const_iterator it = m_xpath_map.find(xpath);
265  if (it != m_xpath_map.end())
266  {
267  return it->second;
268  }
269  m_lock.lock();
270  TinyXPath::xpath_processor xp_proc(m_root, xpath.c_str());
271  // TIXML_STRING S_res = TinyXPath::S_xpath_string(m_doc->RootElement(), xpath.c_str());
272  std::string S_res = xp_proc.S_compute_xpath().c_str();
273  m_xpath_map[xpath] = S_res;
274  m_lock.unlock();
275  return S_res;
276 }
277 
278 bool lvDCOMInterface::doXPATHbool_old(const std::string& xpath)
279 {
280  if (m_doc == NULL)
281  {
282  throw std::runtime_error("m_cfg is NULL");
283  }
284  std::map<std::string,bool>::const_iterator it = m_xpath_bool_map.find(xpath);
285  if (it != m_xpath_bool_map.end())
286  {
287  return it->second;
288  }
289  m_lock.lock();
290  TinyXPath::xpath_processor xp_proc(m_root, xpath.c_str());
291  bool res = xp_proc.o_compute_xpath();
292  m_xpath_bool_map[xpath] = res;
293  m_lock.unlock();
294  return res;
295 }
296 
297 #endif /* #if 0 */
298 
299 std::string lvDCOMInterface::doPath(const std::string& xpath)
300 {
301  std::string S_res = doXPATH(xpath);
302  std::replace(S_res.begin(), S_res.end(), '/', '\\');
303  return S_res;
304 }
305 
307 {
308  m_pxmldom = NULL;
309  HRESULT hr=CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_SERVER,
310  IID_IXMLDOMDocument2, (void**)&m_pxmldom);
311  if (FAILED(hr))
312  {
313  throw std::runtime_error("Cannot load DomFromCom");
314  }
315  if (m_pxmldom != NULL)
316  {
317  m_pxmldom->put_async(VARIANT_FALSE);
318  m_pxmldom->put_validateOnParse(VARIANT_FALSE);
319  m_pxmldom->put_resolveExternals(VARIANT_FALSE);
320  }
321  else
322  {
323  throw std::runtime_error("Cannot load DomFromCom");
324  }
325 }
326 
327 
335 lvDCOMInterface::lvDCOMInterface(const char *configSection, const char* configFile, const char* host, int options, const char* progid, const char* username, const char* password) :
336 m_configSection(configSection), m_pidentity(NULL), m_pxmldom(NULL), m_options(options),
337  m_progid(progid != NULL? progid : ""), m_username(username != NULL? username : ""), m_password(password != NULL ? password : ""),
338  m_mac_env(NULL)
339 
340 {
341  epicsThreadOnce(&onceId, initCOM, NULL);
342  if (host != NULL && host[0] != '\0')
343  {
344  m_host = host;
345  }
346  else
347  {
348  // char name_buffer[MAX_COMPUTERNAME_LENGTH + 1];
349  // DWORD name_size = MAX_COMPUTERNAME_LENGTH + 1;
350  // if ( GetComputerNameEx(ComputerNameNetBIOS, name_buffer, &name_size) != 0 )
351  // {
352  // m_host = name_buffer;
353  // }
354  // else
355  // {
356  // m_host = "localhost";
357  // }
358  m_host = "localhost";
359  }
360  if (macCreateHandle(&m_mac_env, NULL) != 0)
361  {
362  throw std::runtime_error("Cannot create mac handle");
363  }
364  // load current environment into m_mac_env, this is so we can create a macEnvExpand() equivalent
365  // but tied to the environment at a specific time. It is useful if we want to load the same
366  // XML file twice but with a macro defined differently in each case
367  for(char** cp = environ; *cp != NULL; ++cp)
368  {
369  char* str_tmp = strdup(*cp);
370  char* equals_loc = strchr(str_tmp, '='); // split name=value string
371  if (equals_loc != NULL)
372  {
373  *equals_loc = '\0';
374  macPutValue(m_mac_env, str_tmp, equals_loc + 1);
375  }
376  free(str_tmp);
377  }
378  // m_doc = new TiXmlDocument;
379  // if ( !m_doc->LoadFile(configFile) )
380  // {
381  // delete m_doc;
382  // m_doc = NULL;
383  // throw std::runtime_error("Cannot load " + std::string(configFile) + ": load failure");
384  // }
385  // m_root = m_doc->RootElement();
386  if (configFile != NULL && *configFile != '\0')
387  {
388  DomFromCOM();
389  short sResult = FALSE;
390  char* configFile_expanded = envExpand(configFile);
391  m_configFile = configFile_expanded;
392  HRESULT hr = m_pxmldom->load(_variant_t(configFile_expanded), &sResult);
393  free(configFile_expanded);
394  if(FAILED(hr))
395  {
396  throw std::runtime_error("Cannot load XML \"" + m_configFile + "\" (expanded from \"" + std::string(configFile) + "\"): load failure");
397  }
398  if (sResult != VARIANT_TRUE)
399  {
400  throw std::runtime_error("Cannot load XML \"" + m_configFile + "\" (expanded from \"" + std::string(configFile) + "\"): load failure");
401  }
402  std::cerr << "Loaded XML config file \"" << m_configFile << "\" (expanded from \"" << configFile << "\")" << std::endl;
403  m_extint = doPath("/lvinput/extint/@path").c_str();
404  }
405  epicsAtExit(epicsExitFunc, this);
406  if (m_progid.size() > 0)
407  {
408  if ( CLSIDFromProgID(CT2W(m_progid.c_str()), &m_clsid) != S_OK )
409  {
410  throw std::runtime_error("Cannot find progId " + m_progid);
411  }
412  }
413  else
414  {
416  wchar_t* progid_str = NULL;
417  if ( ProgIDFromCLSID(m_clsid, &progid_str) == S_OK )
418  {
419  m_progid = CW2CT(progid_str);
420  CoTaskMemFree(progid_str);
421  }
422  else
423  {
424  m_progid = "LabVIEW.Application";
425  }
426  }
427  wchar_t* clsid_str = NULL;
428  if ( StringFromCLSID(m_clsid, &clsid_str) == S_OK )
429  {
430  std::cerr << "Using ProgID \"" << m_progid << "\" CLSID " << CW2CT(clsid_str) << std::endl;
431  CoTaskMemFree(clsid_str);
432  }
433  else
434  {
435  std::cerr << "Using ProgID \"" << m_progid << "\" but StringFromCLSID() failed" << std::endl;
436  }
437  if ( checkOption(lvNoStart) )
438  {
439  waitForLabVIEW();
440  }
441 }
442 
444 {
445  lvDCOMInterface* dcomint = static_cast<lvDCOMInterface*>(arg);
446  if (dcomint == NULL)
447  {
448  return;
449  }
450  if ( dcomint->checkOption(viAlwaysStopOnExit) )
451  {
452  dcomint->stopVis(false);
453  }
454  else if ( dcomint->checkOption(viStopOnExitIfStarted) )
455  {
456  dcomint->stopVis(true);
457  }
458 }
459 
462 {
463  double lvuptime = getLabviewUptime();
464  if (lvuptime < m_minLVUptime)
465  {
466  errlogSevPrintf(errlogMinor, "LabVIEW not currently running - waiting for LabVIEW uptime of %.1f seconds...\n", m_minLVUptime);
467  while ( (lvuptime = getLabviewUptime()) < m_minLVUptime )
468  {
469  epicsThreadSleep(5.0);
470  }
471  }
472  return lvuptime;
473 }
474 
475 void lvDCOMInterface::getBlockDetails(std::vector< std::vector<std::string> >& values)
476 {
477  CComBSTR vi_name("c:\\LabVIEW Modules\\dae\\monitor\\dae_monitor.vi");
478  CComBSTR control_name("Parameter details");
479  int n, nr, nc;
480 
481  waitForLabVIEW();
482  // wait until table populated i.e. non zero number of rows, also non-black first block name
483  do {
484  epicsThreadSleep(5.0);
485  CComVariant v;
486  getLabviewValue(vi_name, control_name, &v);
487  if ( v.vt != (VT_ARRAY | VT_BSTR) )
488  {
489  throw std::runtime_error("GetBlockDetails failed (type mismatch)");
490  }
491  n = nr = nc = 0;
492  values.clear();
493  CComSafeArray<BSTR> sa;
494  sa.Attach(v.parray);
495  if (sa.GetDimensions() == 2)
496  {
497  nr = sa.GetCount(0);
498  nc = sa.GetCount(1);
499  if (nc > 5)
500  {
501  nc = 5; // we only want (and use) the first 5 columns
502  }
503  values.resize(nr);
504  for(int i=0; i<nr; ++i)
505  {
506  values[i].clear();
507  values[i].reserve(nc);
508  for(int j=0; j<nc; ++j)
509  {
510  BSTR t = NULL;
511  LONG dims[2] = { i, j };
512  if (sa.MultiDimGetAt(dims, t) == S_OK)
513  {
514  values[i].push_back(static_cast<const char*>(CW2CT(t)));
515  SysFreeString(t);
516  ++n;
517  }
518  }
519  }
520  }
521  sa.Detach();
522  } while ( (nr * nc) == 0 || (nr * nc) != n || values[0][0].size() == 0 );
523 }
524 
526 {
527  if ( !(m_options & static_cast<int>(lvDCOMOptions::lvSECIConfig)) )
528  {
529  return false;
530  }
531  std::vector< std::vector<std::string> > values;
532  getBlockDetails(values);
533  return (m_seci_values == values ? false : true);
534 }
535 
537 int lvDCOMInterface::generateFilesFromSECI(const char* portName, const char* macros, const char* configSection, const char* configFile, const char* dbSubFile, const char* blocks_match, bool no_setter)
538 {
539  double lvuptime = waitForLabVIEW();
540  errlogSevPrintf(errlogInfo, "LabVIEW has been running for %.1f seconds\n", lvuptime);
541  std::cerr << "Waiting for block details to appear in dae_monitor.vi ..." << std::endl;
543  int nr = m_seci_values.size();
544  std::cerr << "Found " << nr << " potential SECI blocks" << std::endl;
545  char** pairs = NULL;
546  macPushScope(m_mac_env);
547  macParseDefns(m_mac_env, macros, &pairs);
548  macInstallMacros(m_mac_env, pairs);
549 
550  std::fstream fs, fsdb;
551  fs.open(configFile, std::ios::out);
552  fsdb.open(dbSubFile, std::ios::out);
553  fs << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
554  fs << "<lvinput xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://epics.isis.rl.ac.uk/lvDCOMinput/1.0\" xsi:schemaLocation=\"http://epics.isis.rl.ac.uk/lvDCOMinput/1.0 lvDCOMinput.xsd\">\n";
555 // fs << " <extint path=\"$(LVDCOM)/lvDCOMApp/src/extint/Main/Library/External Interface - Set Value.vi\"/>\n";
556  // SECI instruments are already using this, but from a different source
557  fs << " <extint path=\"c:/labview modules/Common/External Interface/External Interface.llb/External Interface - Set Value.vi\"/>\n";
558  fs << " <section name=\"" << configSection << "\">\n";
559  if (blocks_match == NULL || *blocks_match == '\0')
560  {
561  blocks_match = ".*";
562  }
563  pcrecpp::RE blocks_re(blocks_match);
564  int nblocks = 0;
565  for(int i=0; i<nr; ++i)
566  {
567  std::string rsuffix, ssuffix, pv_type;
568  std::string& name = m_seci_values[i][0];
569  std::string vi_path = m_seci_values[i][1];
570  if ( blocks_re.FullMatch(name.c_str()) )
571  {
572  std::cerr << "Processing block \"" << name << "\"" << std::endl;
573  ++nblocks;
574  }
575  else
576  {
577  std::cerr << "Skipping block \"" << name << "\" as not matched by regular expression" << std::endl;
578  continue;
579  }
580  std::string read_type("unknown"), set_type("unknown");
581  if (m_seci_values[i][2] != "none")
582  {
583  read_type = getLabviewValueType(CComBSTR(vi_path.c_str()), CComBSTR(m_seci_values[i][2].c_str()));
584  }
585  if (!no_setter && m_seci_values[i][3] != "none")
586  {
587  set_type = getLabviewValueType(CComBSTR(vi_path.c_str()), CComBSTR(m_seci_values[i][3].c_str()));
588  }
589  std::replace(vi_path.begin(), vi_path.end(), '\\', '/');
590  fs << " <vi path=\"" << replaceWithEntities(vi_path) << "\">\n";
591  if (set_type != "unknown")
592  {
593  fs << " <param name=\"" << name << "_set\" type=\"" << set_type << "\">\n";
594  fs << " <read method=\"GCV\" target=\"" << replaceWithEntities(m_seci_values[i][3]) << "\"/>\n";
595  fs << " <set method=\"SCV\" extint=\"true\" target=\"" << replaceWithEntities(m_seci_values[i][3]) << "\"";
596  if (m_seci_values[i][4].size() > 0 && m_seci_values[i][4] != "none")
597  {
598  fs << " post_button=\"" << replaceWithEntities(m_seci_values[i][4]) << "\"";
599  }
600  fs << "/>\n";
601  fs << " </param>\n";
602  rsuffix = "_set";
603  ssuffix = "_set";
604  pv_type = set_type;
605  }
606  if (read_type != "unknown")
607  {
608  fs << " <param name=\"" << name << "_read\" type=\"" << read_type << "\">\n";
609  fs << " <read method=\"GCV\" target=\"" << replaceWithEntities(m_seci_values[i][2]) << "\"/>\n";
610  fs << " </param>\n";
611  rsuffix = "_read";
612  pv_type = read_type;
613  if (ssuffix.size() == 0) // no set defined, generate a dummy one for PV template
614  {
615  ssuffix = "_read";
616  }
617  }
618  fs << " </vi>\n";
619  // if you define a read for the seci block but not a set, map set -> read
620  // if you define a set for the block but not a read, we will map the READ -> set
621  // This is so scannng the read PV will always work
622  if (rsuffix.size() > 0 && ssuffix.size() > 0)
623  {
624  fsdb << "file \"${LVDCOM}/db/lvDCOM_" << pv_type << ".template\" {\n";
625  fsdb << " { P=\"" << envExpand("$(P=)") << "\",PORT=\"" << portName << "\",SCAN=\""
626  << envExpand("$(SCAN=1 second)") << "\",PARAM=\"" << name
627  << "\",NOSET=\"" << (no_setter ? "#" : " ")
628  << "\",RPARAM=\"" << name << rsuffix << "\",SPARAM=\"" << name << ssuffix << "\" }\n";
629  fsdb << "}\n\n";
630  }
631  else
632  {
633  std::cerr << "Skipping DB for block \"" << name << "\" (unknown type)" << std::endl;
634  std::cerr << "Controls: \"" << m_seci_values[i][2] << "\" \"" << m_seci_values[i][3] << "\"" << std::endl;
635  }
636  }
637  fs << " </section>\n";
638  fs << "</lvinput>\n";
639  fs.close();
640  fsdb.close();
641  macPopScope(m_mac_env);
642  return nblocks;
643 }
644 
645 void lvDCOMInterface::stopVis(bool only_ones_we_started)
646 {
647  for(vi_map_t::const_iterator it = m_vimap.begin(); it != m_vimap.end(); ++it)
648  {
649  LabVIEW::VirtualInstrumentPtr vi_ref = it->second.vi_ref;
650  if ( (!only_ones_we_started || it->second.started) && (vi_ref != NULL) )
651  {
652  if (vi_ref->ExecState != LabVIEW::eIdle) // don't try to stop it if it is already stopped
653  {
654  std::cerr << "stopping \"" << CW2CT(it->first.c_str()) << "\" as it was auto-started and is still running" << std::endl;
655  try
656  {
657  vi_ref->Abort();
658  }
659  catch(const std::exception& ex)
660  {
661  std::cerr << "error stopping vi: " << ex.what() << std::endl;
662  }
663  catch(...)
664  {
665  std::cerr << "error stopping vi: unknown" << std::endl;
666  }
667  }
668  }
669  }
670 }
671 
673 {
674  long n = 0;
675  char control_name_xpath[MAX_PATH_LEN];
676  _snprintf(control_name_xpath, sizeof(control_name_xpath), "/lvinput/section[@name='%s']/vi/param", m_configSection.c_str());
677  IXMLDOMNodeList* pXMLDomNodeList = NULL;
678  HRESULT hr = m_pxmldom->selectNodes(_bstr_t(control_name_xpath), &pXMLDomNodeList);
679  if (SUCCEEDED(hr) && pXMLDomNodeList != NULL)
680  {
681  pXMLDomNodeList->get_length(&n);
682  pXMLDomNodeList->Release();
683  }
684  return n;
685 }
686 
687 void lvDCOMInterface::getParams(std::map<std::string,std::string>& res)
688 {
689  res.clear();
690  char control_name_xpath[MAX_PATH_LEN];
691  _snprintf(control_name_xpath, sizeof(control_name_xpath), "/lvinput/section[@name='%s']/vi/param", m_configSection.c_str());
692  IXMLDOMNodeList* pXMLDomNodeList = NULL;
693  HRESULT hr = m_pxmldom->selectNodes(_bstr_t(control_name_xpath), &pXMLDomNodeList);
694  if (FAILED(hr) || pXMLDomNodeList == NULL)
695  {
696  return;
697  }
698  IXMLDOMNode *pNode, *pAttrNode1, *pAttrNode2;
699  long n = 0;
700  pXMLDomNodeList->get_length(&n);
701  for(long i=0; i<n; ++i)
702  {
703  pNode = NULL;
704  hr = pXMLDomNodeList->get_item(i, &pNode);
705  if (SUCCEEDED(hr) && pNode != NULL)
706  {
707  IXMLDOMNamedNodeMap *attributeMap = NULL;
708  pAttrNode1 = pAttrNode2 = NULL;
709  pNode->get_attributes(&attributeMap);
710  hr = attributeMap->getNamedItem(_bstr_t("name"), &pAttrNode1);
711  hr = attributeMap->getNamedItem(_bstr_t("type"), &pAttrNode2);
712  BSTR bstrValue1 = NULL, bstrValue2 = NULL;
713  hr=pAttrNode1->get_text(&bstrValue1);
714  hr=pAttrNode2->get_text(&bstrValue2);
715  res[std::string(COLE2CT(bstrValue1))] = COLE2CT(bstrValue2);
716  SysFreeString(bstrValue1);
717  SysFreeString(bstrValue2);
718  pAttrNode1->Release();
719  pAttrNode2->Release();
720  attributeMap->Release();
721  pNode->Release();
722  }
723  }
724  pXMLDomNodeList->Release();
725 }
726 
727 COAUTHIDENTITY* lvDCOMInterface::createIdentity(const std::string& user, const std::string& domain, const std::string& pass)
728 {
729  if (user.size() == 0)
730  {
731  return NULL;
732  }
733  COAUTHIDENTITY* pidentity = new COAUTHIDENTITY;
734  pidentity->Domain = (USHORT*)strdup(domain.c_str());
735  pidentity->DomainLength = static_cast<ULONG>(strlen((const char*)pidentity->Domain));
736  pidentity->Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
737  pidentity->Password = (USHORT*)strdup(pass.c_str());
738  pidentity->PasswordLength = static_cast<ULONG>(strlen((const char*)pidentity->Password));
739  pidentity->User = (USHORT*)strdup(user.c_str());
740  pidentity->UserLength = static_cast<ULONG>(strlen((const char*)pidentity->User));
741  return pidentity;
742 }
743 
744 HRESULT lvDCOMInterface::setIdentity(COAUTHIDENTITY* pidentity, IUnknown* pUnk)
745 {
746  HRESULT hr;
747  if (pidentity != NULL)
748  {
749  hr = CoSetProxyBlanket(pUnk, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
750  RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, pidentity, EOAC_NONE);
751  if (FAILED(hr))
752  {
753  std::cerr << "setIdentity failed" << std::endl;
754  return hr;
755  }
756  }
757  return S_OK;
758 }
759 
760 
761 void lvDCOMInterface::getViRef(BSTR vi_name, bool reentrant, LabVIEW::VirtualInstrumentPtr& vi)
762 {
763  UINT len = SysStringLen(vi_name);
764  std::wstring ws(vi_name, SysStringLen(vi_name));
765 
766  epicsGuard<epicsMutex> _lock(m_lock);
767  vi_map_t::iterator it = m_vimap.find(ws);
768  if(it != m_vimap.end())
769  {
770  vi = it->second.vi_ref;
771  try
772  {
773  vi->GetExecState();
774  }
775  catch(...)
776  {
777  //Gets here if VI ref is not longer valid
778  createViRef(vi_name, reentrant, vi);
779  }
780  }
781  else
782  {
783  createViRef(vi_name, reentrant, vi);
784  }
785 }
786 
789 {
790  HANDLE hProcess;
791  double lvUptime = -1.0;
792  int lvCount = 0; // number of LabVIEW.exe processes found
793  PROCESSENTRY32 pe32;
794  FILETIME lvCreationTime, lvExitTime, lvKernelTime, lvUserTime, sysTime;
795  epicsGuard<epicsMutex> _lock(m_lock); // just to restrict number of simultaneous snapshots
796  HANDLE hProcessSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
797  if( hProcessSnap == INVALID_HANDLE_VALUE )
798  {
799  return lvUptime;
800  }
801  pe32.dwSize = sizeof( PROCESSENTRY32 );
802  if( !Process32First( hProcessSnap, &pe32 ) )
803  {
804  CloseHandle( hProcessSnap );
805  return lvUptime;
806  }
807  do
808  {
809  if ( !stricmp(pe32.szExeFile, "LabVIEW.exe") )
810  {
811  ++lvCount;
812  hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID);
813  if( hProcess != NULL )
814  {
815  if (GetProcessTimes(hProcess, &lvCreationTime, &lvExitTime, &lvKernelTime, &lvUserTime) != 0)
816  {
817  GetSystemTimeAsFileTime(&sysTime);
818  lvUptime = diffFileTimes(sysTime, lvCreationTime);
819  }
820  CloseHandle( hProcess );
821  }
822  }
823  } while( Process32Next( hProcessSnap, &pe32 ) );
824  CloseHandle( hProcessSnap );
825 // std::cerr << "lvuptime=" << lvUptime << "s lvcount=" << lvCount << std::endl;
826  return lvUptime;
827 }
828 
830 double lvDCOMInterface::diffFileTimes(const FILETIME& f1, const FILETIME& f2)
831 {
832  ULARGE_INTEGER u1, u2;
833  u1.LowPart = f1.dwLowDateTime;
834  u1.HighPart = f1.dwHighDateTime;
835  u2.LowPart = f2.dwLowDateTime;
836  u2.HighPart = f2.dwHighDateTime;
837  return static_cast<double>(u1.QuadPart - u2.QuadPart) / 1e7;
838 }
839 
841 {
842  if ( checkOption(lvNoStart) )
843  {
845  {
846  if ( checkOption(lvSECIConfig) )
847  {
848  // likely a seci restart, so exit and procServ will restart us ready for new config
849  std::cerr << "Terminating as in SECI mode and no LabVIEW" << std::endl;
850  epicsExit(0);
851  }
852  else
853  {
854  waitForLabVIEW();
855  //throw std::runtime_error("LabVIEW not running and \"lvNoStart\" requested");
856  }
857  }
858  }
859 }
860 
861 // this is called with m_lock held
862 void lvDCOMInterface::createViRef(BSTR vi_name, bool reentrant, LabVIEW::VirtualInstrumentPtr& vi)
863 {
864  epicsThreadOnce(&onceId, initCOM, NULL);
865  std::wstring ws(vi_name, SysStringLen(vi_name));
866  HRESULT hr = E_FAIL;
867  // we do maybeWaitForLabVIEWOrExit() either side of this to try and avoid a race condition...
869  if (m_lv != NULL)
870  {
871  try
872  {
873  hr = m_lv->CheckConnection();
874  }
875  catch(const std::exception&)
876  {
877  hr = E_FAIL;
878  }
879  if ( FAILED(hr) )
880  {
881  epicsThreadSleep(5.0);
882  }
883  }
885  if (hr == S_OK)
886  {
887  ;
888  }
889  else if (m_host.size() > 0)
890  {
891  std::cerr << "(Re)Making connection to LabVIEW on " << m_host << std::endl;
892  CComBSTR host(m_host.c_str());
894  COAUTHINFO* pauth = new COAUTHINFO;
895  COSERVERINFO csi = { 0, NULL, NULL, 0 };
896  pauth->dwAuthnSvc = RPC_C_AUTHN_WINNT;
897  pauth->dwAuthnLevel = RPC_C_AUTHN_LEVEL_DEFAULT;
898  pauth->dwAuthzSvc = RPC_C_AUTHZ_NONE;
899  pauth->dwCapabilities = EOAC_NONE;
900  pauth->dwImpersonationLevel = RPC_C_IMP_LEVEL_IMPERSONATE;
901  pauth->pAuthIdentityData = m_pidentity;
902  pauth->pwszServerPrincName = NULL;
903  csi.pwszName = host;
904  csi.pAuthInfo = pauth;
905  MULTI_QI mq[ 1 ] = { 0 };
906  mq[ 0 ].pIID = &IID_IDispatch; // &LabVIEW::DIID__Application; // &IID_IDispatch;
907  mq[ 0 ].pItf = NULL;
908  mq[ 0 ].hr = S_OK;
909  hr = CoCreateInstanceEx( m_clsid, NULL, CLSCTX_REMOTE_SERVER | CLSCTX_LOCAL_SERVER, &csi, 1, mq );
910  if( FAILED( hr ) )
911  {
912  hr = CoCreateInstanceEx( m_clsid, NULL, CLSCTX_ALL, &csi, 1, mq );
913  }
914  if( FAILED( hr ) )
915  {
916  throw COMexception("CoCreateInstanceEx (LabVIEW) ", hr);
917  }
918  if( S_OK != mq[ 0 ].hr || NULL == mq[ 0 ].pItf )
919  {
920  throw COMexception("CoCreateInstanceEx (LabVIEW)(mq) ", mq[ 0 ].hr);
921  }
922  setIdentity(m_pidentity, mq[ 0 ].pItf);
923  m_lv.Release();
924  m_lv.Attach( reinterpret_cast< LabVIEW::_Application* >( mq[ 0 ].pItf ) );
925  std::cerr << "Successfully connected to LabVIEW on " << m_host << std::endl;
926  }
927  else
928  {
929  std::cerr << "(Re)Making local connection to LabVIEW" << std::endl;
930  m_pidentity = NULL;
931  m_lv.Release();
932  hr = m_lv.CoCreateInstance(m_clsid, NULL, CLSCTX_LOCAL_SERVER);
933  if( FAILED( hr ) )
934  {
935  throw COMexception("CoCreateInstance (LabVIEW) ", hr);
936  }
937  std::cerr << "Successfully connected to local LabVIEW" << std::endl;
938  }
939  if (reentrant)
940  {
941  vi = m_lv->GetVIReference(vi_name, "", 1, 8);
943  }
944  else
945  {
946  //If a VI is reentrant then always get it as reentrant
947  vi = m_lv->GetVIReference(vi_name, "", 0, 0);
949  if (vi->IsReentrant)
950  {
951  vi = m_lv->GetVIReference(vi_name, "", 1, 8);
953  reentrant = true;
954  }
955  }
956  ViRef viref(vi, reentrant, false);
957  // LabVIEW::ExecStateEnum::eIdle = 1
958  // LabVIEW::ExecStateEnum::eRunTopLevel = 2
959  if (vi->ExecState == LabVIEW::eIdle)
960  {
961  if ( checkOption(viStartIfIdle) )
962  {
963  std::cerr << "Starting \"" << CW2CT(vi_name) << "\" on " << (m_host.size() > 0 ? m_host : "localhost") << std::endl;
964  vi->Run(true);
965  viref.started = true;
966  }
967  else if ( checkOption(viWarnIfIdle) )
968  {
969  std::cerr << "\"" << CW2CT(vi_name) << "\" is not running on " << (m_host.size() > 0 ? m_host : "localhost") << " and autostart is disabled" << std::endl;
970  }
971  }
972  m_vimap[ws] = viref;
973 }
974 
975 
976 template <>
977 void lvDCOMInterface::getLabviewValue(const char* param, std::string* value)
978 {
979  if (value == NULL)
980  {
981  throw std::runtime_error("getLabviewValue failed (NULL)");
982  }
983  if (param == NULL || *param == '\0')
984  {
985  throw std::runtime_error("getLabviewValue: param is NULL");
986  }
987  CComVariant v;
988  char vi_name_xpath[MAX_PATH_LEN], control_name_xpath[MAX_PATH_LEN];
989  _snprintf(vi_name_xpath, sizeof(vi_name_xpath), "/lvinput/section[@name='%s']/vi[param[@name='%s']]/@path", m_configSection.c_str(), param);
990  _snprintf(control_name_xpath, sizeof(control_name_xpath), "/lvinput/section[@name='%s']/vi/param[@name='%s']/read/@target", m_configSection.c_str(), param);
991  CComBSTR vi_name(doPath(vi_name_xpath).c_str());
992  CComBSTR control_name(doXPATH(control_name_xpath).c_str());
993  if (vi_name.Length() == 0 || control_name.Length() == 0)
994  {
995  throw std::runtime_error("getLabviewValue: vi or control is NULL");
996  }
997  getLabviewValue(vi_name, control_name, &v);
998  if ( v.ChangeType(VT_BSTR) == S_OK )
999  {
1000  *value = CW2CT(v.bstrVal);
1001  }
1002  else
1003  {
1004  throw std::runtime_error("getLabviewValue failed (ChangeType BSTR)");
1005  }
1006 }
1007 
1008 template<typename T>
1009 void lvDCOMInterface::getLabviewValue(const char* param, T* value, size_t nElements, size_t& nIn)
1010 {
1011  if (value == NULL)
1012  {
1013  throw std::runtime_error("getLabviewValue failed (NULL)");
1014  }
1015  if (param == NULL || *param == '\0')
1016  {
1017  throw std::runtime_error("getLabviewValue: param is NULL");
1018  }
1019  CComVariant v;
1020  char vi_name_xpath[MAX_PATH_LEN], control_name_xpath[MAX_PATH_LEN];
1021  _snprintf(vi_name_xpath, sizeof(vi_name_xpath), "/lvinput/section[@name='%s']/vi[param[@name='%s']]/@path", m_configSection.c_str(), param);
1022  _snprintf(control_name_xpath, sizeof(control_name_xpath), "/lvinput/section[@name='%s']/vi/param[@name='%s']/read/@target", m_configSection.c_str(), param);
1023  CComBSTR vi_name(doPath(vi_name_xpath).c_str());
1024  CComBSTR control_name(doXPATH(control_name_xpath).c_str());
1025  if (vi_name.Length() == 0 || control_name.Length() == 0)
1026  {
1027  throw std::runtime_error("getLabviewValue: vi or control is NULL");
1028  }
1029  getLabviewValue(vi_name, control_name, &v);
1030  if ( v.vt != (VT_ARRAY | CVarTypeInfo<T>::VT) )
1031  {
1032  throw std::runtime_error("getLabviewValue failed (type mismatch)");
1033  }
1034  CComSafeArray<T> sa;
1035  sa.Attach(v.parray);
1036  nIn = ( sa.GetCount() > nElements ? nElements : sa.GetCount() );
1037  for(LONG i=0; i<nIn; ++i)
1038  {
1039  value[i] = sa.GetAt(i);
1040  }
1041  sa.Detach();
1042 }
1043 
1044 template <typename T>
1045 void lvDCOMInterface::getLabviewValue(const char* param, T* value)
1046 {
1047  if (value == NULL)
1048  {
1049  throw std::runtime_error("getLabviewValue failed (NULL)");
1050  }
1051  if (param == NULL || *param == '\0')
1052  {
1053  throw std::runtime_error("getLabviewValue: param is NULL");
1054  }
1055  CComVariant v;
1056  char vi_name_xpath[MAX_PATH_LEN], control_name_xpath[MAX_PATH_LEN];
1057  _snprintf(vi_name_xpath, sizeof(vi_name_xpath), "/lvinput/section[@name='%s']/vi[param[@name='%s']]/@path", m_configSection.c_str(), param);
1058  _snprintf(control_name_xpath, sizeof(control_name_xpath), "/lvinput/section[@name='%s']/vi/param[@name='%s']/read/@target", m_configSection.c_str(), param);
1059  CComBSTR vi_name(doPath(vi_name_xpath).c_str());
1060  CComBSTR control_name(doXPATH(control_name_xpath).c_str());
1061  if (vi_name.Length() == 0 || control_name.Length() == 0)
1062  {
1063  throw std::runtime_error("getLabviewValue: vi or control is NULL");
1064  }
1065  getLabviewValue(vi_name, control_name, &v);
1066  if ( v.ChangeType(CVarTypeInfo<T>::VT) == S_OK )
1067  {
1068  *value = v.*(CVarTypeInfo<T>::pmField);
1069  }
1070  else
1071  {
1072  throw std::runtime_error("getLabviewValue failed (ChangeType)");
1073  }
1074 }
1075 
1076 void lvDCOMInterface::getLabviewValue(BSTR vi_name, BSTR control_name, VARIANT* value)
1077 {
1078  HRESULT hr = S_OK;
1079  LabVIEW::VirtualInstrumentPtr vi;
1080  getViRef(vi_name, false, vi);
1081  *value = vi->GetControlValue(control_name).Detach();
1082  vi.Detach();
1083  if (FAILED(hr))
1084  {
1085  throw std::runtime_error("getLabviewValue failed");
1086  }
1087 }
1088 
1091 std::string lvDCOMInterface::getLabviewValueType(BSTR vi_name, BSTR control_name)
1092 {
1093  CComVariant v;
1094  try
1095  {
1096  getLabviewValue(vi_name, control_name, &v);
1097  }
1098  catch (const std::exception& ex)
1099  {
1100  std::cerr << "getLabviewValueType: Unable to read \"" << COLE2CT(control_name) << "\" on \"" << COLE2CT(vi_name) << "\": " << ex.what() << std::endl;
1101  return "unknown";
1102  }
1103  VARTYPE vt = (&v)->vt;
1104  switch(vt)
1105  {
1106  case VT_BOOL:
1107  return "boolean";
1108 
1109  case VT_BSTR:
1110  return "string";
1111 
1112  case VT_I1:
1113  case VT_I2:
1114  case VT_I4:
1115  case VT_INT:
1116  case VT_UI1:
1117  case VT_UI2:
1118  case VT_UI4:
1119  case VT_UINT:
1120  return "int32";
1121 
1122  default:
1123  break;
1124  }
1125  if ( v.ChangeType(VT_R8) == S_OK )
1126  {
1127  return "float64";
1128  }
1129  else
1130  {
1131  return "unknown";
1132  }
1133 }
1134 
1136 {
1137  CComBSTR m_bstr;
1138 public:
1139  StringItem(lvDCOMInterface* dcom, const char* xpath, const char* config_section, const char* param, bool filepath = false)
1140  {
1141  char base_xpath[MAX_PATH_LEN];
1142  _snprintf(base_xpath, sizeof(base_xpath), xpath, config_section, param);
1143  if (filepath)
1144  {
1145  m_bstr = dcom->doPath(base_xpath).c_str();
1146  }
1147  else
1148  {
1149  m_bstr = dcom->doXPATH(base_xpath).c_str();
1150  }
1151  };
1152  const CComBSTR& bstr() { return m_bstr; }
1153  size_t size() { return m_bstr.Length(); }
1154  operator BSTR() { return m_bstr; }
1155 };
1156 
1157 class BoolItem
1158 {
1159  bool m_value;
1160 public:
1161  BoolItem(lvDCOMInterface* dcom, const char* xpath, const char* config_section, const char* param)
1162  {
1163  char base_xpath[MAX_PATH_LEN];
1164  _snprintf(base_xpath, sizeof(base_xpath), xpath, config_section, param);
1165  m_value = dcom->doXPATHbool(base_xpath);
1166  };
1167  operator bool() { return m_value; }
1168 };
1169 
1170 template <>
1171 void lvDCOMInterface::setLabviewValue(const char* param, const std::string& value)
1172 {
1173  CComVariant v(value.c_str()), results, button_value(true);
1174  if (param == NULL || *param == '\0')
1175  {
1176  throw std::runtime_error("setLabviewValue: param is NULL");
1177  }
1178  StringItem vi_name(this, "/lvinput/section[@name='%s']/vi/@path", m_configSection.c_str(), "", true);
1179  StringItem control_name(this, "/lvinput/section[@name='%s']/vi/param[@name='%s']/set/@target", m_configSection.c_str(), param);
1180  StringItem post_button(this, "/lvinput/section[@name='%s']/vi/param[@name='%s']/set/@post_button", m_configSection.c_str(), param);
1181  BoolItem post_button_wait(this, "/lvinput/section[@name='%s']/vi/param[@name='%s']/set/@post_button_wait", m_configSection.c_str(), param);
1182  BoolItem use_ext(this, "/lvinput/section[@name='%s']/vi/param[@name='%s']/set/@extint", m_configSection.c_str(), param);
1183  if (vi_name.size() == 0 || control_name.size() == 0)
1184  {
1185  throw std::runtime_error("setLabviewValue: vi or control is NULL");
1186  }
1187  if (use_ext)
1188  {
1189  setLabviewValueExt(vi_name, control_name, v, &results);
1190  if (post_button.size() > 0)
1191  {
1192  setLabviewValueExt(vi_name, post_button, button_value, &results);
1193  }
1194  }
1195  else
1196  {
1197  setLabviewValue(vi_name, control_name, v);
1198  if (post_button.size() > 0)
1199  {
1200  setLabviewValue(vi_name, post_button, button_value);
1201  }
1202  }
1203  if (post_button_wait && (post_button.size() > 0) )
1204  {
1205  waitForLabviewBoolean(vi_name, post_button, false);
1206  }
1207 }
1208 
1209 void lvDCOMInterface::waitForLabviewBoolean(BSTR vi_name, BSTR control_name, bool value)
1210 {
1211  CComVariant v;
1212  bool done = false;
1213  while(!done)
1214  {
1215  getLabviewValue(vi_name, control_name, &v);
1216  if ( v.ChangeType(VT_BOOL) == S_OK )
1217  {
1218  done = ( v.boolVal == (value ? VARIANT_TRUE : VARIANT_FALSE) );
1219  v.Clear();
1220  }
1221  epicsThreadSleep(0.1);
1222  }
1223 }
1224 
1225 template <typename T>
1226 void lvDCOMInterface::setLabviewValue(const char* param, const T& value)
1227 {
1228  CComVariant v(value), results, button_value(true);
1229  if (param == NULL || *param == '\0')
1230  {
1231  throw std::runtime_error("setLabviewValue: param is NULL");
1232  }
1233  StringItem vi_name(this, "/lvinput/section[@name='%s']/vi/@path", m_configSection.c_str(), "", true);
1234  StringItem control_name(this, "/lvinput/section[@name='%s']/vi/param[@name='%s']/set/@target", m_configSection.c_str(), param);
1235  StringItem post_button(this, "/lvinput/section[@name='%s']/vi/param[@name='%s']/set/@post_button", m_configSection.c_str(), param);
1236  BoolItem post_button_wait(this, "/lvinput/section[@name='%s']/vi/param[@name='%s']/set/@post_button_wait", m_configSection.c_str(), param);
1237  BoolItem use_ext(this, "/lvinput/section[@name='%s']/vi/param[@name='%s']/set/@extint", m_configSection.c_str(), param);
1238  if (vi_name.size() == 0 || control_name.size() == 0)
1239  {
1240  throw std::runtime_error("setLabviewValue: vi or control is NULL");
1241  }
1242  if (use_ext)
1243  {
1244  setLabviewValueExt(vi_name, control_name, v, &results);
1245  if (post_button.size() > 0)
1246  {
1247  setLabviewValueExt(vi_name, post_button,button_value, &results);
1248  }
1249  }
1250  else
1251  {
1252  setLabviewValue(vi_name, control_name, v);
1253  if (post_button.size() > 0)
1254  {
1255  setLabviewValue(vi_name, post_button, button_value);
1256  }
1257  }
1258  if (post_button_wait && (post_button.size() > 0) )
1259  {
1260  waitForLabviewBoolean(vi_name, post_button, false);
1261  }
1262 }
1263 
1264 void lvDCOMInterface::setLabviewValue(BSTR vi_name, BSTR control_name, const VARIANT& value)
1265 {
1266  HRESULT hr = S_OK;
1267  LabVIEW::VirtualInstrumentPtr vi;
1268  getViRef(vi_name, false, vi);
1269  hr = vi->SetControlValue(control_name, value);
1270  vi.Detach();
1271  if (FAILED(hr))
1272  {
1273  throw std::runtime_error("SetLabviewValue failed");
1274  }
1275 }
1276 
1277 void lvDCOMInterface::setLabviewValueExt(BSTR vi_name, BSTR control_name, const VARIANT& value, VARIANT* results)
1278 {
1279 
1280  CComSafeArray<BSTR> names(6);
1281  names[0].AssignBSTR(_bstr_t(L"VI Name"));
1282  names[1].AssignBSTR(_bstr_t(L"Control Name"));
1283  names[2].AssignBSTR(_bstr_t(L"String Control Value"));
1284  names[3].AssignBSTR(_bstr_t(L"Variant Control Value"));
1285  names[4].AssignBSTR(_bstr_t(L"Machine Name"));
1286  names[5].AssignBSTR(_bstr_t(L"Return Message"));
1287 
1288  _variant_t n;
1289  n.vt = VT_ARRAY | VT_BSTR;
1290  n.parray = names.Detach();
1291 
1292  CComSafeArray<VARIANT> values(6);
1293  values[0] = vi_name;
1294  values[1] = control_name;
1295  //values[2] =
1296  values[3] = value;
1297  //values[4] =
1298  //values[5] =
1299 
1300  _variant_t v;
1301  v.vt = VT_ARRAY | VT_VARIANT;
1302  v.parray = values.Detach();
1303  //Must be called as reentrant!
1304  callLabview(m_extint, n, v, true, results);
1305 }
1306 
1307 void lvDCOMInterface::callLabview(BSTR vi_name, VARIANT& names, VARIANT& values, VARIANT_BOOL reentrant, VARIANT* results)
1308 {
1309  HRESULT hr = S_OK;
1310  LabVIEW::VirtualInstrumentPtr vi;
1311  if (reentrant)
1312  {
1313  getViRef(vi_name, true, vi);
1314  }
1315  else
1316  {
1317  getViRef(vi_name, false, vi);
1318  }
1319  hr = vi->Call(&names, &values);
1320  vi.Detach();
1321  CComVariant var(values);
1322  var.Detach(results);
1323  if (FAILED(hr))
1324  {
1325  throw std::runtime_error("CallLabviewValue failed");
1326  }
1327 }
1328 
1330 void lvDCOMInterface::report(FILE* fp, int details)
1331 {
1332  fprintf(fp, "XML ConfigFile: \"%s\"\n", m_configFile.c_str());
1333  fprintf(fp, "XML ConfigFile section: \"%s\"\n", m_configSection.c_str());
1334  fprintf(fp, "lvDCOMConfigure() Options: %d\n", m_options);
1335  fprintf(fp, "DCOM Target ProgID: \"%s\"\n", m_progid.c_str());
1336  fprintf(fp, "DCOM Target Host: \"%s\"\n", m_host.c_str());
1337  fprintf(fp, "DCOM Target Username: \"%s\"\n", m_username.c_str());
1338 // fprintf(fp, "Password: %s\n", m_password.c_str());
1339  std::string vi_name;
1340  for(vi_map_t::const_iterator it = m_vimap.begin(); it != m_vimap.end(); ++it)
1341  {
1342  vi_name = CW2CT(it->first.c_str());
1343  fprintf(fp, "LabVIEW VI: \"%s\"\n", vi_name.c_str());
1344  }
1345  if (details > 0)
1346  {
1347  for(std::map<std::string,std::string>::const_iterator it = m_xpath_map.begin(); it != m_xpath_map.end(); ++it)
1348  {
1349  fprintf(fp, "Config XPath: \"%s\" = \"%s\"\n", it->first.c_str(), it->second.c_str());
1350  }
1351  for(std::map<std::string,bool>::const_iterator it = m_xpath_bool_map.begin(); it != m_xpath_bool_map.end(); ++it)
1352  {
1353  fprintf(fp, "Config XPath: \"%s\" = %s\n", it->first.c_str(), (it->second ? "true" : "false") );
1354  }
1355  }
1356 }
1357 
1359 double lvDCOMInterface::m_minLVUptime = 60.0;
1360 
1361 std::vector< std::vector<std::string> > lvDCOMInterface::m_seci_values;
1362 
1363 #ifndef DOXYGEN_SHOULD_SKIP_THIS
1364 
1365 template void lvDCOMInterface::setLabviewValue(const char* param, const double& value);
1366 template void lvDCOMInterface::setLabviewValue(const char* param, const int& value);
1367 
1368 template void lvDCOMInterface::getLabviewValue(const char* param, double* value);
1369 template void lvDCOMInterface::getLabviewValue(const char* param, int* value);
1370 
1371 template void lvDCOMInterface::getLabviewValue(const char* param, double* value, size_t nElements, size_t& nIn);
1372 
1373 template void lvDCOMInterface::getLabviewValue(const char* param, int* value, size_t nElements, size_t& nIn);
1374 
1375 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
static void initCOM(void *)
create an C++ exception from a COM HRESULT.
Definition: variant_utils.h:15
static std::string replaceWithEntities(const std::string &str)
COAUTHIDENTITY * m_pidentity
std::string m_configSection
section of configFile to load information from
(4) On IOC exit, stop any LabVIEW VIs that we started due to viStartIfIdle being specified ...
CComPtr< LabVIEW::_Application > m_lv
BoolItem(lvDCOMInterface *dcom, const char *xpath, const char *config_section, const char *param)
bool FullMatch(const char *)
bool checkForNewBlockDetails()
std::string m_progid
Header file for various COM utilities.
bool started
did we start this vi because it was idle and viStartIfIdle was specified
void waitForLabviewBoolean(BSTR vi_name, BSTR control_name, bool value)
static void __stdcall my_com_raise_error(HRESULT hr, IErrorInfo *perrinfo)
The Microsoft ATL _com_error is not derived from std::exception hence this bit of code to throw our o...
(8) On IOC exit, stop any LabVIEW VIs that we have connected to
MAC_HANDLE * m_mac_env
std::string doPath(const std::string &xpath)
const CComBSTR & bstr()
std::string m_password
IXMLDOMDocument2 * m_pxmldom
void callLabview(BSTR vi_name, VARIANT &names, VARIANT &values, VARIANT_BOOL reentrant, VARIANT *results)
static std::vector< std::vector< std::string > > m_seci_values
horrible - do properly some time
void report(FILE *fp, int details)
Helper for EPICS driver report function.
(16) Do not start LabVIEW, connect to existing instance otherwise fail. As loading a Vi starts labvie...
#define MAX_PATH_LEN
header for lvDCOMInterface class.
double waitForLabVIEW()
wait for LabVIEW to have been running for m_minLVUptime seconds
RE(const char *)
int m_options
the various lvDCOMOptions currently in use
std::map< std::string, bool > m_xpath_bool_map
void getParams(std::map< std::string, std::string > &res)
(32) Automatically set if lvDCOMSECIConfigure() has been used
std::string doXPATH(const std::string &xpath)
std::map< std::string, std::string > m_xpath_map
void getBlockDetails(std::vector< std::vector< std::string > > &values)
double getLabviewUptime()
returns -1.0 if labview not running, else labview uptime in seconds
static void epicsExitFunc(void *arg)
void stopVis(bool only_ones_we_started)
int generateFilesFromSECI(const char *portName, const char *macros, const char *configSection, const char *configFile, const char *dbSubFile, const char *blocks_match, bool no_setter)
generate XML and DB files for SECI blocks
void maybeWaitForLabVIEWOrExit()
static double m_minLVUptime
minimum time labview must be running before connection made in &quot;lvNoStart&quot; mode
Manager class for LabVIEW DCOM Interaction. Parses an lvinput.xml file and provides access to the Lab...
void setLabviewValue(const char *param, const T &value)
HRESULT setIdentity(COAUTHIDENTITY *pidentity, IUnknown *pUnk)
(1) If the LabVIEW VI is idle when we connect to it, issue a warning message
Hold a reference to a LabVIEW VI.
void getLabviewValue(const char *param, T *value)
bool GlobalReplace(const std::string, std::string *)
std::string getLabviewValueType(BSTR vi_name, BSTR control_name)
determine best epics type for a labvier variable, this will be used to choose the appropriate EPICS r...
(2) If the LabVIEW VI is idle when we connect to it, attempt to start it
std::string m_configFile
static double diffFileTimes(const FILETIME &f1, const FILETIME &f2)
filetime uses 100ns units, returns difference in seconds
static epicsThreadOnceId onceId
std::string m_host
COAUTHIDENTITY * createIdentity(const std::string &user, const std::string &domain, const std::string &pass)
bool checkOption(lvDCOMOptions option)
void createViRef(BSTR vi_name, bool reentrant, LabVIEW::VirtualInstrumentPtr &vi)
void setLabviewValueExt(BSTR vi_name, BSTR control_name, const VARIANT &value, VARIANT *results)
char * envExpand(const char *str)
expand epics environment strings using previously saved environment based on EPICS macEnvExpand() ...
std::string m_username
bool doXPATHbool(const std::string &xpath)
lvDCOMInterface(const char *configSection, const char *configFile, const char *host, int options, const char *progid, const char *username, const char *password)
section name of configFile we will load settings from
void getViRef(BSTR vi_name, bool reentrant, LabVIEW::VirtualInstrumentPtr &vi)
StringItem(lvDCOMInterface *dcom, const char *xpath, const char *config_section, const char *param, bool filepath=false)
const GUID CLSID_Application
Definition: labview.tlh:1439
Copyright © 2013 Science and Technology Facilities Council | Generated by   doxygen 1.8.5