GenApi  3.5.1
ValueArrayAdapter.h
1 //-----------------------------------------------------------------------------
2 // (c) 2020 by SICK AG
3 // Author: Mattias Johannesson
4 //
5 // License: This file is published under the license of the EMVA GenICam Standard Group.
6 // A text file describing the legal terms is included in your installation as 'GenICam_license.pdf'.
7 // If for some reason you are missing this file please contact the EMVA or visit the website
8 // (http://www.genicam.org) for a full copy.
9 //
10 // THIS SOFTWARE IS PROVIDED BY THE EMVA GENICAM STANDARD GROUP "AS IS"
11 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
12 // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
13 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE EMVA GENICAM STANDARD GROUP
14 // OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
15 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
16 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
17 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
18 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
19 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
20 // POSSIBILITY OF SUCH DAMAGE.
21 //
22 //-----------------------------------------------------------------------------
28 #ifndef VALUE_ARRAY_ADAPTER_H
29 #define VALUE_ARRAY_ADAPTER_H
30 
31 #include <Base/GCException.h>
32 #include <GenApi/GenApi.h>
33 #include <GenApi/Synch.h>
34 
35 #include <algorithm>
36 #include <exception>
37 #include <functional>
38 #include <string>
39 #include <vector>
40 #include <iterator>
41 
42 /* Unified access to byte-swapping tools, possibly candidate to get
43  * extracted/extended to a reusable header. */
44 #ifdef _MSC_VER
45  #include <cstdlib>
46  #define SW16(s) _byteswap_ushort(s)
47  #define SW32(s) _byteswap_ulong(s)
48  #define SW64(s) _byteswap_uint64(s)
49 #elif __APPLE__
50  #include <libkern/OSByteOrder.h>
51  #define SW16(s) __builtin_bswap16( (s) )
52  #define SW32(s) __builtin_bswap32( (s) )
53  #define SW64(s) __builtin_bswap64( (s) )
54 #else
55  #include <byteswap.h>
56  #define SW16(s) bswap_16(s)
57  #define SW32(s) bswap_32(s)
58  #define SW64(s) bswap_64(s)
59 #endif
60 
61 namespace GENAPI_NAMESPACE
62 {
63 
64  /* Helper mini-template to allow specialized template conversions.
65  * Might be reused (or eliminated) in future, for now intended mainly to
66  * implement conversion to bool and numeric types differently.
67  * In future we might check if some type limits checking or similar is
68  * desirable. */
69  template<typename TTargetType>
70  class TypeConverter
71  {
72  public:
73  template<typename TSourceType>
74  static TTargetType Convert (TSourceType src)
75  {
76  return static_cast<TTargetType>(src);
77  }
78  };
79 
80  template<>
81  class TypeConverter<bool>
82  {
83  public:
84  template<typename TSourceType>
85  static bool Convert (TSourceType src)
86  {
87  return 0 != src;
88  }
89  };
90 
91  /* Helper mini-template to extract byte-swapped values for all numeric
92  * types (incl. floats).
93  * Reverse-copy by default, attempt to perform dedicated swaps for
94  * known sizes. */
95  template<size_t TValueSize>
96  class SwapExtractor
97  {
98  public:
99  static void Extract (const void *src, void *dst)
100  {
101  std::reverse_copy(reinterpret_cast<const uint8_t*>(src),
102  reinterpret_cast<const uint8_t*>(src) + TValueSize,
103  reinterpret_cast<uint8_t*>(dst));
104  }
105  };
106  template<>
107  class SwapExtractor<8>
108  {
109  public:
110  static void Extract (const void *src, void *dst)
111  {
112  *reinterpret_cast<uint64_t*>(dst) = SW64(*reinterpret_cast<const uint64_t*>(src));
113  }
114  };
115  template<>
116  class SwapExtractor<4>
117  {
118  public:
119  static void Extract (const void *src, void *dst)
120  {
121  *reinterpret_cast<uint32_t*>(dst) = SW32(*reinterpret_cast<const uint32_t*>(src));
122  }
123  };
124  template<>
125  class SwapExtractor<2>
126  {
127  public:
128  static void Extract (const void *src, void *dst)
129  {
130  *reinterpret_cast<uint16_t*>(dst) = SW16(*reinterpret_cast<const uint16_t*>(src));
131  }
132  };
133  template<>
134  class SwapExtractor<1>
135  {
136  public:
137  static void Extract (const void *src, void *dst)
138  {
139  *reinterpret_cast<uint8_t*>(dst) = *reinterpret_cast<const uint8_t*>(src);
140  }
141  };
142 
151  class GENAPI_DECL CValueArrayAdapterBase
152  {
153  protected:
154  CValueArrayAdapterBase(IValue* base_value, IInteger *selector);
155  virtual ~CValueArrayAdapterBase ();
156  private:
157  /* Not copyable */
160 
161  protected:
162  /* Retrieves current status of the value array parameters and reads
163  * "shadow copy" of the memory holding the array from the port.
164  * It is caller's responsibility to apply the lock as needed.
165  * Note: the lock is recursive and ReadFromPort() uses only read operations,
166  * no write that would trigger callbacks, it is therefore safe to apply
167  * the lock from outside. */
168  void ReadFromPort ();
169  CLock& GetLock() const;
170 
171  protected:
172  /* Flag indicating successful parse of the dependencies and initialization
173  * of the adapter - if false, the adapter cannot be used. */
174  bool is_valid;
175  /* Static information sniffed from the XML/nodemap at time of creation
176  * of the adapter - info about the structure that does not change at runtime.
177  * These are guaranteed to be up-to-date once the instance is constructed. */
178  bool is_int_reg;
179  bool swap_endian;
180  bool is_signed;
181  bool masked_int;
182  uint32_t lsbit;
183  uint32_t msbit;
184  uint64_t lsbit_mask;
185  /* Dynamic values used for every single read. In particular read copy
186  * of the register itself.
187  * These are updated during each call to ReadFromPort() that should happen
188  * just before attempt to extract the actual values. */
189  uint64_t current_base_address;
190  uint64_t current_reg_length;
191  uint64_t current_address_step;
192  size_t current_num_values;
193  uint8_t *current_array_shadow;
194 
195  private:
196  /* Implementation details */
197  struct ValueArrayInternals;
198  ValueArrayInternals* pinternal;
199  };
200 
215  template<typename TValueNodeType, typename TOutputValueType, typename TEffectiveValueType = TOutputValueType>
217  {
218  public:
226  CValueArrayAdapter(TValueNodeType* base_value, IInteger *selector)
227  : CValueArrayAdapterBase (base_value, selector),
228  current_array_getter(NULL)
229  { }
230 
239  bool IsValid() const
240  {
241  return is_valid;
242  }
243 
266  std::vector<TOutputValueType> GetAllValues()
267  {
268  std::vector<TOutputValueType> values(current_num_values);
269  GetAllValues (values);
270  return values;
271  }
272 
300  void GetAllValues(std::vector<TOutputValueType> &values)
301  {
302  if (!is_valid)
303  {
304  throw RUNTIME_EXCEPTION("Invalid value array adapter");
305  }
306 
307  /* Lock the entire operation, protecting not only against concurrent
308  * changes of underlying nodemap parameters, but also against concurrent
309  * accessess to the value array adapter itself. */
310  AutoLock l(GetLock());
311 
312  /* Freeze current state of all the dynamic properties and read the register. */
313  PrepareValues ();
314 
315  assert (current_array_getter != NULL);
316  (this->*current_array_getter) (values);
317  }
318 
332  static bool CheckAdvertisedCompatibility(TValueNodeType* base_value, IInteger *selector)
333  {
334  try
335  {
336  if (!base_value || !selector)
337  {
338  return false;
339  }
340 
341  /* The nodes must necessarily belong to the same nodemap */
342  INodeMap *node_map = base_value->GetNode()->GetNodeMap();
343  if (node_map != selector->GetNode()->GetNodeMap())
344  {
345  return false;
346  }
347 
348  /* The set of candidates is per SFNC advertised through string feature
349  * ValueArrayCandidates. */
350  CStringPtr candidates_node = node_map->GetNode("ValueArrayCandidates");
351  if (!candidates_node.IsValid() || !IsReadable(candidates_node))
352  {
353  return false;
354  }
355  GENICAM_NAMESPACE::gcstring rawstr = candidates_node->GetValue ();
356 
357  /* Erase everything starting from a first occurence of the '#' character.
358  * This is a backdoor to allow extending the ValueArrayCandidates format
359  * in a backward compatible way. */
360  GENICAM_NAMESPACE::gcstring candidates_str = rawstr.substr (0, rawstr.find_first_of("#"));
361 
362  /* The list of adapter candidates is encoded (per SFNC) as comma
363  * separated list of Value[Selector] entries. */
364  GENICAM_NAMESPACE::gcstring_vector tokens;
365  Tokenize(candidates_str, tokens, ",");
366 
367  /* Finally check if the requested value/selector pair is in the list. */
368  GENICAM_NAMESPACE::gcstring wanted = base_value->GetNode()->GetName()
369  + "[" + selector->GetNode()->GetName() + "]";
370  return tokens.contains (wanted);
371  }
372  catch (const GenICam::GenericException &)
373  {
374  return false;
375  }
376  }
377 
378  private:
379  /* Array getter implementations for individual field types.
380  * Implemented as complete array getter rather than just single value getter
381  * called from a loop to allow for unrolling-related compiler optimizations. */
382  template<typename TFieldType>
383  void GetArrayOfFieldValues (std::vector<TOutputValueType> &values)
384  {
385  assert (current_reg_length!=0 && current_address_step != 0);
386 
387  /* Resize the output (note that vector capacity does not change if
388  * the original is bigger than needed). */
389  values.resize (current_num_values);
390 
391  /* Extract the values.
392  * The memory read into our shadow copy starts at first desired value
393  * (see PrepareValues), therefore we start at offset 0. */
394  if (swap_endian)
395  {
396  ExtractFieldValues_Swap<TFieldType> (current_num_values,
397  static_cast<size_t>(current_address_step),
398  current_array_shadow, &values[0]);
399  }
400  else
401  {
402  ExtractFieldValues_Noswap<TFieldType> (current_num_values,
403  static_cast<size_t>(current_address_step),
404  current_array_shadow, &values[0]);
405  }
406  }
407  template<typename TFieldType>
408  void GetArrayOfFieldBits (std::vector<TOutputValueType> &values)
409  {
410  assert (current_reg_length!=0 && current_address_step != 0);
411 
412  /* Resize the output (note that vector capacity does not change if
413  * the original is bigger than needed). */
414  values.resize (current_num_values);
415 
416  /* Extract the values.
417  * The memory read into our shadow copy starts at first desired value
418  * (see PrepareValues), therefore we start at offset 0. */
419  if (swap_endian)
420  {
421  ExtractFieldBits_Swap<TFieldType> (current_num_values,
422  static_cast<size_t>(current_address_step),
423  lsbit_mask, lsbit,
424  current_array_shadow, &values[0]);
425  }
426  else
427  {
428  ExtractFieldBits_Noswap<TFieldType> (current_num_values,
429  static_cast<size_t>(current_address_step),
430  lsbit_mask, lsbit,
431  current_array_shadow, &values[0]);
432  }
433  }
434  /* Static workers responsible for the main-loop part of the GetArray-functions.
435  * Separating similar workers with code duplication and using all params as
436  * local variables instead of referring to member variables to assist
437  * optimizer as much as possible. */
438  template<typename TFieldType>
439  static void ExtractFieldValues_Noswap(size_t num_values, size_t address_step,
440  const uint8_t *src, TOutputValueType *dst)
441  {
442  for (size_t i = 0; i < num_values; ++i)
443  {
444  const uint8_t *field_ptr = src + i * address_step;
445 
446  TFieldType field_value = *reinterpret_cast<const TFieldType*>(field_ptr);
447  dst[i] = static_cast<TOutputValueType>(EffectiveValue (field_value));
448  }
449  }
450  template<typename TFieldType>
451  static void ExtractFieldValues_Swap(size_t num_values, size_t address_step,
452  const uint8_t *src, TOutputValueType *dst)
453  {
454  for (size_t i = 0; i < num_values; ++i)
455  {
456  const uint8_t *field_ptr = src + i * address_step;
457 
458  TFieldType field_value = ReadSwapped<TFieldType> (field_ptr);
459  dst[i] = static_cast<TOutputValueType>(EffectiveValue (field_value));
460  }
461  }
462  template<typename TFieldType>
463  static void ExtractFieldBits_Noswap(size_t num_values, size_t address_step,
464  uint64_t mask, size_t shift,
465  const uint8_t *src, TOutputValueType *dst)
466  {
467  for (size_t i = 0; i < num_values; ++i)
468  {
469  const uint8_t *field_ptr = src + i * address_step;
470 
471  TFieldType field_value = *reinterpret_cast<const TFieldType*>(field_ptr);
472  uint64_t field_bits = mask & (field_value >> shift);
473  dst[i] = static_cast<TOutputValueType>(EffectiveValue (field_bits));
474  }
475  }
476  template<typename TFieldType>
477  static void ExtractFieldBits_Swap(size_t num_values, size_t address_step,
478  uint64_t mask, size_t shift,
479  const uint8_t *src, TOutputValueType *dst)
480  {
481  for (size_t i = 0; i < num_values; ++i)
482  {
483  const uint8_t *field_ptr = src + i * address_step;
484 
485  TFieldType field_value = ReadSwapped<TFieldType> (field_ptr);
486  uint64_t field_bits = mask & (field_value >> shift);
487  dst[i] = static_cast<TOutputValueType>(EffectiveValue (field_bits));
488  }
489  }
490 
491  /* Common worker for the public read-functions.
492  * Prepares for effective value-by-value read - computes current values
493  * of the possibly dynamic parameters, reads from the register into
494  * the shadow copy. */
495  void PrepareValues()
496  {
497  /* Scan the current dependencies and read the underlying memory of the
498  * value array from the port. Ready for extraction.
499  * This updates all the array "coordinates" (address/step/etc.) and
500  * gets the memory blob from which it can be extracted. */
501  ReadFromPort ();
502 
503  /* Finally, knowing all parameters about the array, we can construct
504  * the value getter function. */
505  if (is_int_reg)
506  {
507  /* Integer register */
508  if (masked_int)
509  {
510  /* Masked integer value.
511  * Supported only with unsigned MaskedIntReg registers, signed refused during consruction. */
512  if (is_signed)
513  {
514  assert (false && "Signed MaskeIntReg should be refused in constructor");
515  throw RUNTIME_EXCEPTION("Unsupported signed masked integer register");
516  }
517 
518  /* Unsigned integer */
519  switch (current_reg_length)
520  {
521  case 8:
522  current_array_getter = &CValueArrayAdapter::GetArrayOfFieldBits<uint64_t>;
523  break;
524 
525  case 4:
526  current_array_getter = &CValueArrayAdapter::GetArrayOfFieldBits<uint32_t>;
527  break;
528 
529  case 2:
530  current_array_getter = &CValueArrayAdapter::GetArrayOfFieldBits<uint16_t>;
531  break;
532 
533  case 1:
534  current_array_getter = &CValueArrayAdapter::GetArrayOfFieldBits<uint8_t>;
535  break;
536 
537  default:
538  throw RUNTIME_EXCEPTION("Unsupported unsigned integer register length");
539  }
540  } /* if (masked_int) */
541  else
542  {
543  /* Full (not masked) integer value */
544  if (is_signed)
545  {
546  /* Signed integer */
547  switch (current_reg_length)
548  {
549  case 8:
550  current_array_getter = &CValueArrayAdapter::GetArrayOfFieldValues<int64_t>;
551  break;
552 
553  case 4:
554  current_array_getter = &CValueArrayAdapter::GetArrayOfFieldValues<int32_t>;
555  break;
556 
557  case 2:
558  current_array_getter = &CValueArrayAdapter::GetArrayOfFieldValues<int16_t>;
559  break;
560 
561  case 1:
562  current_array_getter = &CValueArrayAdapter::GetArrayOfFieldValues<int8_t>;
563  break;
564 
565  default:
566  throw RUNTIME_EXCEPTION("Unsupported signed integer register length");
567  }
568  } /* if (signed) */
569  else
570  {
571  /* Unsigned integer */
572  switch (current_reg_length)
573  {
574  case 8:
575  current_array_getter = &CValueArrayAdapter::GetArrayOfFieldValues<uint64_t>;
576  break;
577 
578  case 4:
579  current_array_getter = &CValueArrayAdapter::GetArrayOfFieldValues<uint32_t>;
580  break;
581 
582  case 2:
583  current_array_getter = &CValueArrayAdapter::GetArrayOfFieldValues<uint16_t>;
584  break;
585 
586  case 1:
587  current_array_getter = &CValueArrayAdapter::GetArrayOfFieldValues<uint8_t>;
588  break;
589 
590  default:
591  throw RUNTIME_EXCEPTION("Unsupported unsigned integer register length");
592  }
593  } /* if-else (signed) */
594  } /* if-else (masked_int) */
595  } /* if (is_int_reg) */
596  else
597  {
598  /* Float register. */
599  switch (current_reg_length)
600  {
601  case 8:
602  current_array_getter = &CValueArrayAdapter::GetArrayOfFieldValues<float64_t>;
603  break;
604 
605  case 4:
606  current_array_getter = &CValueArrayAdapter::GetArrayOfFieldValues<float32_t>;
607  break;
608 
609  default:
610  throw RUNTIME_EXCEPTION("Unsupported float register length");
611  }
612  } /* if-else (is_int_reg) */
613  }
614 
615  template<typename TRawType>
616  static TEffectiveValueType EffectiveValue (TRawType raw_value)
617  {
618  return TypeConverter<TEffectiveValueType>::Convert(raw_value);
619  }
620 
621  template<typename TFieldType>
622  static TFieldType ReadSwapped (const void *ptr)
623  {
624  TFieldType result;
625  SwapExtractor<sizeof(TFieldType)>::Extract (ptr, &result);
626  return result;
627  }
628 
629  private:
630  typedef void (CValueArrayAdapter::*ArrayGetterFunction)(std::vector<TOutputValueType> &values);
631  ArrayGetterFunction current_array_getter;
632  };
633 
634 
640  class CIntegerValueArray : public CValueArrayAdapter<IInteger, int64_t>
641  {
642  public:
643  CIntegerValueArray(IInteger *base_value, IInteger *selector)
644  : CValueArrayAdapter<IInteger, int64_t> (base_value, selector)
645  {
646  }
647  };
648 
654  class CFloatValueArray : public CValueArrayAdapter<IFloat, double>
655  {
656  public:
657  CFloatValueArray(IFloat *base_value, IInteger *selector)
658  : CValueArrayAdapter<IFloat, double> (base_value, selector)
659  {
660  }
661  };
662 
669  class CBooleanValueArray : public CValueArrayAdapter<IBoolean, uint8_t, bool>
670  {
671  public:
672  CBooleanValueArray(IBoolean *base_value, IInteger *selector)
673  : CValueArrayAdapter<IBoolean, uint8_t, bool> (base_value, selector)
674  {
675  }
676  };
677 
678 }
679 
680 #endif // VALUE_ARRAY_ADAPTER_H
681 
CValueArrayAdapter(TValueNodeType *base_value, IInteger *selector)
Creates a CValueArrayAdapter object.
Definition: ValueArrayAdapter.h:226
GENICAM_INTERFACE GENAPI_DECL_ABSTRACT IFloat
Interface for float properties.
Definition: IFloat.h:58
bool IsValid() const
true if the pointer is valid
Definition: Pointer.h:109
Concrete value array implementation to be used with IInteger based target value nodes.
Definition: ValueArrayAdapter.h:640
void GetAllValues(std::vector< TOutputValueType > &values)
Get all values of the array.
Definition: ValueArrayAdapter.h:300
bool IsReadable(EAccessMode AccessMode)
Tests if readable.
Definition: INode.h:176
#define RUNTIME_EXCEPTION
Fires a runtime exception, e.g. throw RUNTIME_EXCEPTION("buh!")
Definition: GCException.h:246
static bool CheckAdvertisedCompatibility(TValueNodeType *base_value, IInteger *selector)
Check value/selector feature pair suitability for use with the adapter.
Definition: ValueArrayAdapter.h:332
Base class wrapping internal implementation details of the value array adapter functionality.
Definition: ValueArrayAdapter.h:151
Encapsulates a GenApi pointer dealing with the dynamic_cast automatically.
Definition: Pointer.h:49
Main include file for using GenApi with smart pointers.
Adapter for accessing structured register known to include an array of selector-iterated values...
Definition: ValueArrayAdapter.h:216
bool IsValid() const
Check if the instance is valid and useable.
Definition: ValueArrayAdapter.h:239
GENICAM_INTERFACE GENAPI_DECL_ABSTRACT IBoolean
Interface for Boolean properties.
Definition: IBoolean.h:59
std::vector< TOutputValueType > GetAllValues()
Get all values of the array.
Definition: ValueArrayAdapter.h:266
GENICAM_INTERFACE INodeMap
Interface to access the node map.
Definition: INode.h:50
Concrete value array implementation to be used with IFloat based target value nodes.
Definition: ValueArrayAdapter.h:654
virtual CLock & GetLock() const =0
Returns the lock which guards the node map.
Standard GenICam Exceptions.
GENICAM_INTERFACE GENAPI_DECL_ABSTRACT IValue
Interface for value properties.
Definition: IValue.h:59
A string class which is a clone of std::string.
Definition: GCString.h:50
Concrete value array implementation to be used with IBoolean based target value nodes.
Definition: ValueArrayAdapter.h:669
GENICAM_INTERFACE IInteger
Interface for integer properties.
Definition: IFloat.h:112
GenICam&#39;s exception class.
Definition: GCException.h:62
Definition of Lock classes.
Definition: ChunkAdapter.h:37
A lock class.
Definition: Synch.h:61