Instrument Neutral Distributed Interface INDI 2.2.5
primalucacommandset.cpp
Go to the documentation of this file.
1/*
2 Primaluca Labs Essato-Arco-Sesto Command Set
3 For USB Control Specification Document Revision 3.3 published 2020.07.08
4
5 Copyright (C) 2022 Jasem Mutlaq
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
21 JM 2022.07.16: Major refactor to using json.h and update to Essato Arco
22 Document protocol revision 3.3 (8th July 2022).
23*/
24
25#include <cmath>
26#include <cstring>
27#include <memory>
28#include <algorithm>
29#include <limits>
30#include <regex>
31
32#include <assert.h>
33#include <termios.h>
34#include <unistd.h>
35#include <sys/ioctl.h>
36
37#include "primalucacommandset.h"
38#include "indicom.h"
39#include "indilogger.h"
40
42{
43
44std::vector<std::string> split(const std::string &input, const std::string &regex)
45{
46 // passing -1 as the submatch index parameter performs splitting
47 std::regex re(regex);
48 std::sregex_token_iterator
49 first{input.begin(), input.end(), re, -1},
50 last;
51 return {first, last};
52}
53
54/******************************************************************************************************
55 * Communication
56*******************************************************************************************************/
57bool Communication::sendRequest(const json &command, json *response)
58{
59 int tty_rc = TTY_OK;
60 int nbytes_written = 0, nbytes_read = 0;
61 tcflush(m_PortFD, TCIOFLUSH);
62
63 std::string output = command.dump();
64 LOGF_DEBUG("<REQ> %s", output.c_str());
65 if ( (tty_rc = tty_write(m_PortFD, output.c_str(), output.length(), &nbytes_written)) != TTY_OK)
66 {
67 char errorMessage[MAXRBUF] = {0};
68 tty_error_msg(tty_rc, errorMessage, MAXRBUF);
69 LOGF_ERROR("Serial write error: %s", errorMessage);
70 return false;
71 }
72
73 // Should we ignore response?
74 if (response == nullptr)
75 return true;
76
77 char read_buf[DRIVER_LEN];
78 do
79 {
80 memset(read_buf, 0, sizeof(read_buf));
81 if ( (tty_rc = tty_read_section(m_PortFD, read_buf, DRIVER_STOP_CHAR, DRIVER_TIMEOUT, &nbytes_read)) != TTY_OK)
82 {
83 char errorMessage[MAXRBUF] = {0};
84 tty_error_msg(tty_rc, errorMessage, MAXRBUF);
85 LOGF_ERROR("Serial write error: %s", errorMessage);
86 return false;
87 }
88 }
89 while (strncmp(read_buf, "ERR:", 4) == 0 && (LOGF_WARN("%s", read_buf), true));
90
91 LOGF_DEBUG("<RES> %s", read_buf);
92
93 try
94 {
95 if (strstr(read_buf, "Error:"))
96 {
97 LOGF_ERROR("Required %s failed: %s", command.dump().c_str(), read_buf);
98 return false;
99 }
100 *response = json::parse(read_buf)["res"];
101 }
102 catch (json::exception &e)
103 {
104 // output exception information
105 LOGF_ERROR("Error parsing device response %s id: %d", e.what(), e.id);
106 return false;
107 }
108
109 return true;
110}
111
112bool Communication::getStringAsDouble(NodeType type, const std::string &parameter, double &value)
113{
114 std::string response;
115 if (get(type, parameter, response))
116 {
117 sscanf(response.c_str(), "%lf", &value);
118 return true;
119 }
120 return false;
121}
122
123template <typename T> bool Communication::get(NodeType type, const std::string &parameter, T &value)
124{
125 std::string node;
126 switch (type)
127 {
128 case MOT_1:
129 node = "MOT1";
130 break;
131 case MOT_2:
132 node = "MOT2";
133 break;
134 default:
135 break;
136 }
137
138 json jsonRequest = {{parameter, ""}};
139 return genericRequest(node, "get", jsonRequest, &value);
140}
141
143{
144 std::string node;
145 switch (type)
146 {
147 case MOT_1:
148 node = "MOT1";
149 break;
150 case MOT_2:
151 node = "MOT2";
152 break;
153 default:
154 break;
155 }
156
157 std::string isDone;
158 if (genericRequest(node, "set", value, &isDone))
159 return isDone == "done";
160 return false;
161}
162
163template <typename T> bool Communication::genericRequest(const std::string &node, const std::string &type,
164 const json &command, T *response)
165{
166 json jsonRequest;
167 if (node.empty())
168 jsonRequest = {{"req", {{type, command}}}};
169 else
170 jsonRequest = {{"req", {{type, {{node, command}}}}}};
171 if (response == nullptr)
172 return sendRequest(jsonRequest);
173 else
174 {
175 json jsonResponse;
176 if (sendRequest(jsonRequest, &jsonResponse))
177 {
178 // There is no command.items().last() so we have to iterate all
179 std::string key;
180 std::string flat = command.flatten().items().begin().key();
181 std::vector<std::string> keys = split(flat, "/");
182 key = keys.back();
183 // for (auto &oneItem : command.items())
184 // key = oneItem.key();
185 try
186 {
187 if (node.empty())
188 {
189 if (jsonResponse[type].contains(key))
190 jsonResponse[type][key].get_to(*response);
191 else if (jsonResponse[type].contains("ERROR"))
192 {
193 std::string error = jsonResponse[type]["ERROR"];
194 LOGF_ERROR("Error: %s", error.c_str());
195 return false;
196 }
197 }
198 else
199 {
200 if (jsonResponse[type][node].contains(key))
201 jsonResponse[type][node][key].get_to(*response);
202 else if (jsonResponse[type][node].contains("ERROR"))
203 {
204 std::string error = jsonResponse[type][node]["ERROR"];
205 LOGF_ERROR("Error: %s", error.c_str());
206 return false;
207 }
208 }
209
210
211 }
212 catch (json::exception &e)
213 {
214 // output exception information
215 LOGF_ERROR("Failed Request: %s\nResponse: %s\nException: %s id: %d", jsonRequest.dump().c_str(),
216 jsonResponse.dump().c_str(),
217 e.what(), e.id);
218 return false;
219 }
220 return true;
221 }
222 }
223
224 return false;
225}
226
227template <typename T> bool Communication::command(NodeType type, const json &jsonCommand)
228{
229 std::string node;
230 switch (type)
231 {
232 case MOT_1:
233 node = "MOT1";
234 break;
235 case MOT_2:
236 node = "MOT2";
237 break;
238 default:
239 break;
240 }
241 std::string response;
242 if (genericRequest(node, "cmd", jsonCommand, &response))
243 return response == "done";
244 return false;
245}
246
247/******************************************************************************************************
248 * Common Focuser functions between SestoSenso2 & Esatto
249*******************************************************************************************************/
250Focuser::Focuser(const std::string &name, int port)
251{
252 m_Communication.reset(new Communication(name, port));
253}
254
255/******************************************************************************************************
256 *
257*******************************************************************************************************/
258bool Focuser::goAbsolutePosition(uint32_t position)
259{
260 return m_Communication->command(MOT_1, {{"MOVE_ABS", {{"STEP", position}}}});
261}
262
263/******************************************************************************************************
264 *
265*******************************************************************************************************/
267{
268 return m_Communication->command(MOT_1, {{"MOT_STOP", ""}});
269}
270
271/******************************************************************************************************
272 *
273*******************************************************************************************************/
275{
276 return m_Communication->command(MOT_1, {{"F_OUTW", ""}});
277}
278
279/******************************************************************************************************
280 *
281*******************************************************************************************************/
283{
284 return m_Communication->command(MOT_1, {{"F_INW", ""}});
285}
286
287/******************************************************************************************************
288 *
289*******************************************************************************************************/
290bool Focuser::getMaxPosition(uint32_t &position)
291{
292 return m_Communication->get(MOT_1, "CAL_MAXPOS", position);
293}
294
295/******************************************************************************************************
296 *
297*******************************************************************************************************/
298bool Focuser::isHallSensorDetected(bool &isDetected)
299{
300 int detected = 0;
301 if (m_Communication->get(MOT_1, "HSENDET", detected))
302 {
303 isDetected = detected == 1;
304 return true;
305 }
306 return false;
307}
308
309/******************************************************************************************************
310 *
311*******************************************************************************************************/
312bool Focuser::getAbsolutePosition(uint32_t &position)
313{
314 // ABS_POS_STEP is the documented mechanical position (Protocol v4.0). ABS_POS is undocumented and
315 // appears to be the raw signed stepper driver register, which can go negative. Fall back to it only
316 // for older firmware that does not report ABS_POS_STEP.
317 constexpr int32_t INVALID_POSITION = std::numeric_limits<int32_t>::min();
318 int32_t steps = INVALID_POSITION;
319 if (m_Communication->get(MOT_1, "ABS_POS_STEP", steps) == false || steps == INVALID_POSITION)
320 {
321 steps = INVALID_POSITION;
322 if (m_Communication->get(MOT_1, "ABS_POS", steps) == false || steps == INVALID_POSITION)
323 return false;
324 }
325
326 if (steps < 0)
327 {
329 "Ignoring invalid negative focuser position %d.", steps);
330 return false;
331 }
332
333 position = static_cast<uint32_t>(steps);
334 return true;
335}
336
337/******************************************************************************************************
338 *
339*******************************************************************************************************/
340bool Focuser::getCurrentSpeed(uint32_t &speed)
341{
342 return m_Communication->get(MOT_1, "SPEED", speed);
343}
344
345/******************************************************************************************************
346 *
347*******************************************************************************************************/
349{
350 return m_Communication->get(MOT_1, "STATUS", status);
351}
352
353/******************************************************************************************************
354 *
355*******************************************************************************************************/
357{
358 json status;
359 if (m_Communication->get(MOT_1, "STATUS", status))
360 {
361 return status["BUSY"] == 1;
362 }
363 return false;
364}
365
366/******************************************************************************************************
367 *
368*******************************************************************************************************/
369bool Focuser::getMotorTemp(double &value)
370{
371 return m_Communication->getStringAsDouble(MOT_1, "NTC_T", value);
372}
373
374/******************************************************************************************************
375 *
376*******************************************************************************************************/
377bool Focuser::getExternalTemp(double &value)
378{
379 return m_Communication->getStringAsDouble(GENERIC_NODE, "EXT_T", value);
380}
381
382/******************************************************************************************************
383 *
384*******************************************************************************************************/
385bool Focuser::getSerialNumber(std::string &response)
386{
387 return m_Communication->get(GENERIC_NODE, "SN", response);
388}
389
390/******************************************************************************************************
391 *
392*******************************************************************************************************/
393bool Focuser::getVoltage12v(double &value)
394{
395 return m_Communication->getStringAsDouble(GENERIC_NODE, "VIN_12V", value);
396}
397
398/******************************************************************************************************
399 *
400*******************************************************************************************************/
401bool Focuser::getFirmwareVersion(std::string &response)
402{
403 json versions;
404 if (m_Communication->get(GENERIC_NODE, "SWVERS", versions))
405 {
406 versions["SWAPP"].get_to(response);
407 return true;
408 }
409 return false;
410}
411
412/******************************************************************************************************
413 *
414*******************************************************************************************************/
415bool Focuser::setBacklash(uint32_t steps)
416{
417 return m_Communication->set(MOT_1, {{"BKLASH", steps}});
418}
419
420/******************************************************************************************************
421 *
422*******************************************************************************************************/
423bool Focuser::getBacklash(uint32_t &steps)
424{
425 return m_Communication->get(MOT_1, "BKLASH", steps);
426}
427
428
429/******************************************************************************************************
430 * SestoSenso2 functions
431*******************************************************************************************************/
432SestoSenso2::SestoSenso2(const std::string &name, int port) : Focuser(name, port)
433{
434}
435
436bool SestoSenso2::getModel(std::string &model)
437{
438 return m_Communication->get(GENERIC_NODE, "MODNAME", model);
439}
440
441/******************************************************************************************************
442 * ===== SESTOSENSO 2 Calibration Methods =====
443 *******************************************************************************************************/
445{
446 return m_Communication->command(MOT_1, {{"CAL_FOCUSER", "Init"}});
447}
448
450{
451 return m_Communication->command(MOT_1, {{"CAL_FOCUSER", "GoOutToFindMaxPos"}});
452}
453
455{
456 return m_Communication->command(MOT_1, {{"CAL_FOCUSER", "StoreAsMaxPos"}});
457}
458
460{
461 return m_Communication->command(MOT_1, {{"CAL_FOCUSER", "StoreAsMinPos"}});
462}
463
464/******************************************************************************************************
465 *
466*******************************************************************************************************/
467bool SestoSenso2::applyMotorPreset(const std::string &name)
468{
469 return m_Communication->command(GENERIC_NODE, {{"RUNPRESET", name}});
470}
471
472/******************************************************************************************************
473 *
474*******************************************************************************************************/
475bool SestoSenso2::setMotorUserPreset(uint32_t index, const MotorRates &rates, const MotorCurrents &currents)
476{
477 auto name = std::string("RUNPRESET_") + std::to_string(index);
478 auto user = std::string("user_") + std::to_string(index);
479
480 json preset = {{"RP_NAME", user},
481 {"M1ACC", rates.accRate},
482 {"M1DEC", rates.decRate},
483 {"M1SPD", rates.runSpeed},
484 {"M1CACC", currents.accCurrent},
485 {"M1CDEC", currents.decCurrent},
486 {"M1CSPD", currents.runCurrent},
487 {"M1CHOLD", currents.holdCurrent}
488 };
489
490 return m_Communication->set(MOT_1, {{name, preset}});
491}
492
493/******************************************************************************************************
494 *
495*******************************************************************************************************/
496bool SestoSenso2::getMotorSettings(MotorRates &rates, MotorCurrents &currents, bool &motorHoldActive)
497{
498 json jsonRequest = {{"req", {{"get", {{"MOT1", { {"FnRUN_ACC", ""},
499 {"FnRUN_DEC", ""}, {"FnRUN_SPD", ""}, {"FnRUN_CURR_ACC", ""},
500 {"FnRUN_CURR_DEC", ""}, {"FnRUN_CURR_SPD", ""}, {"FnRUN_CURR_HOLD", ""}, {"HOLDCURR_STATUS", ""}
501 }
502 }
503 }
504 }
505 }
506 }
507 };
508 json jsonResponse;
509
510 if (m_Communication->sendRequest(jsonRequest, &jsonResponse))
511 {
512 jsonResponse["get"]["MOT1"]["FnRUN_ACC"].get_to(rates.accRate);
513 jsonResponse["get"]["MOT1"]["FnRUN_DEC"].get_to(rates.decRate);
514 jsonResponse["get"]["MOT1"]["FnRUN_SPD"].get_to(rates.runSpeed);
515
516 jsonResponse["get"]["MOT1"]["FnRUN_CURR_ACC"].get_to(currents.accCurrent);
517 jsonResponse["get"]["MOT1"]["FnRUN_CURR_DEC"].get_to(currents.decCurrent);
518 jsonResponse["get"]["MOT1"]["FnRUN_CURR_SPD"].get_to(currents.runCurrent);
519 jsonResponse["get"]["MOT1"]["FnRUN_CURR_HOLD"].get_to(currents.holdCurrent);
520
521 int status = 0;
522 jsonResponse["get"]["MOT1"]["HOLDCURR_STATUS"].get_to(status);
523 motorHoldActive = ( status == 1 );
524 return true;
525 }
526 return false;
527}
528
529/******************************************************************************************************
530 *
531*******************************************************************************************************/
533{
534 json jsonRates =
535 {
536 {"FnRUN_ACC", rates.accRate},
537 {"FnRUN_ACC", rates.accRate},
538 {"FnRUN_ACC", rates.accRate},
539 };
540
541 return m_Communication->set(MOT_1, jsonRates);
542}
543
544/******************************************************************************************************
545 *
546*******************************************************************************************************/
548{
549 json jsonRates =
550 {
551 {"FnRUN_CURR_ACC", currents.accCurrent},
552 {"FnRUN_CURR_DEC", currents.decCurrent},
553 {"FnRUN_CURR_SPD", currents.runCurrent},
554 {"FnRUN_CURR_HOLD", currents.holdCurrent},
555 };
556
557 return m_Communication->set(MOT_1, jsonRates);
558}
559
560/******************************************************************************************************
561 *
562*******************************************************************************************************/
564{
565 return m_Communication->set(MOT_1, {{"HOLDCURR_STATUS", hold ? 1 : 0}});
566}
567
568/******************************************************************************************************
569 * SestoSenso3 functions
570*******************************************************************************************************/
571SestoSenso3::SestoSenso3(const std::string &name, int port) : Focuser(name, port)
572{
573}
574
575bool SestoSenso3::goAbsolutePosition(uint32_t position)
576{
577 return m_Communication->command(MOT_1, {{"GOTO", position}});
578}
579
581{
582 json status;
583 if (m_Communication->get(MOT_1, "STATUS", status))
584 {
585 bool busy = (status["BUSY"] == 1);
586 std::string mst;
587 if (status.contains("MST"))
588 status["MST"].get_to(mst);
589 // Motor is still moving if BUSY=1, or if MST is not yet "stop".
590 // The SS3 can transiently assert BUSY=0 while MST is still
591 // "CstSpeed" or "dec" during encoder-feedback transitions.
592 bool motorStopped = mst.empty() || (mst == "stop");
593 return busy || !motorStopped;
594 }
595 return false;
596}
597
598bool SestoSenso3::getAbsolutePosition(uint32_t &position)
599{
600 return m_Communication->get(MOT_1, "ABS_POS_STEP", position);
601}
602
603bool SestoSenso3::getModel(std::string &model)
604{
605 return m_Communication->get(GENERIC_NODE, "MODNAME", model);
606}
607
608bool SestoSenso3::getSubModel(std::string &submodel)
609{
610 json jsonRequest = {{"req", {{"srv", {{"GET_MODEL_SUBMODEL", ""}}}}}};
611 json jsonResponse;
612
613 if (m_Communication->sendRequest(jsonRequest, &jsonResponse))
614 {
615 try
616 {
617 std::string response;
618 jsonResponse["srv"]["GET_MODEL_SUBMODEL"].get_to(response);
619
620 size_t subModelPos = response.find("SubModel = ");
621 if (subModelPos != std::string::npos)
622 {
623 subModelPos += 11;
624 size_t commaPos = response.find(",", subModelPos);
625 if (commaPos != std::string::npos)
626 submodel = response.substr(subModelPos, commaPos - subModelPos);
627 else
628 submodel = response.substr(subModelPos);
629 return true;
630 }
631 }
632 catch (json::exception &e)
633 {
634 LOGF_ERROR("Error parsing submodel response: %s", e.what());
635 }
636 }
637 return false;
638}
639
641{
642 return m_Communication->set(GENERIC_NODE, {{"RECOVER_DELAY", delay}});
643}
644
646{
647 return m_Communication->get(GENERIC_NODE, "RECOVER_DELAY", delay);
648}
649
650bool SestoSenso3::getMotorSettings(MotorRates &rates, MotorCurrents &currents, bool &motorHoldActive)
651{
652 json jsonRequest = {{"req", {{"get", {{"MOT1", { {"FnRUN_ACC", ""},
653 {"FnRUN_DEC", ""}, {"FnRUN_SPD", ""}, {"FnRUN_CURR_ACC", ""},
654 {"FnRUN_CURR_DEC", ""}, {"FnRUN_CURR_SPD", ""}, {"FnRUN_CURR_HOLD", ""}, {"HOLDCURR_STATUS", ""}
655 }
656 }
657 }
658 }
659 }
660 }
661 };
662 json jsonResponse;
663
664 if (m_Communication->sendRequest(jsonRequest, &jsonResponse))
665 {
666 jsonResponse["get"]["MOT1"]["FnRUN_ACC"].get_to(rates.accRate);
667 jsonResponse["get"]["MOT1"]["FnRUN_DEC"].get_to(rates.decRate);
668 jsonResponse["get"]["MOT1"]["FnRUN_SPD"].get_to(rates.runSpeed);
669
670 jsonResponse["get"]["MOT1"]["FnRUN_CURR_ACC"].get_to(currents.accCurrent);
671 jsonResponse["get"]["MOT1"]["FnRUN_CURR_DEC"].get_to(currents.decCurrent);
672 jsonResponse["get"]["MOT1"]["FnRUN_CURR_SPD"].get_to(currents.runCurrent);
673 jsonResponse["get"]["MOT1"]["FnRUN_CURR_HOLD"].get_to(currents.holdCurrent);
674
675 int status = 0;
676 jsonResponse["get"]["MOT1"]["HOLDCURR_STATUS"].get_to(status);
677 motorHoldActive = (status == 1);
678 return true;
679 }
680 return false;
681}
682
684{
685 json jsonRates =
686 {
687 {"FnRUN_ACC", rates.accRate},
688 {"FnRUN_DEC", rates.decRate},
689 {"FnRUN_SPD", rates.runSpeed},
690 };
691 return m_Communication->set(MOT_1, jsonRates);
692}
693
695{
696 json jsonCurrents =
697 {
698 {"FnRUN_CURR_ACC", currents.accCurrent},
699 {"FnRUN_CURR_DEC", currents.decCurrent},
700 {"FnRUN_CURR_SPD", currents.runCurrent},
701 {"FnRUN_CURR_HOLD", currents.holdCurrent},
702 };
703 return m_Communication->set(MOT_1, jsonCurrents);
704}
705
707{
708 return m_Communication->set(MOT_1, {{"HOLDCURR_STATUS", hold ? 1 : 0}});
709}
710
711bool SestoSenso3::applyMotorPreset(const std::string &name)
712{
713 return m_Communication->command(GENERIC_NODE, {{"RUNPRESET", name}});
714}
715
717{
718 return m_Communication->command(MOT_1, {{"CAL_FOCUSER", "Init"}});
719}
720
722{
723 return m_Communication->command(MOT_1, {{"CAL_FOCUSER", "GoInToFindMinPos"}});
724}
725
727{
728 return m_Communication->command(MOT_1, {{"CAL_FOCUSER", "GoOutToFindMaxPos"}});
729}
730
732{
733 return m_Communication->command(MOT_1, {{"CAL_FOCUSER", "StopMotor"}});
734}
735
737{
738 return m_Communication->command(MOT_1, {{"CAL_FOCUSER", "StoreAsMinPos"}});
739}
740
742{
743 return m_Communication->command(MOT_1, {{"CAL_FOCUSER", "StoreAsMaxPos"}});
744}
745
746bool SestoSenso3::moveIn(uint32_t steps)
747{
748 return m_Communication->command(MOT_1, {{"CAL_FOCUSER", "MoveIn-" + std::to_string(steps)}});
749}
750
751bool SestoSenso3::moveOut(uint32_t steps)
752{
753 return m_Communication->command(MOT_1, {{"CAL_FOCUSER", "MoveOut-" + std::to_string(steps)}});
754}
755
757{
758 return m_Communication->command(MOT_1, {{"CAL_FOCUSER", "start_auto_cal"}});
759}
760
762{
763 return m_Communication->command(MOT_1, {{"CAL_FOCUSER", "stop_calib"}});
764}
765
766/******************************************************************************************************
767 * Esatto functions
768*******************************************************************************************************/
769Esatto::Esatto(const std::string &name, int port) : Focuser(name, port) {}
770bool Esatto::setBacklash(uint32_t steps)
771{
772 return m_Communication->set(MOT_1, {{"BKLASH", steps}});
773}
774
775/******************************************************************************************************
776 *
777*******************************************************************************************************/
778bool Esatto::getBacklash(uint32_t &steps)
779{
780 return m_Communication->get(MOT_1, "BKLASH", steps);
781}
782
783/******************************************************************************************************
784 *
785*******************************************************************************************************/
786bool Esatto::getVoltageUSB(double &value)
787{
788 return m_Communication->getStringAsDouble(GENERIC_NODE, "VIN_USB", value);
789}
790
791bool Esatto::getModel(std::string &model)
792{
793 return m_Communication->get(GENERIC_NODE, "MODNAME", model);
794}
795
796/******************************************************************************************************
797 * Arco
798*******************************************************************************************************/
799Arco::Arco(const std::string &name, int port)
800{
801 m_Communication.reset(new Communication(name, port));
802}
803
804/******************************************************************************************************
805 *
806*******************************************************************************************************/
807bool Arco::setEnabled(bool enabled)
808{
809 return m_Communication->set(GENERIC_NODE, {{"ARCO", enabled ? 1 : 0}});
810}
811
812/******************************************************************************************************
813 *
814*******************************************************************************************************/
816{
817 int enabled = 0;
818 if (m_Communication->get(GENERIC_NODE, "ARCO", enabled))
819 return enabled == 1;
820 return false;
821}
822
823/******************************************************************************************************
824 *
825*******************************************************************************************************/
826bool Arco::getAbsolutePosition(Units unit, double &value)
827{
828 json command;
829 switch (unit)
830 {
831 case UNIT_DEGREES:
832 command = {{"POSITION_DEG", ""}};
833 break;
834 case UNIT_ARCSECS:
835 command = {{"POSITION_ARCSEC", ""}};
836 break;
837 case UNIT_STEPS:
838 command = {{"POSITION_STEP", ""}};
839 break;
840 }
841
842 return m_Communication->genericRequest("MOT2", "get", command, &value);
843}
844
845/******************************************************************************************************
846 *
847*******************************************************************************************************/
848bool Arco::moveAbsolutePoition(Units unit, double value)
849{
850 json command;
851 switch (unit)
852 {
853 case UNIT_DEGREES:
854 command = {{"MOVE_ABS", {{"DEG", value}}}};
855 break;
856 case UNIT_ARCSECS:
857 command = {{"MOVE_ABS", {{"ARCSEC", value}}}};
858 break;
859 case UNIT_STEPS:
860 command = {{"MOVE_ABS", {{"STEP", static_cast<int>(value)}}}};
861 break;
862 }
863
864 return m_Communication->command(MOT_2, command);
865}
866
867/******************************************************************************************************
868 *
869*******************************************************************************************************/
870bool Arco::sync(Units unit, double value)
871{
872 json command;
873 switch (unit)
874 {
875 case UNIT_DEGREES:
876 command = {{"SYNC_POS", {{"DEG", value}}}};
877 break;
878 case UNIT_ARCSECS:
879 command = {{"SYNC_POS", {{"ARCSEC", value}}}};
880 break;
881 case UNIT_STEPS:
882 command = {{"SYNC_POS", {{"STEP", static_cast<int>(value)}}}};
883 break;
884 }
885
886 return m_Communication->command(MOT_2, command);
887}
888
889/******************************************************************************************************
890 *
891*******************************************************************************************************/
893{
894 json status;
895 if (m_Communication->get(MOT_2, "STATUS", status))
896 {
897 return status["BUSY"] == 1;
898 }
899 return false;
900}
901
902/******************************************************************************************************
903 *
904*******************************************************************************************************/
906{
907 return m_Communication->get(MOT_2, "STATUS", status);
908}
909
910/******************************************************************************************************
911 *
912*******************************************************************************************************/
914{
915 return m_Communication->command(MOT_2, {{"MOT_STOP", ""}});
916}
917
918/******************************************************************************************************
919 *
920*******************************************************************************************************/
922{
923 return m_Communication->set(MOT_2, {{"CAL_STATUS", "exec"}});
924}
925
926/******************************************************************************************************
927 *
928*******************************************************************************************************/
930{
931 std::string value;
932 if (m_Communication->get(MOT_2, "CAL_STATUS", value))
933 {
934 return value == "exec";
935 }
936 return false;
937}
938
939/******************************************************************************************************
940 *
941*******************************************************************************************************/
942bool Arco::reverse(bool enabled)
943{
944 return m_Communication->set(MOT_2, {{"REVERSE", enabled ? 1 : 0}});
945}
946
947/******************************************************************************************************
948 *
949*******************************************************************************************************/
951{
952 int value;
953 if (m_Communication->get(MOT_2, "REVERSE", value))
954 {
955 return value == 1;
956 }
957 return false;
958}
959
960/******************************************************************************************************
961 *
962*******************************************************************************************************/
963bool Arco::getSerialNumber(std::string &response)
964{
965 return m_Communication->get(GENERIC_NODE, "ARCO_SN", response);
966}
967
968/******************************************************************************************************
969 *
970*******************************************************************************************************/
971bool Arco::getFirmwareVersion(std::string &response)
972{
973 // JM 2022.07.22: Apparently not possible now in protocol.
974 response = "NA";
975 return true;
976}
977
978bool Arco::getModel(std::string &model)
979{
980 return m_Communication->get(GENERIC_NODE, "MODNAME", model);
981}
982
983
984/******************************************************************************************************
985 *
986*******************************************************************************************************/
988{
989 json jsonRequest = {{"req", {{"get", {{"MOT2", ""}}}}}};
990 return m_Communication->sendRequest(jsonRequest, &info);
991}
992
993/******************************************************************************************************
994 * GIOTTO
995*******************************************************************************************************/
996GIOTTO::GIOTTO(const std::string &name, int port)
997{
998 m_Communication.reset(new Communication(name, port));
999}
1000
1001/******************************************************************************************************
1002 *
1003*******************************************************************************************************/
1004bool GIOTTO::getModel(std::string &model)
1005{
1006 return m_Communication->get(GENERIC_NODE, "MODNAME", model);
1007}
1008
1009/******************************************************************************************************
1010 *
1011*******************************************************************************************************/
1012bool GIOTTO::setLightEnabled(bool enabled)
1013{
1014 json jsonRequest = {{"req", {{"set", {{"LIGHT", enabled ? 1 : 0}}}}}};
1015 return m_Communication->sendRequest(jsonRequest);
1016}
1017
1018
1019/******************************************************************************************************
1020 *
1021*******************************************************************************************************/
1023{
1024 int value;
1025 if (m_Communication->get(GENERIC_NODE, "LIGHT", value))
1026 {
1027 return value == 1;
1028 }
1029 return false;
1030}
1031/******************************************************************************************************
1032 *
1033*******************************************************************************************************/
1034bool GIOTTO::getMaxBrightness(uint16_t &value)
1035{
1036 return m_Communication->get(GENERIC_NODE, "MAX_BRIGHTNESS", value);
1037}
1038
1039/******************************************************************************************************
1040 *
1041*******************************************************************************************************/
1042bool GIOTTO::setBrightness(uint16_t value)
1043{
1044 return m_Communication->set(GENERIC_NODE, {{"BRIGHTNESS", value}});
1045}
1046
1047/******************************************************************************************************
1048 *
1049*******************************************************************************************************/
1050bool GIOTTO::getBrightness(uint16_t &value)
1051{
1052 return m_Communication->get(GENERIC_NODE, "BRIGHTNESS", value);
1053}
1054
1055/******************************************************************************************************
1056 *
1057*******************************************************************************************************/
1058ALTO::ALTO(const std::string &name, int port)
1059{
1060 m_Communication.reset(new Communication(name, port));
1061}
1062
1063/******************************************************************************************************
1064 *
1065*******************************************************************************************************/
1066bool ALTO::getModel(std::string &model)
1067{
1068 return m_Communication->get(GENERIC_NODE, "MODNAME", model);
1069}
1070
1071/******************************************************************************************************
1072 *
1073*******************************************************************************************************/
1075{
1076 return m_Communication->get(MOT_1, "STATUS", status);
1077}
1078
1079/******************************************************************************************************
1080 *
1081*******************************************************************************************************/
1083{
1084 return setPosition(0);
1085}
1086
1087/******************************************************************************************************
1088 *
1089*******************************************************************************************************/
1091{
1092 return setPosition(100);
1093}
1094
1095/******************************************************************************************************
1096 *
1097*******************************************************************************************************/
1098bool ALTO::setPosition(uint8_t value)
1099{
1100 return m_Communication->set(MOT_1, {{"POSITION", value}});
1101}
1102
1103/******************************************************************************************************
1104 *
1105*******************************************************************************************************/
1106bool ALTO::getPosition(uint8_t &value)
1107{
1108 return m_Communication->get(MOT_1, "POSITION", value);
1109}
1110
1111/******************************************************************************************************
1112 *
1113*******************************************************************************************************/
1115{
1116 return m_Communication->command(MOT_1, {{"MOT_STOP", ""}});
1117}
1118
1119/******************************************************************************************************
1120 *
1121*******************************************************************************************************/
1123{
1124 return m_Communication->command(MOT_1, {{"CAL_ALTO", "Init"}});
1125}
1126
1127/******************************************************************************************************
1128 *
1129*******************************************************************************************************/
1130bool ALTO::close(bool fast)
1131{
1132 return m_Communication->command(MOT_1, {{"CAL_ALTO", fast ? "Close_Fast" : "Close_Slow"}});
1133}
1134
1135/******************************************************************************************************
1136 *
1137*******************************************************************************************************/
1138bool ALTO::open(bool fast)
1139{
1140 return m_Communication->command(MOT_1, {{"CAL_ALTO", fast ? "Open_Fast" : "Open_Slow"}});
1141}
1142
1143/******************************************************************************************************
1144 *
1145*******************************************************************************************************/
1147{
1148 return m_Communication->command(MOT_1, {{"CAL_ALTO", "StoreAsClosedPos"}});
1149}
1150
1151/******************************************************************************************************
1152 *
1153*******************************************************************************************************/
1155{
1156 return m_Communication->command(MOT_1, {{"CAL_ALTO", "StoreAsMaxOpenPos"}});
1157}
1158
1159}
ALTO()
Definition alto.cpp:25
Esatto()
Definition esatto.cpp:34
GIOTTO()
Definition giotto.cpp:29
bool close(bool fast=false)
bool getPosition(uint8_t &value)
bool getModel(std::string &model)
bool setPosition(uint8_t value)
bool open(bool fast=false)
bool getStatus(json &status)
bool setEnabled(bool enabled)
bool getModel(std::string &model)
bool getStatus(json &status)
bool getMotorInfo(json &info)
bool moveAbsolutePoition(Units unit, double value)
Arco(const std::string &name, int port)
bool sync(Units unit, double value)
bool getSerialNumber(std::string &response)
bool getAbsolutePosition(Units unit, double &value)
bool reverse(bool enabled)
bool getFirmwareVersion(std::string &response)
bool command(NodeType type, const json &jsonCommand)
bool genericRequest(const std::string &node, const std::string &type, const json &command, T *response=nullptr)
bool sendRequest(const json &command, json *response=nullptr)
bool getStringAsDouble(NodeType type, const std::string &parameter, double &value)
getStringAsDouble Same as get, but it receives a string and scan it for double.
bool get(NodeType type, const std::string &parameter, T &value)
bool set(NodeType type, const json &value)
bool setBacklash(uint32_t steps)
bool getBacklash(uint32_t &steps)
bool getVoltageUSB(double &value)
bool getModel(std::string &model)
bool getVoltage12v(double &value)
std::unique_ptr< Communication > m_Communication
bool getAbsolutePosition(uint32_t &position)
bool getExternalTemp(double &value)
bool isHallSensorDetected(bool &isDetected)
bool getSerialNumber(std::string &response)
Focuser(const std::string &name, int port)
bool getFirmwareVersion(std::string &response)
bool setBacklash(uint32_t steps)
bool getCurrentSpeed(uint32_t &speed)
bool getMotorTemp(double &value)
bool getMaxPosition(uint32_t &position)
bool getBacklash(uint32_t &steps)
bool goAbsolutePosition(uint32_t position)
bool getMaxBrightness(uint16_t &value)
bool getModel(std::string &model)
bool setLightEnabled(bool enabled)
bool getBrightness(uint16_t &value)
bool setBrightness(uint16_t value)
bool getMotorSettings(struct MotorRates &rates, struct MotorCurrents &currents, bool &motorHoldActive)
bool setMotorCurrents(const MotorCurrents &currents)
bool applyMotorPreset(const std::string &name)
bool setMotorUserPreset(uint32_t index, const MotorRates &rates, const MotorCurrents &currents)
bool getModel(std::string &model)
bool setMotorRates(const MotorRates &rates)
bool getModel(std::string &model)
bool setMotorRates(const MotorRates &rates)
bool getSubModel(std::string &submodel)
bool applyMotorPreset(const std::string &name)
bool goAbsolutePosition(uint32_t position)
bool getMotorSettings(MotorRates &rates, MotorCurrents &currents, bool &motorHoldActive)
bool getRecoveryDelay(int32_t &delay)
bool getAbsolutePosition(uint32_t &position)
bool setMotorCurrents(const MotorCurrents &currents)
a class to store JSON values
ValueType & get_to(ValueType &v) const noexcept(noexcept(JSONSerializer< ValueType >::from_json(std::declval< const basic_json_t & >(), v)))
get a value (explicit)
string_t dump(const int indent=-1, const char indent_char=' ', const bool ensure_ascii=false, const error_handler_t error_handler=error_handler_t::strict) const
serialization
bool empty() const noexcept
checks whether the container is empty.
bool contains(const typename object_t::key_type &key) const
check the existence of an element in a JSON object
auto get() const noexcept(noexcept(std::declval< const basic_json_t & >().template get_impl< ValueType >(detail::priority_tag< 4 > {}))) -> decltype(std::declval< const basic_json_t & >().template get_impl< ValueType >(detail::priority_tag< 4 > {}))
get a (pointer) value (explicit)
general exception of the basic_json class
const int id
the id of the exception
const char * what() const noexcept override
returns the explanatory string
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
#define MAXRBUF
Definition indidriver.c:52
#define LOGF_WARN(fmt,...)
Definition indilogger.h:81
#define LOGF_DEBUG(fmt,...)
Definition indilogger.h:83
#define LOGF_ERROR(fmt,...)
Definition indilogger.h:80
#define DEBUGFDEVICE(device, priority, msg,...)
Definition indilogger.h:61
std::vector< std::string > split(const std::string &input, const std::string &regex)
uint16_t type
Definition pwc-ioctl.h:0