Instrument Neutral Distributed Interface INDI  1.9.2
si_efs.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright(c) 2019 Jasem Mutlaq. All rights reserved.
3 
4  Starlight Instruments EFS Focuser
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Library General Public
8  License version 2 as published by the Free Software Foundation.
9  .
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Library General Public License for more details.
14  .
15  You should have received a copy of the GNU Library General Public License
16  along with this library; see the file COPYING.LIB. If not, write to
17  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  Boston, MA 02110-1301, USA.
19 *******************************************************************************/
20 
21 #include "si_efs.h"
22 
23 #include <cmath>
24 #include <cstring>
25 #include <memory>
26 
27 #define FOCUS_SETTINGS_TAB "Settings"
28 
29 static std::unique_ptr<SIEFS> siefs(new SIEFS());
30 
31 const std::map<SIEFS::SI_COMMANDS, std::string> SIEFS::CommandsMap =
32 {
33  {SIEFS::SI_NOOP, "No Operation"},
34  {SIEFS::SI_IN, "Moving Inwards"},
35  {SIEFS::SI_OUT, "Moving Outwards"},
36  {SIEFS::SI_GOTO, "Goto"},
37  {SIEFS::SI_SET_POS, "Set Position"},
38  {SIEFS::SI_MAX_POS, "Set Max Position"},
39  {SIEFS::SI_FAST_IN, "Fast In"},
40  {SIEFS::SI_FAST_OUT, "Fast Out"},
41  {SIEFS::SI_HALT, "Halt"},
42 };
43 
44 const std::map<SIEFS::SI_MOTOR, std::string> SIEFS::MotorMap =
45 {
46  {SIEFS::SI_NOT_MOVING, "Idle"},
47  {SIEFS::SI_MOVING_IN, "Moving Inwards"},
48  {SIEFS::SI_MOVING_OUT, "Moving Outwards"},
49  {SIEFS::SI_LOCKED, "Locked"},
50 };
51 
53 {
54  setVersion(0, 1);
55 
58 }
59 
61 {
62  if (isSimulation())
63  {
65  return true;
66  }
67 
68  handle = hid_open(0x04D8, 0xF056, nullptr);
69 
70  if (handle == nullptr)
71  {
72  LOG_ERROR("No SIEFS focuser found.");
73  return false;
74  }
75  else
76  {
77  uint32_t maximumPosition = 0;
78  bool rc = getMaxPosition(&maximumPosition);
79  if (rc)
80  {
81  FocusMaxPosN[0].value = maximumPosition;
82 
83  FocusAbsPosN[0].min = 0;
84  FocusAbsPosN[0].max = FocusMaxPosN[0].value;
85  FocusAbsPosN[0].step = FocusMaxPosN[0].value / 50.0;
86 
87  FocusSyncN[0].min = 0;
88  FocusSyncN[0].max = FocusMaxPosN[0].value;
89  FocusSyncN[0].step = FocusMaxPosN[0].value / 50.0;
90 
91  FocusRelPosN[0].max = FocusMaxPosN[0].value / 2;
92  FocusRelPosN[0].step = FocusMaxPosN[0].value / 100.0;
93  FocusRelPosN[0].min = 0;
94  }
95 
97  }
98 
99  return (handle != nullptr);
100 }
101 
103 {
104  if (isSimulation() == false)
105  {
106  hid_close(handle);
107  hid_exit();
108  }
109 
110  return true;
111 }
112 
114 {
115  return "SI EFS";
116 }
117 
119 {
121 
123 
124  return true;
125 }
126 
128 {
129  if (!isConnected())
130  return;
131 
132  uint32_t currentTicks = 0;
133 
134  bool rc = getAbsPosition(&currentTicks);
135 
136  if (rc)
137  FocusAbsPosN[0].value = currentTicks;
138 
139  getStatus();
140 
142  {
143  if (isSimulation())
144  {
145  if (FocusAbsPosN[0].value < targetPosition)
146  simPosition += 500;
147  else
148  simPosition -= 500;
149 
150  if (std::abs(simPosition - static_cast<int32_t>(targetPosition)) < 500)
151  {
152  FocusAbsPosN[0].value = targetPosition;
153  simPosition = FocusAbsPosN[0].value;
154  m_Motor = SI_NOT_MOVING;
155  }
156 
157  FocusAbsPosN[0].value = simPosition;
158  }
159 
160  if (m_Motor == SI_NOT_MOVING && targetPosition == FocusAbsPosN[0].value)
161  {
162  if (FocusRelPosNP.s == IPS_BUSY)
163  {
165  IDSetNumber(&FocusRelPosNP, nullptr);
166  }
167 
169  LOG_DEBUG("Focuser reached target position.");
170  }
171  }
172 
173  IDSetNumber(&FocusAbsPosNP, nullptr);
174 
176 }
177 
178 IPState SIEFS::MoveAbsFocuser(uint32_t targetTicks)
179 {
180  bool rc = setAbsPosition(targetTicks);
181 
182  if (!rc)
183  return IPS_ALERT;
184 
185  targetPosition = targetTicks;
186 
187  rc = sendCommand(SI_GOTO);
188 
189  if (!rc)
190  return IPS_ALERT;
191 
193 
194  return IPS_BUSY;
195 }
196 
198 {
199  int direction = (dir == FOCUS_INWARD) ? -1 : 1;
200  int reversed = (FocusReverseS[INDI_ENABLED].s == ISS_ON) ? -1 : 1;
201  int relative = static_cast<int>(ticks);
202 
203  int targetAbsPosition = FocusAbsPosN[0].value + (relative * direction * reversed);
204 
205  targetAbsPosition = std::min(static_cast<uint32_t>(FocusMaxPosN[0].value)
206  , static_cast<uint32_t>(std::max(static_cast<int>(FocusAbsPosN[0].min), targetAbsPosition)));
207 
208  return MoveAbsFocuser(targetAbsPosition);
209 }
210 
211 bool SIEFS::setPosition(uint32_t ticks, uint8_t cmdCode)
212 {
213  int rc = 0;
214  uint8_t command[3];
215  uint8_t response[3];
216 
217  // 20 bit resolution position. 4 high bits + 16 lower bits
218 
219  // Send 4 high bits first
220  command[0] = cmdCode + 8;
221  command[1] = (ticks & 0x40000) >> 16;
222 
223  LOGF_DEBUG("Set %s Position (%ld)", cmdCode == 0x20 ? "Absolute" : "Maximum", ticks);
224  LOGF_DEBUG("CMD <%02X %02X>", command[0], command[1]);
225 
226  if (isSimulation())
227  rc = 2;
228  else
229  rc = hid_write(handle, command, 2);
230 
231  if (rc < 0)
232  {
233  LOGF_ERROR("setPosition: Error writing to device (%s)", hid_error(handle));
234  return false;
235  }
236 
237  if (isSimulation())
238  {
239  rc = 2;
240  response[0] = cmdCode + 8;
241  response[1] = command[1];
242  }
243  else
244  rc = hid_read_timeout(handle, response, 2, SI_TIMEOUT);
245 
246  if (rc < 0)
247  {
248  LOGF_ERROR("setPosition: Error reading from device (%s)", hid_error(handle));
249  return false;
250  }
251 
252  LOGF_DEBUG("RES <%02X %02X>", response[0], response[1]);
253 
254  // Send lower 16 bit
255  command[0] = cmdCode;
256  // Low Byte
257  command[1] = ticks & 0xFF;
258  // High Byte
259  command[2] = (ticks & 0xFF00) >> 8;
260 
261  LOGF_DEBUG("CMD <%02X %02X %02X>", command[0], command[1], command[2]);
262 
263  if (isSimulation())
264  rc = 3;
265  else
266  rc = hid_write(handle, command, 3);
267 
268  if (rc < 0)
269  {
270  LOGF_ERROR("setPosition: Error writing to device (%s)", hid_error(handle));
271  return false;
272  }
273 
274  if (isSimulation())
275  {
276  rc = 3;
277  response[0] = command[0];
278  response[1] = command[1];
279  response[2] = command[2];
280  }
281  else
282  rc = hid_read_timeout(handle, response, 3, SI_TIMEOUT);
283 
284  if (rc < 0)
285  {
286  LOGF_ERROR("setPosition: Error reading from device (%s)", hid_error(handle));
287  return false;
288  }
289 
290  LOGF_DEBUG("RES <%02X %02X %02X>", response[0], response[1], response[2]);
291 
292  targetPosition = ticks;
293 
294  // TODO add checking later
295  return true;
296 }
297 
298 bool SIEFS::getPosition(uint32_t *ticks, uint8_t cmdCode)
299 {
300  int rc = 0;
301  uint32_t pos = 0;
302  uint8_t command[1] = {0};
303  uint8_t response[3] = {0};
304 
305  // 20 bit resolution position. 4 high bits + 16 lower bits
306 
307  // Get 4 high bits first
308  command[0] = cmdCode + 8;
309 
310  LOGF_DEBUG("Get %s Position (High 4 bits)", cmdCode == 0x21 ? "Absolute" : "Maximum");
311  LOGF_DEBUG("CMD <%02X>", command[0]);
312 
313  if (isSimulation())
314  rc = 2;
315  else
316  rc = hid_write(handle, command, 1);
317 
318  if (rc < 0)
319  {
320  LOGF_ERROR("getPosition: Error writing to device (%s)", hid_error(handle));
321  return false;
322  }
323 
324  if (isSimulation())
325  {
326  rc = 2;
327  response[0] = command[0];
328  response[1] = simPosition >> 16;
329  }
330  else
331  rc = hid_read_timeout(handle, response, 2, SI_TIMEOUT);
332 
333  if (rc < 0)
334  {
335  LOGF_ERROR("getPosition: Error reading from device (%s)", hid_error(handle));
336  return false;
337  }
338 
339  LOGF_DEBUG("RES <%02X %02X>", response[0], response[1]);
340 
341  // Store 4 high bits part of a 20 bit number
342  pos = response[1] << 16;
343 
344  // Get 16 lower bits
345  command[0] = cmdCode;
346 
347  LOGF_DEBUG("Get %s Position (Lower 16 bits)", cmdCode == 0x21 ? "Absolute" : "Maximum");
348  LOGF_DEBUG("CMD <%02X>", command[0]);
349 
350  if (isSimulation())
351  rc = 1;
352  else
353  rc = hid_write(handle, command, 1);
354 
355  if (rc < 0)
356  {
357  LOGF_ERROR("getPosition: Error writing to device (%s)", hid_error(handle));
358  return false;
359  }
360 
361  if (isSimulation())
362  {
363  rc = 3;
364  response[0] = command[0];
365  response[1] = simPosition & 0xFF;
366  response[2] = (simPosition & 0xFF00) >> 8;
367  }
368  else
369  rc = hid_read_timeout(handle, response, 3, SI_TIMEOUT);
370 
371  if (rc < 0)
372  {
373  LOGF_ERROR("getPosition: Error reading from device (%s)", hid_error(handle));
374  return false;
375  }
376 
377  LOGF_DEBUG("RES <%02X %02X %02X>", response[0], response[1], response[2]);
378 
379  // Res[1] is lower byte and Res[2] is high byte. Combine them and add them to ticks.
380  pos |= response[1] | response[2] << 8;
381 
382  *ticks = pos;
383 
384  LOGF_DEBUG("%s Position: %ld", cmdCode == 0x21 ? "Absolute" : "Maximum", pos);
385 
386  return true;
387 }
388 
389 bool SIEFS::setAbsPosition(uint32_t ticks)
390 {
391  return setPosition(ticks, 0x20);
392 }
393 
394 bool SIEFS::getAbsPosition(uint32_t *ticks)
395 {
396  return getPosition(ticks, 0x21);
397 }
398 
399 bool SIEFS::setMaxPosition(uint32_t ticks)
400 {
401  return setPosition(ticks, 0x22);
402 }
403 
404 bool SIEFS::getMaxPosition(uint32_t *ticks)
405 {
406  return getPosition(ticks, 0x23);
407 }
408 
409 bool SIEFS::sendCommand(SI_COMMANDS targetCommand)
410 {
411  int rc = 0;
412  uint8_t command[2] = {0};
413  uint8_t response[3] = {0};
414 
415  command[0] = 0x10;
416  command[1] = targetCommand;
417 
418  LOGF_DEBUG("CMD <%02X %02X>", command[0], command[1]);
419 
420  if (isSimulation())
421  rc = 2;
422  else
423  rc = hid_write(handle, command, 2);
424 
425  if (rc < 0)
426  {
427  LOGF_ERROR("setStatus: Error writing to device (%s)", hid_error(handle));
428  return false;
429  }
430 
431  if (isSimulation())
432  {
433  rc = 3;
434  response[0] = command[0];
435  response[1] = 0;
436  response[2] = command[1];
437  // m_Motor = targetCommand;
438  }
439  else
440  rc = hid_read_timeout(handle, response, 3, SI_TIMEOUT);
441 
442  if (rc < 0)
443  {
444  LOGF_ERROR("setStatus: Error reading from device (%s)", hid_error(handle));
445  return false;
446  }
447 
448  LOGF_DEBUG("RES <%02X %02X %02X>", response[0], response[1], response[2]);
449 
450  if (response[1] == 0xFF)
451  {
452  LOG_ERROR("setStatus: Invalid state change.");
453  return false;
454  }
455 
456  return true;
457 }
458 
459 bool SIEFS::getStatus()
460 {
461  int rc = 0;
462  uint8_t command[1] = {0};
463  uint8_t response[2] = {0};
464 
465  command[0] = 0x11;
466 
467  LOGF_DEBUG("CMD <%02X>", command[0]);
468 
469  if (isSimulation())
470  rc = 1;
471  else
472  rc = hid_write(handle, command, 1);
473 
474  if (rc < 0)
475  {
476  LOGF_ERROR("getStatus: Error writing to device (%s)", hid_error(handle));
477  return false;
478  }
479 
480  if (isSimulation())
481  {
482  rc = 2;
483  response[0] = command[0];
484  response[1] = m_Motor;
485  // Halt/SetPos is state = 0 "not moving".
486  // if (response[1] == SI_HALT || response[1] == SI_SET_POS)
487  // response[1] = 0;
488  }
489  else
490  rc = hid_read_timeout(handle, response, 2, SI_TIMEOUT);
491 
492  if (rc < 0)
493  {
494  LOGF_ERROR("getStatus: Error reading from device (%s)", hid_error(handle));
495  return false;
496  }
497 
498  LOGF_DEBUG("RES <%02X %02X>", response[0], response[1]);
499 
500  m_Motor = static_cast<SI_MOTOR>(response[1]);
501  if (MotorMap.count(m_Motor) > 0)
502  LOGF_DEBUG("State: %s", MotorMap.at(m_Motor).c_str());
503  else
504  {
505  LOGF_WARN("Warning: Unknown status (%d)", response[1]);
506  return false;
507  }
508 
509  return true;
510 }
511 
513 {
514  return sendCommand(SI_HALT);
515 }
516 
517 bool SIEFS::SyncFocuser(uint32_t ticks)
518 {
519  bool rc = setAbsPosition(ticks);
520 
521  if (!rc)
522  return false;
523 
524  simPosition = ticks;
525 
526  rc = sendCommand(SI_SET_POS);
527 
528  return rc;
529 }
530 
531 bool SIEFS::SetFocuserMaxPosition(uint32_t ticks)
532 {
533  bool rc = setAbsPosition(ticks);
534 
535  if (!rc)
536  return false;
537 
538  rc = sendCommand(SI_MAX_POS);
539 
540  return rc;
541 }
SIEFS::SI_FAST_OUT
@ SI_FAST_OUT
Definition: si_efs.h:76
INDI::FocuserInterface::FocusAbsPosNP
INumberVectorProperty FocusAbsPosNP
Definition: indifocuserinterface.h:282
INDI::FocuserInterface::FOCUSER_CAN_REL_MOVE
@ FOCUSER_CAN_REL_MOVE
Definition: indifocuserinterface.h:75
IPState
IPState
Property state.
Definition: indiapi.h:158
hid_write
int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length)
Write an Output report to a HID device.
Definition: hid_libusb.c:929
LOGF_ERROR
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
IPS_OK
@ IPS_OK
Definition: indiapi.h:161
_INumberVectorProperty::s
IPState s
Definition: indiapi.h:332
min
double min(void)
SIEFS::TimerHit
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
Definition: si_efs.cpp:127
IPS_ALERT
@ IPS_ALERT
Definition: indiapi.h:163
INDI::DefaultDevice::isSimulation
bool isSimulation() const
Definition: defaultdevice.cpp:734
INDI::FocuserInterface::FOCUSER_CAN_ABS_MOVE
@ FOCUSER_CAN_ABS_MOVE
Definition: indifocuserinterface.h:74
hid_exit
int HID_API_EXPORT hid_exit(void)
Finalize the HIDAPI library.
Definition: hid_libusb.c:407
INDI::FocuserInterface::FOCUSER_CAN_SYNC
@ FOCUSER_CAN_SYNC
Definition: indifocuserinterface.h:78
INDI::DefaultDevice::setVersion
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
Definition: defaultdevice.cpp:1219
SIEFS::SI_NOOP
@ SI_NOOP
Definition: si_efs.h:69
SIEFS::SI_HALT
@ SI_HALT
Definition: si_efs.h:77
max
double max(void)
SIEFS::SI_MAX_POS
@ SI_MAX_POS
Definition: si_efs.h:74
INDI::DefaultDevice::getCurrentPollingPeriod
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
Definition: defaultdevice.cpp:1139
LOGF_DEBUG
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
INDI::DefaultDevice::SetTimer
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
Definition: defaultdevice.cpp:865
SIEFS::SI_GOTO
@ SI_GOTO
Definition: si_efs.h:72
hid_read_timeout
int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
Read an Input report from a HID device with timeout.
Definition: hid_libusb.c:997
SIEFS::Connect
virtual bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
Definition: si_efs.cpp:60
LOGF_WARN
#define LOGF_WARN(fmt,...)
Definition: indilogger.h:81
SIEFS::SI_LOCKED
@ SI_LOCKED
Definition: si_efs.h:67
INDI::FocuserInterface::FOCUS_INWARD
@ FOCUS_INWARD
Definition: indifocuserinterface.h:68
SIEFS::getDefaultName
const char * getDefaultName() override
Definition: si_efs.cpp:113
SIEFS::SI_FAST_IN
@ SI_FAST_IN
Definition: si_efs.h:75
IPS_BUSY
@ IPS_BUSY
Definition: indiapi.h:162
SIEFS::MoveAbsFocuser
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
Definition: si_efs.cpp:178
INDI::FocuserInterface::FocusRelPosN
INumber FocusRelPosN[1]
Definition: indifocuserinterface.h:287
SIEFS::SIEFS
SIEFS()
Definition: si_efs.cpp:52
SIEFS
Definition: si_efs.h:28
hid_error
const HID_API_EXPORT wchar_t *HID_API_CALL hid_error(hid_device *dev)
Get a string describing the last error which occurred.
Definition: hid_libusb.c:1227
SIEFS::SI_MOVING_OUT
@ SI_MOVING_OUT
Definition: si_efs.h:66
SIEFS::SI_SET_POS
@ SI_SET_POS
Definition: si_efs.h:73
SIEFS::SetFocuserMaxPosition
virtual bool SetFocuserMaxPosition(uint32_t ticks) override
SetFocuserMaxPosition Update focuser maximum position. It only updates the PresetNP property limits.
Definition: si_efs.cpp:531
INDI::FocuserInterface::FocusRelPosNP
INumberVectorProperty FocusRelPosNP
Definition: indifocuserinterface.h:286
INDI::BaseDevice::isConnected
bool isConnected() const
Definition: basedevice.cpp:518
LOG_DEBUG
#define LOG_DEBUG(txt)
Definition: indilogger.h:75
INDI::Focuser::CONNECTION_NONE
@ CONNECTION_NONE
Definition: indifocuser.h:54
LOG_ERROR
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition: indilogger.h:72
SIEFS::SI_IN
@ SI_IN
Definition: si_efs.h:70
SIEFS::SI_NOT_MOVING
@ SI_NOT_MOVING
Definition: si_efs.h:64
INDI::FocuserInterface::SetCapability
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
Definition: indifocuserinterface.h:95
INDI::BaseDevice::INDI_ENABLED
@ INDI_ENABLED
Definition: basedevice.h:64
SIEFS::AbortFocuser
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
Definition: si_efs.cpp:512
INDI::FocuserInterface::FocusReverseS
ISwitch FocusReverseS[2]
Definition: indifocuserinterface.h:303
INDI::FocuserInterface::FocusMaxPosN
INumber FocusMaxPosN[1]
Definition: indifocuserinterface.h:291
INDI::FocuserInterface::FOCUSER_CAN_ABORT
@ FOCUSER_CAN_ABORT
Definition: indifocuserinterface.h:76
SIEFS::SyncFocuser
virtual bool SyncFocuser(uint32_t ticks) override
SyncFocuser Set current position to ticks without moving the focuser.
Definition: si_efs.cpp:517
INDI::FocuserInterface::FocusDirection
FocusDirection
Definition: indifocuserinterface.h:66
SIEFS::SI_MOTOR
SI_MOTOR
Definition: si_efs.h:64
si_efs.h
INDI::Focuser::setSupportedConnections
void setSupportedConnections(const uint8_t &value)
setConnection Set Focuser connection mode. Child class should call this in the constructor before Foc...
Definition: indifocuser.cpp:373
INDI::DefaultDevice::addSimulationControl
void addSimulationControl()
Add Simulation control to the driver.
Definition: defaultdevice.cpp:646
SIEFS::Disconnect
virtual bool Disconnect() override
Disconnect from device.
Definition: si_efs.cpp:102
SIEFS::SI_MOVING_IN
@ SI_MOVING_IN
Definition: si_efs.h:65
SIEFS::initProperties
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: si_efs.cpp:118
hid_open
hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
Open a HID device using a Vendor ID (VID), Product ID (PID) and optionally a serial number.
Definition: hid_libusb.c:612
INDI::FocuserInterface::FocusSyncN
INumber FocusSyncN[1]
Definition: indifocuserinterface.h:295
currentTicks
#define currentTicks
Definition: robofocus.cpp:42
INDI::Focuser::initProperties
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: indifocuser.cpp:58
INDI::FocuserInterface::FocusAbsPosN
INumber FocusAbsPosN[1]
Definition: indifocuserinterface.h:283
hid_close
void HID_API_EXPORT hid_close(hid_device *dev)
Close a HID device.
Definition: hid_libusb.c:1163
IDSetNumber
void void void IDSetNumber(const INumberVectorProperty *n, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Tell client to update an existing number vector property.
SIEFS::SI_OUT
@ SI_OUT
Definition: si_efs.h:71
SIEFS::MoveRelFocuser
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
Definition: si_efs.cpp:197
ISS_ON
@ ISS_ON
Definition: indiapi.h:151