MotionLib  1.0.0
SamBuCa motion library
Units.cpp
1 /*
2  * Copyright (C) 2022 CERN
3  *
4  * This software is provided 'as-is', without any express or implied
5  * warranty. In no event will the authors be held liable for any damages
6  * arising from the use of this software.
7  *
8  * Permission is granted to anyone to use this software for any purpose,
9  * including commercial applications, and to alter it and redistribute it
10  * freely, subject to the following restrictions:
11  *
12  * 1. The origin of this software must not be misrepresented; you must not
13  * claim that you wrote the original software. If you use this software
14  * in a product, an acknowledgment in the product documentation would be
15  * appreciated but is not required.
16  * 2. Altered source versions must be plainly marked as such, and must not be
17  * misrepresented as being the original software.
18  * 3. This notice may not be removed or altered from any source distribution.
19  *
20  * Created on: 2022-01-31
21  * Author: Michal Mysior <mmysior> <michal.mysior@cern.ch>
22  *
23  */
24 
25 #include "Units.hpp"
26 
27 #include <unordered_map>
28 
29 #include <ccut/utils.hpp>
30 #include <math.h>
31 
32 #include "Exception.hpp"
33 #include "util/serialize.hpp"
34 
35 using smc::units::Unit;
36 
37 namespace smc {
38 
39 static const std::string s_empty;
40 static const std::unordered_map<Unit, std::string> s_unitToStr{
41  {{units::UNKNOWN, "unknown"},
42  {units::MILLIMETERS, "mm"},
43  {units::METERS, "m"},
44  {units::STEPS, "steps"},
45  {units::DEGREES, "deg"},
46  {units::RADIANS, "rad"},
47  {units::TURNS, "turns"},
48  {units::MILLIMETERS_PER_MINUTE, "mm/min"},
49  {units::METERS_PER_SECOND, "m/s"},
50  {units::STEPS_PER_MILLIMETER, "steps/mm"},
51  {units::STEPS_PER_TURN, "steps/turn"},
52  {units::MILLIMETERS_PER_SECOND, "mm/s"},
53  {units::MILLIMETERS_PER_SECOND_SQUARED, "mm/s^2"}}};
54 static const std::unordered_map<std::string, Unit> s_unitFromStr{
55  ccut::flip(s_unitToStr)};
56 static const std::set<Unit> s_unitKeys{ccut::keys(s_unitToStr)};
57 
58 const std::string &to_string(Unit unit)
59 {
60  std::unordered_map<Unit, std::string>::const_iterator it = s_unitToStr.find(
61  unit);
62  return (it != s_unitToStr.end()) ? it->second : s_empty;
63 }
64 
65 template<>
66 Unit from_string<Unit>(const std::string &str)
67 {
68  std::unordered_map<std::string, Unit>::const_iterator it =
69  s_unitFromStr.find(str);
70  return (it != s_unitFromStr.end()) ? it->second : Unit::UNKNOWN;
71 }
72 
73 template<>
74 const std::set<Unit> &getEnumValues()
75 {
76  return s_unitKeys;
77 }
78 
79 namespace units {
80 
81 Value::Value() : unit{UNKNOWN}, value{0.0} {}
82 
83 Value::Value(Unit unit, value_type value) : unit{unit}, value{value} {}
84 
85 bool Value::canConvert(unit_t unit) const
86 {
87  value_type throwaway;
88  return checkConvert(throwaway, unit);
89 }
90 
91 bool Value::convert(unit_t unit)
92 {
93  if (checkConvert(this->value, unit))
94  {
95  this->unit = unit;
96  return true;
97  }
98  return false;
99 }
100 
101 Value &Value::operator+=(value_type raw)
102 {
103  this->value += raw;
104  return *this;
105 }
106 
107 Value &Value::operator-=(value_type raw)
108 {
109  this->value -= raw;
110  return *this;
111 }
112 
113 Value &Value::operator*=(value_type raw)
114 {
115  this->value *= raw;
116  return *this;
117 }
118 
119 Value &Value::operator/=(value_type raw)
120 {
121  this->value /= raw;
122  return *this;
123 }
124 
125 Value operator*(Value::value_type raw, Unit unit)
126 {
127  return Value{unit, raw};
128 }
129 
130 Value operator"" _mm(long double raw)
131 {
132  return Value{Unit::MILLIMETERS, Value::value_type(raw)};
133 }
134 
135 Value operator"" _m(long double raw)
136 {
137  return Value{Unit::METERS, Value::value_type(raw)};
138 }
139 
140 Value operator"" _steps(long double raw)
141 {
142  return Value{Unit::STEPS, Value::value_type(raw)};
143 }
144 
145 Value operator"" _deg(long double raw)
146 {
147  return Value{Unit::DEGREES, Value::value_type(raw)};
148 }
149 
150 Value operator"" _rad(long double raw)
151 {
152  return Value{Unit::RADIANS, Value::value_type(raw)};
153 }
154 
155 Value operator"" _turns(long double raw)
156 {
157  return Value{Unit::TURNS, Value::value_type(raw)};
158 }
159 
160 Value operator"" _mm_per_min(long double raw)
161 {
162  return Value{Unit::MILLIMETERS_PER_MINUTE, Value::value_type(raw)};
163 }
164 
165 Value operator"" _m_per_s(long double raw)
166 {
167  return Value{Unit::METERS_PER_SECOND, Value::value_type(raw)};
168 }
169 
170 Value operator"" _steps_per_mm(long double raw)
171 {
172  return Value{Unit::STEPS_PER_MILLIMETER, Value::value_type(raw)};
173 }
174 
175 Value operator"" _steps_per_turn(long double raw)
176 {
177  return Value{Unit::STEPS_PER_TURN, Value::value_type(raw)};
178 }
179 
180 Value operator"" _mm_per_s(long double raw)
181 {
182  return Value{Unit::MILLIMETERS_PER_SECOND, Value::value_type(raw)};
183 }
184 
185 Value operator"" _mm_per_s2(long double raw)
186 {
187  return Value{Unit::MILLIMETERS_PER_SECOND_SQUARED, Value::value_type(raw)};
188 }
189 
190 Value operator*(const Value &value1, const Value &value2)
191 {
192  switch (value1.unit)
193  {
194  case Unit::STEPS_PER_MILLIMETER:
195  if (value2.unit == Unit::MILLIMETERS)
196  return Value{Unit::STEPS, (value1.value * value2.value)};
197  break;
198  case Unit::MILLIMETERS:
199  if (value2.unit == Unit::STEPS_PER_MILLIMETER)
200  return Value{Unit::STEPS, (value1.value * value2.value)};
201  break;
202  case Unit::STEPS_PER_TURN:
203  if (value2.unit == Unit::TURNS)
204  return Value{Unit::STEPS, (value1.value * value2.value)};
205  break;
206  default: break;
207  }
208  throw Exception{ErrorCode::IncompatibleUnits};
209 }
210 
211 Value operator/(const Value &value1, const Value &value2)
212 {
213  switch (value1.unit)
214  {
215  case Unit::STEPS:
216  if (value2.unit == Unit::MILLIMETERS)
217  return Value{Unit::STEPS_PER_MILLIMETER,
218  (value1.value / value2.value)};
219  else if (value2.unit == Unit::STEPS_PER_MILLIMETER)
220  return Value{Unit::MILLIMETERS, (value1.value / value2.value)};
221  else if (value2.unit == Unit::STEPS_PER_TURN)
222  return Value{Unit::TURNS, (value1.value / value2.value)};
223  else if (value2.unit == Unit::TURNS)
224  return Value{Unit::STEPS_PER_TURN, (value1.value / value2.value)};
225  break;
226  default: break;
227  }
228  throw Exception{ErrorCode::IncompatibleUnits};
229 }
230 
231 std::string Value::toString() const
232 {
233  return (std::to_string(this->value) + to_string(this->unit));
234 }
235 
236 bool Value::checkConvert(value_type &converted, Unit unit) const
237 {
238  if (this->unit == unit)
239  {
240  return true;
241  }
242 
243  bool ret = true;
244  switch (this->unit)
245  {
246  // degrees <-> radians
247  case unit_t::DEGREES:
248  if (unit == unit_t::RADIANS)
249  converted = (((this->value) * M_PI) / 180);
250  else if (unit == unit_t::TURNS)
251  converted = (this->value / 360);
252  else
253  ret = false;
254  return ret;
255  case unit_t::RADIANS:
256  if (unit == unit_t::DEGREES)
257  converted = (((this->value) * 180) / M_PI);
258  else if (unit == unit_t::TURNS)
259  converted = (this->value / (2 * M_PI));
260  else
261  ret = false;
262  return ret;
263  case unit_t::TURNS:
264  if (unit == unit_t::DEGREES)
265  converted = this->value * 360;
266  else if (unit == unit_t::RADIANS)
267  converted = this->value * (2 * M_PI);
268  else
269  ret = false;
270  return ret;
271  // mm/min -> m/s ; mm/min -> mm/s
272  case Unit::MILLIMETERS_PER_MINUTE:
273  if (unit == Unit::METERS_PER_SECOND)
274  {
275  converted = ((this->value / (60 * 1000)));
276  return true;
277  }
278  else if (unit == Unit::MILLIMETERS_PER_SECOND)
279  {
280  converted = ((this->value / 60));
281  return true;
282  }
283  return false;
284 
285  // m/s -> mm/min ; m/s -> mm/s
286  case Unit::METERS_PER_SECOND:
287  if (unit == Unit::MILLIMETERS_PER_MINUTE)
288  {
289  converted = ((this->value * (60 * 1000)));
290  return true;
291  }
292  else if (unit == Unit::MILLIMETERS_PER_SECOND)
293  {
294  converted = ((this->value * 1000));
295  return true;
296  }
297  return false;
298 
299  // mm/s -> m/s ; mm/s -> mm/min
300  case Unit::MILLIMETERS_PER_SECOND:
301  if (unit == Unit::METERS_PER_SECOND)
302  {
303  converted = ((this->value / 1000));
304  return true;
305  }
306  else if (unit == Unit::MILLIMETERS_PER_MINUTE)
307  {
308  converted = ((this->value * 60));
309  return true;
310  }
311  return false;
312 
313  // mm -> m
314  case Unit::MILLIMETERS:
315  if (unit == Unit::METERS)
316  {
317  converted = ((this->value / 1000));
318  return true;
319  }
320  return false;
321 
322  // m -> mm
323  case Unit::METERS:
324  if (unit == Unit::MILLIMETERS)
325  {
326  converted = ((this->value * 1000));
327  return true;
328  }
329  return false;
330 
331  // default
332  default: return false;
333  }
334 }
335 
336 std::string IOPort::toString(IOPort::StrFmt format) const
337 {
338  if (size == 0)
339  return std::string();
340  else if (format == StrFmt::AUTO && size == 1)
341  return value ? "1" : "0";
342  else if ((format == StrFmt::BIN) ||
343  ((format == StrFmt::AUTO) && (size <= 16)))
344  {
345  std::vector<char> buf(size + (size >> 3) + 3);
346  size_t off = buf.size() - 1;
347  uint64_t v = value;
348 
349  buf[off--] = '\0';
350  /* binary notation */
351  for (size_t i = 0; i < size; ++i)
352  {
353  if (i && !(i & 0x07))
354  buf[off--] = '_';
355  buf[off--] = (v & 1) ? '1' : '0';
356  v >>= 1;
357  }
358  buf[off--] = 'b';
359  buf[off] = '0';
360  return &buf[off];
361  }
362  else
363  {
364  std::vector<char> buf(size + (size >> 5) + 3);
365  size_t off = buf.size() - 1;
366  uint64_t v = value;
367 
368  buf[off--] = '\0';
369  /* hex notation */
370  for (size_t i = 0; i < size;)
371  {
372  if (i && !(i & 0x1F))
373  buf[off--] = '_';
374  const uint8_t quartet = (v & 0x0F);
375  buf[off--] = (quartet >= 10) ? (quartet - 10 + 'A') :
376  (quartet + '0');
377  v >>= 4;
378  i += 4;
379  }
380  buf[off--] = 'x';
381  buf[off] = '0';
382  return &buf[off];
383  }
384 }
385 
386 } /* namespace units */
387 
388 template<>
389 units::io_port_t from_string<units::io_port_t>(const std::string &str)
390 {
391  if (str.empty())
392  return units::IOPort{};
393  else if (str.size() == 1)
394  {
395  switch (str[0])
396  {
397  case '0': return units::IOPort{false};
398  case '1': return units::IOPort{true};
399  default: return units::IOPort{};
400  }
401  }
402  else if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
403  {
404  uint64_t value = 0;
405  size_t sz = 0;
406  for (size_t i = 2; i < str.size(); ++i)
407  {
408  const char c = str[i];
409  if (c == '_')
410  continue;
411 
412  value <<= 4;
413  sz += 4;
414  if (c >= '0' && c <= '9')
415  value += c - '0';
416  else if (c >= 'a' && c <= 'f')
417  value += c - 'a' + 10;
418  else if (c >= 'A' && c <= 'F')
419  value += c - 'A' + 10;
420  else
421  return units::IOPort{};
422  }
423  return units::IOPort{value, sz};
424  }
425  else if (str[0] == '0' && (str[1] == 'b' || str[1] == 'B'))
426  {
427  uint64_t value = 0;
428  size_t sz = 0;
429  for (size_t i = 2; i < str.size(); ++i)
430  {
431  const char c = str[i];
432  if (c == '_')
433  continue;
434 
435  value <<= 1;
436  ++sz;
437  if (c == '1')
438  value |= 1;
439  else if (c != '0')
440  return units::IOPort{};
441  }
442  return units::IOPort{value, sz};
443  }
444  else
445  return units::IOPort{};
446 }
447 
448 } /* namespace smc */
const std::set< AxisPositionMonitor::State > & getEnumValues()
Get devices types list (to be iterated)
const std::string & to_string(Axis::State state)
convert State to string
Definition: Axis.cpp:78
main motion-lib namespace
Definition: Client.cpp:30