Instrument Neutral Distributed Interface INDI 2.2.5
tcfs.cpp
Go to the documentation of this file.
1/*
2 INDI Driver for Optec TCF-S Focuser
3
4 Copyright (C) 2010 Jasem Mutlaq ([email protected])
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20*/
21
22#include "tcfs.h"
23
24#include "indicom.h"
26
27#include <cmath>
28#include <memory>
29#include <cstring>
30#include <termios.h>
31
32#define mydev "Optec TCF-S"
33
34// We declare an auto pointer to TCFS.
35static std::unique_ptr<TCFS> tcfs(new TCFS());
36
37/****************************************************************
38**
39**
40*****************************************************************/
48
49/****************************************************************
50**
51**
52*****************************************************************/
54{
55 //LOGF_DEBUG("%s %s",__FUNCTION__, me);
57
58 // Set upper limit for TCF-S3 focuser
59 if (strcmp(me, "indi_tcfs3_focus") == 0)
60 {
61 isTCFS3 = true;
62
63 FocusMaxPosNP[0].setMax(9999);
64 FocusAbsPosNP[0].setMax(9999);
65 FocusRelPosNP[0].setMax(2000);
66 FocusRelPosNP[0].setStep(100);
67 FocusAbsPosNP[0].setStep(100);
68 FocusRelPosNP[0].setValue(0);
69 LOG_DEBUG("TCF-S3 detected. Updating maximum position value to 9999.");
70 }
71 else
72 {
73 isTCFS3 = false;
74
75 FocusMaxPosNP[0].setMax(7000);
76 FocusAbsPosNP[0].setMax(7000);
77 FocusRelPosNP[0].setMax(2000);
78 FocusRelPosNP[0].setStep(100);
79 FocusAbsPosNP[0].setStep(100);
80 FocusRelPosNP[0].setValue(0);
81 LOG_DEBUG("TCF-S detected. Updating maximum position value to 7000.");
82 }
83
84 IUFillSwitch(&FocusModeS[0], "Manual", "", ISS_ON);
85 IUFillSwitch(&FocusModeS[1], "Auto A", "", ISS_OFF);
86 IUFillSwitch(&FocusModeS[2], "Auto B", "", ISS_OFF);
87 IUFillSwitchVector(&FocusModeSP, FocusModeS, 3, getDeviceName(), "FOCUS_MODE", "Mode", "Main Control", IP_RW,
89
90 IUFillSwitch(&FocusPowerS[0], "FOCUS_SLEEP", "Sleep", ISS_OFF);
91 IUFillSwitch(&FocusPowerS[1], "FOCUS_WAKEUP", "Wake up", ISS_OFF);
92 IUFillSwitchVector(&FocusPowerSP, FocusPowerS, 2, getDeviceName(), "FOCUS_POWER", "Power", "Operation", IP_RW,
94
95 IUFillSwitch(&FocusGotoS[0], "FOCUS_MIN", "Min", ISS_OFF);
96 IUFillSwitch(&FocusGotoS[1], "FOCUS_CENTER", "Center", ISS_OFF);
97 IUFillSwitch(&FocusGotoS[2], "FOCUS_MAX", "Max", ISS_OFF);
98 IUFillSwitch(&FocusGotoS[3], "FOCUS_HOME", "Home", ISS_OFF);
99 IUFillSwitchVector(&FocusGotoSP, FocusGotoS, 4, getDeviceName(), "FOCUS_GOTO", "Go to", "Main Control", IP_RW,
101
102 IUFillNumber(&FocusTemperatureN[0], "FOCUS_TEMPERATURE_VALUE", "Temperature (c)", "%.3f", -50.0, 80, 0, 0);
103 IUFillNumberVector(&FocusTemperatureNP, FocusTemperatureN, 1, getDeviceName(), "FOCUS_TEMPERATURE", "Probe", "Operation",
104 IP_RO, 0, IPS_IDLE);
105
106 IUFillSwitch(&FocusTelemetryS[0], "FOCUS_TELEMETRY_ON", "Enable", ISS_ON);
107 IUFillSwitch(&FocusTelemetryS[1], "FOCUS_TELEMETRY_OFF", "Disable", ISS_OFF);
108 IUFillSwitchVector(&FocusTelemetrySP, FocusTelemetryS, 2, getDeviceName(), "FOCUS_TELEMETRY", "Telemetry", "Operation",
109 IP_RW,
111
112 // Mode parameters
113 IUFillNumber(&FocusModeAN[0], "FOCUS_SLOPE_A", "Slope A", "%.0f", -999, 999, 10, 0);
114 IUFillNumber(&FocusModeAN[1], "FOCUS_INTERCEPT_A", "Intercept A", "%.0f", 0, FocusAbsPosNP[0].getMax(), 10, 0);
115 IUFillNumber(&FocusModeAN[2], "FOCUS_DELAY_A", "Delay A", "%.2f", 0.00, 9.99, 1.0, 0);
116 IUFillNumberVector(&FocusModeANP, FocusModeAN, 3, getDeviceName(), "FOCUS_MODE_A", "Mode A", "Presets", IP_RW, 0, IPS_IDLE);
117 IUFillNumber(&FocusModeBN[0], "FOCUS_SLOPE_B", "Slope B", "%.0f", -999, 999, 10, 0);
118 IUFillNumber(&FocusModeBN[1], "FOCUS_INTERCEPT_B", "Intercept B", "%.0f", 0, FocusAbsPosNP[0].getMax(), 10, 0);
119 IUFillNumber(&FocusModeBN[2], "FOCUS_DELAY_B", "Delay B", "%.2f", 0.00, 9.99, 1.0, 0);
120 IUFillNumberVector(&FocusModeBNP, FocusModeBN, 3, getDeviceName(), "FOCUS_MODE_B", "Mode B", "Presets", IP_RW, 0, IPS_IDLE);
121
122 IUFillSwitch(&FocusStartModeS[0], "FOCUS_START_ON", "Enable", ISS_OFF);
123 IUFillSwitch(&FocusStartModeS[1], "FOCUS_START_OFF", "Disable", ISS_ON);
124 IUFillSwitchVector(&FocusStartModeSP, FocusStartModeS, 2, getDeviceName(), "FOCUS_START_MODE", "Startup Mode", "Presets",
125 IP_RW,
127
128 // Default to 19200
130
132
134
135 return true;
136}
137
138/****************************************************************
139**
140**
141*****************************************************************/
143{
144 //LOGF_DEBUG("%s %s",__FUNCTION__, me);
146
147 if (isConnected())
148 {
149 defineProperty(&FocusGotoSP);
150 defineProperty(&FocusTemperatureNP);
151 defineProperty(&FocusPowerSP);
152 defineProperty(&FocusModeSP);
153 defineProperty(&FocusTelemetrySP);
154 defineProperty(&FocusStartModeSP);
155 defineProperty(&FocusModeANP);
156 defineProperty(&FocusModeBNP);;
158 }
159 else
160 {
161 deleteProperty(FocusGotoSP.name);
162 deleteProperty(FocusTemperatureNP.name);
163 deleteProperty(FocusPowerSP.name);
164 deleteProperty(FocusModeSP.name);
165 deleteProperty(FocusTelemetrySP.name);
166 deleteProperty(FocusStartModeSP.name);
167 deleteProperty(FocusModeANP.name);
168 deleteProperty(FocusModeBNP.name);;
169 }
170
171 return true;
172}
173
174/****************************************************************
175**
176**
177*****************************************************************/
179{
181
182 IUSaveConfigNumber(fp, &FocusModeANP);
183 IUSaveConfigNumber(fp, &FocusModeBNP);
184 IUSaveConfigSwitch(fp, &FocusStartModeSP);
185
186 // Add more properties to config file here
187 // IUSaveConfigSwitch(fp, &SwitchSP);
188 // IUSaveConfigNumber(fp, &NumberNP);
189 // IUSaveConfigText(fp, &TextTP);
190
191 return true;
192}
193
194/****************************************************************
195**
196**
197*****************************************************************/
199{
200 LOGF_DEBUG("A slope=%.0f", FocusModeANP.np[0].value);//, FocusModeAN[0].value);
201 /*
202 char response[TCFS_MAX_CMD] = { 0 };
203 int slope;
204 int is_negative;
205 dispatch_command(FRSLOP, 0, MODE_A);
206 read_tcfs(response);
207 if(sscanf(response, "A=%04d", &slope)<=0)
208 {
209 LOGF_WARN("Failed to read slope A from response: %s", response);
210 return;
211 }
212 response[0] = '\0';
213 dispatch_command(FRSIGN, 0, MODE_A);
214 read_tcfs(response);
215 if(sscanf(response, "A=%01d", &is_negative)<=0)
216 {
217 LOGF_WARN("Failed to read slope sign A from response: %s", response);
218 return;
219 }
220
221 FocusModeANP.np[0].value = slope * (is_negative==1?-1:1);
222 IDSetNumber(&FocusModeANP, nullptr);
223
224 response[0] = '\0';
225 dispatch_command(FRSLOP, 0, MODE_B);
226 read_tcfs(response);
227 if(sscanf(response, "B=%04d", &slope)<=0)
228 {
229 LOGF_WARN("Failed to read slope B from response: %s", response);
230 return;
231 }
232
233 response[0] = '\0';
234 dispatch_command(FRSIGN, 0, MODE_B);
235 read_tcfs(response);
236 if(sscanf(response, "B=%01d", &is_negative)<=0)
237 {
238 LOGF_WARN("Failed to read slope sign B from response: %s", response);
239 return;
240 }
241
242 FocusModeBNP.np[0].value = slope * (is_negative==1?-1:1);
243 IDSetNumber(&FocusModeBNP, nullptr);
244 */
245}
246
247/****************************************************************
248**
249**
250*****************************************************************/
252{
253 LOGF_DEBUG("%s %s", __FUNCTION__, me);
254 if (isSimulation())
255 {
256 LOG_INFO("TCF-S: Simulating connection.");
257 FocusAbsPosNP[0].setValue(simulated_position);
258 return true;
259 }
260 char response[TCFS_MAX_CMD] = { 0 };
261 dispatch_command(FWAKUP);
262 read_tcfs(response);
263 if (strcmp(response, "WAKE") == 0)
264 {
265 LOG_INFO("TCF-S Focuser is awake");
266 tcflush(PortFD, TCIOFLUSH);
267 }
268
269 if(SetManualMode())
270 {
271 LOG_INFO("Successfully connected to TCF-S Focuser in Manual Mode.");
272
273 // Enable temperature readout
274 FocusTemperatureNP.s = IPS_OK;
275
276 return true;
277 }
278 tcflush(PortFD, TCIOFLUSH);
279 LOG_ERROR("Failed connection to TCF-S Focuser.");
280 return false;
281}
282/****************************************************************
283**
284**
285*****************************************************************/
287{
288 char response[TCFS_MAX_CMD] = { 0 };
289 for(int retry = 0; retry < 5; retry++)
290 {
291 dispatch_command(FMMODE);
292 read_tcfs(response);
293 if (strcmp(response, "!") == 0)
294 {
295 tcflush(PortFD, TCIOFLUSH);
296 currentMode = MANUAL;
297 return true;
298 }
299 }
300 tcflush(PortFD, TCIOFLUSH);
301 return false;
302}
303
304/****************************************************************
305**
306**
307*****************************************************************/
309{
310 FocusTemperatureNP.s = IPS_IDLE;
311 IDSetNumber(&FocusTemperatureNP, nullptr);
312
313 dispatch_command(FFMODE);
314
316}
317
318/****************************************************************
319**
320**
321*****************************************************************/
322bool TCFS::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
323{
324 //LOGF_DEBUG("%s %s %s %s",__FUNCTION__, me, dev, name);
325 // first check if it's for our device
326 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
327 {
328 char response[TCFS_MAX_CMD] = { 0 };
329 // In Auto mode only FMMODE can be accepted
330 // In Sleep mode only FWAKUP can be accepted
331 // While focuser is moving don't allow actions other than FMMODE
332
333 if (!strcmp(name, FocusModeANP.name))
334 {
335 IUUpdateNumber(&FocusModeANP, values, names, n);
336
337 dispatch_command(FLSLOP, FocusModeAN[0].value, MODE_A);
338 if (read_tcfs(response) == false)
339 {
340 FocusModeANP.s = IPS_ALERT;
341 IDSetNumber(&FocusModeANP, "Error reading TCF-S reply.");
342 return true;
343 }
344 dispatch_command(FLSIGN, FocusModeAN[0].value, MODE_A);
345 if (read_tcfs(response) == false)
346 {
347 FocusModeANP.s = IPS_ALERT;
348 IDSetNumber(&FocusModeANP, "Error reading TCF-S reply.");
349 return true;
350 }
351 //saveConfig();
352 dispatch_command(FDELAY, FocusModeAN[2].value * 100, MODE_A);
353 if (read_tcfs(response) == false)
354 {
355 FocusModeANP.s = IPS_ALERT;
356 IDSetNumber(&FocusModeANP, "Error reading TCF-S reply.");
357 return true;
358 }
359 //saveConfig();
360 FocusModeANP.s = IPS_OK;
361 IDSetNumber(&FocusModeANP, nullptr);
362
363 return true;
364 }
365 if (!strcmp(name, FocusModeBNP.name))
366 {
367 IUUpdateNumber(&FocusModeBNP, values, names, n);
368
369 dispatch_command(FLSLOP, FocusModeBN[0].value, MODE_B);
370 if (read_tcfs(response) == false)
371 {
372 FocusModeBNP.s = IPS_ALERT;
373 IDSetNumber(&FocusModeBNP, "Error reading TCF-S reply.");
374 return true;
375 }
376 dispatch_command(FLSIGN, FocusModeBN[0].value, MODE_B);
377 if (read_tcfs(response) == false)
378 {
379 FocusModeBNP.s = IPS_ALERT;
380 IDSetNumber(&FocusModeBNP, "Error reading TCF-S reply.");
381 return true;
382 }
383 dispatch_command(FDELAY, FocusModeBN[2].value * 100, MODE_B);
384 if (read_tcfs(response) == false)
385 {
386 FocusModeBNP.s = IPS_ALERT;
387 IDSetNumber(&FocusModeBNP, "Error reading TCF-S reply.");
388 return true;
389 }
390 //saveConfig();
391 FocusModeBNP.s = IPS_OK;
392 IDSetNumber(&FocusModeBNP, nullptr);
393
394 return true;
395 }
396 }
397
398 return Focuser::ISNewNumber(dev, name, values, names, n);
399}
400
401/****************************************************************
402**
403**
404*****************************************************************/
405bool TCFS::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
406{
407 //LOGF_DEBUG("%s %s %s %s",__FUNCTION__, me, dev, name);
408 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
409 {
410 char response[TCFS_MAX_CMD] = { 0 };
411 // In Auto mode only FMMODE can be accepted
412 // In Sleep mode only FWAKUP can be accepted
413 // While focuser is moving don't allow actions other than FMMODE
414 if (FocusMotionSP.isNameMatch(name))
415 {
416 if (FocusModeSP.sp[0].s != ISS_ON)
417 {
418 LOG_WARN("The focuser can only be moved in Manual mode.");
419 return true;
420 }
421 }
423 {
424 LOG_WARN("The focuser is in motion. Wait until it has stopped");
425 return true;
426 }
427 if (!strcmp(FocusPowerSP.name, name))
428 {
429 IUUpdateSwitch(&FocusPowerSP, states, names, n);
430 bool sleep = false;
431
432 ISwitch *sp = IUFindOnSwitch(&FocusPowerSP);
433
434 // Sleep
435 if (!strcmp(sp->name, "FOCUS_SLEEP"))
436 {
437 dispatch_command(FSLEEP);
438 sleep = true;
439 }
440 // Wake Up
441 else
442 dispatch_command(FWAKUP);
443
444 if (read_tcfs(response) == false)
445 {
446 IUResetSwitch(&FocusPowerSP);
447 FocusPowerSP.s = IPS_ALERT;
448 IDSetSwitch(&FocusPowerSP, "Error reading TCF-S reply.");
449 return true;
450 }
451
452 if (sleep)
453 {
454 if (isSimulation())
455 strncpy(response, "ZZZ", TCFS_MAX_CMD);
456
457 if (strcmp(response, "ZZZ") == 0)
458 {
459 FocusPowerSP.s = IPS_OK;
460 IDSetSwitch(&FocusPowerSP, "Focuser is set into sleep mode.");
463 // if (FocusTemperatureNP)
464 {
465 FocusTemperatureNP.s = IPS_IDLE;
466 IDSetNumber(&FocusTemperatureNP, nullptr);
467 }
468
469 return true;
470 }
471 else
472 {
473 FocusPowerSP.s = IPS_ALERT;
474 IDSetSwitch(&FocusPowerSP, "Focuser sleep mode operation failed. Response: %s.", response);
475 return true;
476 }
477 }
478 else
479 {
480 if (isSimulation())
481 strncpy(response, "WAKE", TCFS_MAX_CMD);
482
483 if (strcmp(response, "WAKE") == 0)
484 {
485 FocusPowerSP.s = IPS_OK;
486 IDSetSwitch(&FocusPowerSP, "Focuser is awake.");
489 // if (FocusTemperatureNP)
490 {
491 FocusTemperatureNP.s = IPS_OK;
492 IDSetNumber(&FocusTemperatureNP, nullptr);
493 }
494
495 return true;
496 }
497 else
498 {
499 FocusPowerSP.s = IPS_ALERT;
500 IDSetSwitch(&FocusPowerSP, "Focuser wake up operation failed. Response: %s", response);
501 return true;
502 }
503 }
504 }
505
506 // Do not process any command if focuser is asleep
507 if (isConnected() && FocusPowerSP.sp[0].s == ISS_ON)
508 {
509 auto svp = getSwitch(name);
510 if (svp)
511 {
512 svp.setState(IPS_IDLE);
513 LOG_WARN("Focuser is still in sleep mode. Wake up in order to issue commands.");
514 svp.apply();
515 }
516 return true;
517 }
518
519 if (!strcmp(FocusModeSP.name, name))
520 {
521 IUUpdateSwitch(&FocusModeSP, states, names, n);
522 FocusModeSP.s = IPS_OK;
523
524 ISwitch *sp = IUFindOnSwitch(&FocusModeSP);
525
526 if (!strcmp(sp->name, "Manual"))
527 {
528 if (!isSimulation() && !SetManualMode())
529 {
530 IUResetSwitch(&FocusModeSP);
531 FocusModeSP.s = IPS_ALERT;
532 IDSetSwitch(&FocusModeSP, "Error switching to manual mode. No reply from TCF-S. Try again.");
533 return true;
534 }
535 LOG_INFO("Entered Manual Mode");
536 currentMode = MANUAL;
537 }
538 else if (!strcmp(sp->name, "Auto A"))
539 {
540 if(FocusStartModeSP.sp[0].s == ISS_ON)
541 {
542 FocusModeSP.s = IPS_BUSY;
543 uint32_t startPos = -FocusTemperatureN[0].value * FocusModeAN[0].value
544 + FocusModeAN[1].value;
545 LOGF_DEBUG("Autocomp A T=%.1f; m=%f; i=%f; p0=%d;",
546 FocusTemperatureN[0].value,
547 FocusModeAN[0].value,
548 FocusModeAN[1].value,
549 startPos);
550 MoveAbsFocuser(startPos);
551 }
552 else
553 {
554 dispatch_command(FAMODE);
555 read_tcfs(response);
556 if (!isSimulation() && strcmp(response, "A") != 0)
557 {
558 IUResetSwitch(&FocusModeSP);
559 FocusModeSP.s = IPS_ALERT;
560 IDSetSwitch(&FocusModeSP, "Error switching to Auto Mode A, No reply from TCF-S. Try again.");
561 }
562 LOG_INFO("Entered Auto Mode A");
563 currentMode = MODE_A;
564 }
565 }
566 else
567 {
568 if(FocusStartModeSP.sp[0].s == ISS_ON)
569 {
570 FocusModeSP.s = IPS_BUSY;
571 uint32_t startPos = -FocusTemperatureN[0].value * FocusModeBN[0].value
572 + FocusModeBN[1].value;
573 LOGF_DEBUG("Autocomp B T=%.1f; m=%f; i=%f; p0=%d;",
574 FocusTemperatureN[0].value,
575 FocusModeBN[0].value,
576 FocusModeBN[1].value,
577 startPos);
578 MoveAbsFocuser(startPos);
579 }
580 else
581 {
582 dispatch_command(FBMODE);
583 read_tcfs(response);
584 if (!isSimulation() && strcmp(response, "B") != 0)
585 {
586 IUResetSwitch(&FocusModeSP);
587 FocusModeSP.s = IPS_ALERT;
588 IDSetSwitch(&FocusModeSP, "Error switching to Auto Mode B, No reply from TCF-S. Try again.");
589 }
590 LOG_INFO("Entered Auto Mode B");
591 currentMode = MODE_B;
592 }
593 }
594
595 IDSetSwitch(&FocusModeSP, nullptr);
596 return true;
597 }
598 // Do not process any other command if focuser is in auto mode
599 if (isConnected() && FocusModeSP.sp[0].s != ISS_ON)
600 {
601 auto svp = getSwitch(name);
602 if (svp)
603 {
604 svp.setState(IPS_IDLE);
605 LOG_WARN("Focuser is in auto mode. Change to manual in order to issue commands.");
606 svp.apply();
607 }
608 return true;
609 }
610
611 if (!strcmp(FocusStartModeSP.name, name))
612 {
613 IUUpdateSwitch(&FocusStartModeSP, states, names, n);
614 FocusStartModeSP.s = IPS_OK;
615 // ISwitch *sp = IUFindOnSwitch(&FocusStartModeSP);
616 IDSetSwitch(&FocusStartModeSP, nullptr);
617 LOGF_DEBUG("Start Mode %d", FocusStartModeSP.sp[0].s );
618 return true;
619 }
620
621 if (!strcmp(FocusGotoSP.name, name))
622 {
623 if (FocusModeSP.sp[0].s != ISS_ON)
624 {
625 FocusGotoSP.s = IPS_IDLE;
626 IDSetSwitch(&FocusGotoSP, nullptr);
627 LOG_WARN("The focuser can only be moved in Manual mode.");
628 return false;
629 }
630
631 IUUpdateSwitch(&FocusGotoSP, states, names, n);
632 FocusGotoSP.s = IPS_BUSY;
633
634 ISwitch *sp = IUFindOnSwitch(&FocusGotoSP);
635
636 // Min
637 if (!strcmp(sp->name, "FOCUS_MIN"))
638 {
639 targetTicks = FocusAbsPosNP[0].getValue();
641 IDSetSwitch(&FocusGotoSP, "Moving focuser to minimum position...");
642 }
643 // Center
644 else if (!strcmp(sp->name, "FOCUS_CENTER"))
645 {
646 dispatch_command(FCENTR);
651 IDSetSwitch(&FocusGotoSP, "Moving focuser to center position %d...", isTCFS3 ? 5000 : 3500);
652 return true;
653 }
654 // Max
655 else if (!strcmp(sp->name, "FOCUS_MAX"))
656 {
657 unsigned int delta = 0;
658 delta = FocusAbsPosNP[0].getMax() - FocusAbsPosNP[0].getValue();
660 IDSetSwitch(&FocusGotoSP, "Moving focuser to maximum position %g...", FocusAbsPosNP[0].getMax());
661 }
662 // Home
663 else if (!strcmp(sp->name, "FOCUS_HOME"))
664 {
665 dispatch_command(FHOME);
666 read_tcfs(response);
667
668 if (isSimulation())
669 strncpy(response, "DONE", TCFS_MAX_CMD);
670
671 if (strcmp(response, "DONE") == 0)
672 {
673 IUResetSwitch(&FocusGotoSP);
674 FocusGotoSP.s = IPS_OK;
675 IDSetSwitch(&FocusGotoSP, "Moving focuser to new calculated position based on temperature...");
676 return true;
677 }
678 else
679 {
680 IUResetSwitch(&FocusGotoSP);
681 FocusGotoSP.s = IPS_ALERT;
682 IDSetSwitch(&FocusGotoSP, "Failed to move focuser to home position!");
683 return true;
684 }
685 }
686
687 IDSetSwitch(&FocusGotoSP, nullptr);
688 return true;
689 }
690 // handle quiet mode on/off
691 if (!strcmp(FocusTelemetrySP.name, name))
692 {
693 IUUpdateSwitch(&FocusTelemetrySP, states, names, n);
694
695
696 bool quiet = false;
697
698 ISwitch *sp = IUFindOnSwitch(&FocusTelemetrySP);
699
700 // Telemetry off
701 if (!strcmp(sp->name, "FOCUS_TELEMETRY_OFF"))
702 {
703 dispatch_command(FQUIET, 1);
704 quiet = true;
705 }
706 // Telemetry On
707 else
708 dispatch_command(FQUIET, 0);
709
710 if (read_tcfs(response) == false)
711 {
712 IUResetSwitch(&FocusTelemetrySP);
713 FocusTelemetrySP.s = IPS_ALERT;
714 IDSetSwitch(&FocusTelemetrySP, "Error reading TCF-S reply.");
715 return true;
716 }
717
718 if (isSimulation())
719 strncpy(response, "DONE", TCFS_MAX_CMD);
720
721 if (strcmp(response, "DONE") == 0)
722 {
723 FocusTelemetrySP.s = IPS_OK;
724 IDSetSwitch(&FocusTelemetrySP,
725 quiet ? "Focuser Telemetry is off." : "Focuser Telemetry is on.");
726 // if (FocusTemperatureNP)
727 {
728 FocusTemperatureNP.s = quiet ? IPS_IDLE : IPS_OK;
729 IDSetNumber(&FocusTemperatureNP, nullptr);
730 }
731 return true;
732 }
733 else
734 {
735 FocusTelemetrySP.s = IPS_ALERT;
736 IDSetSwitch(&FocusTelemetrySP, "Focuser telemetry mode failed. Response: %s.", response);
737 return true;
738 }
739 }
740 }
741 return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
742}
743
744IPState TCFS::MoveAbsFocuser(uint32_t targetTicks)
745{
746 int delta = targetTicks - FocusAbsPosNP[0].getValue();
747 LOGF_DEBUG("Moving to absolute position %d using offset %d", targetTicks, delta);
748 return MoveRelFocuser(delta < 0 ? FOCUS_INWARD : FOCUS_OUTWARD, std::abs(delta));
749}
750
752{
753 // if (FocusModeSP.sp[0].s != ISS_ON)
754 // {
755 // LOG_WARN("The focuser can only be moved in Manual mode.");
756 // return IPS_ALERT;
757 // }
758 // The TCFS does not allow any commands othern than FMMODE whilst it is
759 // in auto mode. But this then prevents auto setting of filter offsets during
760 // an imaging sequence. So switch to manual mode, apply the offset then return
761 // to auto mode.
762 targetTicks = ticks;
763 targetPosition = FocusAbsPosNP[0].getValue();
764
765 TCFSMode prevMode = currentMode;
766 if(currentMode != MANUAL)
767 {
769 }
770 // Inward
771 if (dir == FOCUS_INWARD)
772 {
773 targetPosition -= targetTicks;
774 dispatch_command(FIN);
775
776 LOGF_DEBUG("Moving inward by %d steps to position %d", targetTicks, targetPosition);
777 }
778 // Outward
779 else
780 {
781 targetPosition += targetTicks;
782 dispatch_command(FOUT);
783
784 LOGF_DEBUG("Moving outward by %d steps to position %d", targetTicks, targetPosition);
785 }
786
791
792 simulated_position = targetPosition;
793 if(prevMode != MANUAL)
794 {
795 IUResetSwitch(&FocusModeSP);
796 if (prevMode == MODE_A)
797 {
798 FocusModeSP.sp[1].s = ISS_ON;
799 }
800 else
801 {
802 FocusModeSP.sp[2].s = ISS_ON;
803 }
804 FocusModeSP.s = IPS_BUSY;
805 IDSetSwitch(&FocusModeSP, nullptr);
806 }
807
808 return IPS_BUSY;
809}
810
811bool TCFS::dispatch_command(TCFSCommand command_type, int val, TCFSMode m)
812{
813 int err_code = 0, nbytes_written = 0;
814 char command[TCFS_MAX_CMD] = {0};
815
816 switch (command_type)
817 {
818 // Focuser Manual Mode
819 case FMMODE:
820 strncpy(command, "FMMODE", TCFS_MAX_CMD);
821 break;
822
823 // Focuser Free Mode
824 case FFMODE:
825 strncpy(command, "FFMODE", TCFS_MAX_CMD);
826 break;
827 // Focuser Auto-A Mode
828 case FAMODE:
829 strncpy(command, "FAMODE", TCFS_MAX_CMD);
830 break;
831
832 // Focuser Auto-B Mode
833 case FBMODE:
834 strncpy(command, "FBMODE", TCFS_MAX_CMD);
835 break;
836
837 // Focus Center
838 case FCENTR:
839 strncpy(command, "FCENTR", TCFS_MAX_CMD);
840 break;
841
842 // Focuser In “nnnn”
843 case FIN:
844 simulated_position = FocusAbsPosNP[0].getValue();
845
846 snprintf(command, TCFS_MAX_CMD, "FI%04u", targetTicks);
847 break;
848
849 // Focuser Out “nnnn”
850 case FOUT:
851 simulated_position = FocusAbsPosNP[0].getValue();
852
853 snprintf(command, TCFS_MAX_CMD, "FO%04u", targetTicks);
854 break;
855
856 // Focuser Position Read Out
857 case FPOSRO:
858 strncpy(command, "FPOSRO", TCFS_MAX_CMD);
859 break;
860
861 // Focuser Temperature
862 case FTMPRO:
863 strncpy(command, "FTMPRO", TCFS_MAX_CMD);
864 break;
865
866 // Focuser Sleep
867 case FSLEEP:
868 strncpy(command, "FSLEEP", TCFS_MAX_CMD);
869 break;
870 // Focuser Wake Up
871 case FWAKUP:
872 strncpy(command, "FWAKUP", TCFS_MAX_CMD);
873 break;
874 // Focuser Home Command
875 case FHOME:
876 strncpy(command, "FHOME", TCFS_MAX_CMD);
877 break;
878 // Focuser Quiet Command
879 case FQUIET:
880 snprintf(command, TCFS_MAX_CMD, "FQUIT%01d", val);
881 break;
882 // Focuser Load Slope Command
883 case FLSLOP:
884 snprintf(command, TCFS_MAX_CMD, "FL%c%03d", m == MODE_A ? 'A' : 'B', abs(val));
885 break;
886 // Focuser Load Delay Command
887 case FDELAY:
888 snprintf(command, TCFS_MAX_CMD, "FD%c%03d", m == MODE_A ? 'A' : 'B', val);
889 break;
890 // Focuser Load Sign Command
891 case FLSIGN:
892 snprintf(command, TCFS_MAX_CMD, "FZ%cxx%01d", m == MODE_A ? 'A' : 'B', val >= 0 ? 0 : 1);
893 break;
894 // Focuser Read Slope Command
895 case FRSLOP:
896 snprintf(command, TCFS_MAX_CMD, "FREAD%c", m == MODE_A ? 'A' : 'B');
897 break;
898 // Focuser Read Sign Command
899 case FRSIGN:
900 snprintf(command, TCFS_MAX_CMD, "Ftxxx%c", m == MODE_A ? 'A' : 'B');
901 break;
902 case FFWVER:
903 snprintf(command, TCFS_MAX_CMD, "FVxxxx");
904 break;
905 }
906
907 LOGF_DEBUG("CMD <%s>", command);
908
909 if (isSimulation())
910 return true;
911
912 tcflush(PortFD, TCIOFLUSH);
913
914 if ( (err_code = tty_write(PortFD, command, strlen(command), &nbytes_written)) != TTY_OK)
915 {
916 char tcfs_error[TCFS_ERROR_BUFFER];
917 tty_error_msg(err_code, tcfs_error, TCFS_ERROR_BUFFER);
918 LOGF_ERROR("TTY error detected: %s", tcfs_error);
919 return false;
920 }
921
922 return true;
923}
924
926{
927 static double lastPosition = -1, lastTemperature = -1000;
928
929 if (!isConnected())
930 {
932 return;
933 }
934
935 char response[TCFS_MAX_CMD] = { 0 };
936 // If focuser is moving wait until "*" is received
937 // Then set moving indicator to OK
939 {
940 LOGF_DEBUG("%s Motion in Progress...", __FUNCTION__);
941 if (read_tcfs(response, true) == false)
942 {
944 return;
945 }
946 LOGF_DEBUG("%s READY %s", __FUNCTION__, response );
947 if(strcmp(response, "*") == 0)
948 {
953
954 // If the focuser has stopped moving and auto mode is requested then
955 // it is ok to set it now
956 if(FocusModeSP.s == IPS_BUSY && // Had to move before going Auto
957 (FocusModeSP.sp[1].s == ISS_ON || FocusModeSP.sp[2].s == ISS_ON))
958 {
959 const char* mode = (FocusModeSP.sp[1].s == ISS_ON) ? "A" : "B";
960 dispatch_command(
961 (FocusModeSP.sp[1].s == ISS_ON) ? FAMODE : FBMODE);
962 read_tcfs(response);
963 if (!isSimulation() && strcmp(response, mode) != 0)
964 {
965 IUResetSwitch(&FocusModeSP);
966 FocusModeSP.s = IPS_ALERT;
967 IDSetSwitch(&FocusModeSP, "Error switching to Auto Mode %s. No reply from TCF-S. Try again.", mode);
969 return;
970 }
971 FocusModeSP.s = IPS_OK;
972 LOGF_INFO("Entered Auto Mode %s", mode);
973 currentMode = (FocusModeSP.sp[1].s == ISS_ON) ? MODE_A : MODE_B;
974 IDSetSwitch(&FocusModeSP, nullptr);
975 }
977 return;
978 }
979 }
980
981 int f_position = 0;
982 float f_temperature = 0;
983
984 if(!isSimulation() && currentMode != MANUAL)
985 {
986 if (FocusTelemetrySP.sp[1].s == ISS_ON)
987 {
988 LOGF_DEBUG("%s %s", __FUNCTION__, "Telemetry is off");
990 return;
991 }
992 for(int i = 0; i < 2; i++)
993 {
994 if (read_tcfs(response, true) == false)
995 {
997 return;
998 }
999 LOGF_DEBUG("%s Received %s", __FUNCTION__, response);
1000 if(sscanf(response, "P=%d", &f_position) == 1)
1001 {
1002 FocusAbsPosNP[0].setValue(f_position);
1003 if (lastPosition != FocusAbsPosNP[0].getValue())
1004 {
1005 lastPosition = FocusAbsPosNP[0].getValue();
1007 }
1008 }
1009 else if(sscanf(response, "T=%f", &f_temperature) == 1)
1010 {
1011 FocusTemperatureNP.np[0].value = f_temperature;
1012
1013 if (lastTemperature != FocusTemperatureNP.np[0].value)
1014 {
1015 lastTemperature = FocusTemperatureNP.np[0].value;
1016 IDSetNumber(&FocusTemperatureNP, nullptr);
1017 }
1018 }
1019 }
1021 return;
1022 }
1023
1024 if (FocusGotoSP.s == IPS_BUSY)
1025 {
1026 ISwitch *sp = IUFindOnSwitch(&FocusGotoSP);
1027
1028 if (sp != nullptr && strcmp(sp->name, "FOCUS_CENTER") == 0)
1029 {
1030 bool rc = read_tcfs(response, true);
1031
1032 if (!rc)
1033 {
1035 return;
1036 }
1037
1038 if (isSimulation())
1039 strncpy(response, "CENTER", TCFS_MAX_CMD);
1040
1041 if (strcmp(response, "CENTER") == 0)
1042 {
1043 IUResetSwitch(&FocusGotoSP);
1044 FocusGotoSP.s = IPS_OK;
1046
1047 IDSetSwitch(&FocusGotoSP, nullptr);
1049
1050 LOG_INFO("Focuser moved to center position.");
1051 }
1052 }
1053 }
1054
1055 switch (FocusAbsPosNP.getState())
1056 {
1057 case IPS_OK:
1058 if (FocusModeSP.sp[0].s == ISS_ON)
1059 dispatch_command(FPOSRO);
1060
1061 if (read_tcfs(response) == false)
1062 {
1064 return;
1065 }
1066
1067 if (isSimulation())
1068 snprintf(response, TCFS_MAX_CMD, "P=%04d", (int)simulated_position);
1069
1070 sscanf(response, "P=%d", &f_position);
1071 FocusAbsPosNP[0].setValue(f_position);
1072
1073 if (lastPosition != FocusAbsPosNP[0].getValue())
1074 {
1075 lastPosition = FocusAbsPosNP[0].getValue();
1077 }
1078 break;
1079
1080 case IPS_BUSY:
1081 if (read_tcfs(response, true) == false)
1082 {
1084 return;
1085 }
1086
1087 // Ignore error
1088 if (strstr(response, "ER") != nullptr)
1089 {
1090 LOGF_DEBUG("Received error: %s", response);
1092 return;
1093 }
1094
1095 if (isSimulation())
1096 strncpy(response, "*", 2);
1097
1098 if (strcmp(response, "*") == 0)
1099 {
1100 LOGF_DEBUG("Moving focuser %d steps to position %d.", targetTicks, targetPosition);
1103 FocusGotoSP.s = IPS_OK;
1106 IDSetSwitch(&FocusGotoSP, nullptr);
1107 }
1108 else
1109 {
1111 LOGF_ERROR("Unable to read response from focuser #%s#.", response);
1113 }
1114 break;
1115
1116 default:
1117 break;
1118 }
1119
1120 if (FocusTemperatureNP.s == IPS_OK || FocusTemperatureNP.s == IPS_BUSY)
1121 {
1122 // Read Temperature
1123 // Manual Mode
1124 if (FocusModeSP.sp[0].s == ISS_ON)
1125 dispatch_command(FTMPRO);
1126
1127 if (read_tcfs(response) == false)
1128 {
1129 FocusTemperatureNP.s = IPS_ALERT;
1130 IDSetNumber(&FocusTemperatureNP, nullptr);
1131 LOG_ERROR("Failed to read temperature. Is sensor connected?");
1132
1134 return;
1135 }
1136
1137 if (isSimulation())
1138 snprintf(response, TCFS_MAX_CMD, "T=%0.1f", simulated_temperature);
1139
1140 int rc = sscanf(response, "T=%f", &f_temperature);
1141
1142 if (rc == 1)
1143 {
1144 FocusTemperatureNP.np[0].value = f_temperature;
1145
1146 if (fabs(lastTemperature - FocusTemperatureNP.np[0].value) > 0.01)
1147 {
1148 lastTemperature = FocusTemperatureNP.np[0].value;
1149 IDSetNumber(&FocusTemperatureNP, nullptr);
1150 }
1151 }
1152 else
1153 {
1154 FocusTemperatureNP.s = IPS_ALERT;
1155 LOGF_ERROR("Failed to read temperature: %s", response);
1156 IDSetNumber(&FocusTemperatureNP, nullptr);
1157 }
1158 }
1159
1161}
1162
1163bool TCFS::read_tcfs(char *response, bool silent)
1164{
1165 int err_code = 0, nbytes_read = 0;
1166
1167 if (isSimulation())
1168 {
1169 strncpy(response, "SIMULATION", TCFS_MAX_CMD);
1170 return true;
1171 }
1172
1173 // Read until encountring a CR
1174 if ((err_code = tty_read_section(PortFD, response, 0x0D, 2, &nbytes_read)) != TTY_OK)
1175 {
1176 if (!silent)
1177 {
1178 char err_msg[TCFS_ERROR_BUFFER];
1179 tty_error_msg(err_code, err_msg, 32);
1180 LOGF_ERROR("TTY error detected: %s", err_msg);
1181 }
1182
1183 return false;
1184 }
1185
1186 // Remove LF & CR
1187 response[nbytes_read - 2] = '\0';
1188
1189 tcflush(PortFD, TCIOFLUSH);
1190
1191 if (strstr(response, "ER="))
1192 {
1193 int errorCode = 0;
1194 sscanf(response, "ER=%d", &errorCode);
1195 LOGF_ERROR("Error Code <%d>", errorCode);
1196 return false;
1197 }
1198
1199 LOGF_DEBUG("RES <%s>", response);
1200 return true;
1201}
1202
1204{
1205 return mydev;
1206}
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
bool isConnected() const
INDI::PropertySwitch getSwitch(const char *name) const
virtual bool Disconnect()
Disconnect from device.
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
bool isSimulation() const
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
INDI::PropertyNumber FocusRelPosNP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
INDI::PropertySwitch FocusMotionSP
INDI::PropertyNumber FocusMaxPosNP
INDI::PropertyNumber FocusAbsPosNP
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Processes incoming new switch states from the client for this device. Handles updates to FocuserInter...
virtual bool saveConfigItems(FILE *fp) override
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
Connection::Serial * serialConnection
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
bool isNameMatch(const char *otherName) const
Definition tcfs.h:32
void GetFocusParams()
Definition tcfs.cpp:198
virtual bool saveConfigItems(FILE *fp) override
Definition tcfs.cpp:178
TCFS()
Definition tcfs.cpp:41
@ FDELAY
Definition tcfs.h:51
@ FQUIET
Definition tcfs.h:50
@ FPOSRO
Definition tcfs.h:43
@ FWAKUP
Definition tcfs.h:46
@ FBMODE
Definition tcfs.h:39
@ FMMODE
Definition tcfs.h:36
@ FAMODE
Definition tcfs.h:38
@ FFWVER
Definition tcfs.h:54
@ FOUT
Definition tcfs.h:42
@ FHOME
Definition tcfs.h:47
@ FLSIGN
Definition tcfs.h:53
@ FTMPRO
Definition tcfs.h:44
@ FFMODE
Definition tcfs.h:37
@ FSLEEP
Definition tcfs.h:45
@ FRSLOP
Definition tcfs.h:48
@ FRSIGN
Definition tcfs.h:52
@ FLSLOP
Definition tcfs.h:49
@ FCENTR
Definition tcfs.h:40
@ FIN
Definition tcfs.h:41
virtual bool Handshake() override
Performs a device-specific handshake after a connection is established. This method should be overrid...
Definition tcfs.cpp:251
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
Definition tcfs.cpp:53
const char * getDefaultName() override
Definition tcfs.cpp:1203
bool SetManualMode()
Definition tcfs.cpp:286
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
Definition tcfs.cpp:751
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
Definition tcfs.cpp:925
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Processes incoming new number values from the client for this device. Handles updates to FocuserInter...
Definition tcfs.cpp:322
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
Definition tcfs.cpp:142
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Processes incoming new switch states from the client for this device. Handles updates to FocuserInter...
Definition tcfs.cpp:405
virtual bool Disconnect() override
Disconnect from device.
Definition tcfs.cpp:308
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
Definition tcfs.cpp:744
TCFSMode
Definition tcfs.h:58
@ MANUAL
Definition tcfs.h:59
@ MODE_B
Definition tcfs.h:61
@ MODE_A
Definition tcfs.h:60
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
@ IP_RW
Definition indiapi.h:184
@ IP_RO
Definition indiapi.h:182
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_1OFMANY
Definition indiapi.h:171
@ ISR_ATMOST1
Definition indiapi.h:172
int tty_read_section(int fd, char *buf, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
Definition indicom.c:653
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition indicom.c:418
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition indicom.c:1243
Implementations for common driver routines.
@ TTY_OK
Definition indicom.h:54
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition indidevapi.c:33
void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a number vector property. The vector's auxiliary elements will be set to NULL.
Definition indidevapi.c:490
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition indidevapi.c:366
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition indidevapi.c:23
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
Definition indidevapi.c:376
ISwitch * IUFindOnSwitch(const ISwitchVectorProperty *svp)
Returns the first ON switch it finds in the vector switch property.
Definition indidevapi.c:317
void IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max, double step, double value)
Assign attributes for a number property. The number's auxiliary elements will be set to NULL.
Definition indidevapi.c:398
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
Definition indidevapi.c:453
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
char * me
Definition indidriver.c:50
#define LOGF_INFO(fmt,...)
Definition indilogger.h:82
#define LOG_DEBUG(txt)
Definition indilogger.h:75
#define LOG_WARN(txt)
Definition indilogger.h:73
#define LOGF_DEBUG(fmt,...)
Definition indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition indilogger.h:80
#define LOG_INFO(txt)
Definition indilogger.h:74
#define getDeviceName
One switch descriptor.
char name[MAXINDINAME]
Definition indiapi.h:321
double value
Definition indiapi.h:288
char name[MAXINDINAME]
Definition indiapi.h:369
ISState s
Definition indiapi.h:353
#define mydev
Definition tcfs.cpp:32
#define TCFS_MAX_CMD
Definition tcfs.h:28
#define TCFS_ERROR_BUFFER
Definition tcfs.h:29