diff --git a/MMPP_lib/99-edmund_hsfw.rules b/MMPP_lib/99-edmund_hsfw.rules new file mode 100644 index 0000000..daadae6 --- /dev/null +++ b/MMPP_lib/99-edmund_hsfw.rules @@ -0,0 +1 @@ +SUBSYSTEMS=="usb", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="82cd", MODE:="0666" diff --git a/MMPP_lib/CMakeLists.txt b/MMPP_lib/CMakeLists.txt index 450d4ae..68abc7c 100644 --- a/MMPP_lib/CMakeLists.txt +++ b/MMPP_lib/CMakeLists.txt @@ -62,11 +62,14 @@ endif() endif(0) ###### pkgconfig ###### -# pkg-config modules (for pkg-check-modules) -#set(MODULES cfitsio fftw3) +set(MODULES libudev) # find packages: -#find_package(PkgConfig REQUIRED) -#pkg_check_modules(${PROJ} REQUIRED ${MODULES}) +find_package(PkgConfig REQUIRED) +pkg_check_modules(${PROJ} REQUIRED ${MODULES}) +pkg_check_modules(USB libusb) +if(NOT USB_FOUND) + pkg_check_modules(USB REQUIRED libusb-1.0) +endif() # external modules like OpenMP: include(FindOpenMP) @@ -88,7 +91,7 @@ set(RU_FILE ${LCPATH}/ru.po) # library add_library(${PROJ} SHARED ${SOURCES}) # library header files -set(LIBHEADER "usefull_macros.h") +set(LIBHEADER "libmmpp.h") # -I include_directories(${${PROJ}_INCLUDE_DIRS}) # -L @@ -114,6 +117,8 @@ include(GNUInstallDirs) install(TARGETS ${PROJ} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(FILES ${PCFILE} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) +install(FILES "99-edmund_hsfw.rules" DESTINATION "/etc/udev/rules.d") + # EXAMPLES if(DEFINED EXAMPLES AND EXAMPLES EQUAL 1) diff --git a/MMPP_lib/common.h b/MMPP_lib/common.h new file mode 100644 index 0000000..b0f5b08 --- /dev/null +++ b/MMPP_lib/common.h @@ -0,0 +1,67 @@ +/* + * This file is part of the libmmpp project. + * Copyright 2019 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +// common macros +#pragma once +#ifndef COMMON_H__ +#define COMMON_H__ + +#include +#include + +// unused arguments of functions +#define _U_ __attribute__((__unused__)) +// break absent in `case` +#define FALLTHRU __attribute__ ((fallthrough)) +// and synonym for FALLTHRU +#define NOBREAKHERE __attribute__ ((fallthrough)) +// weak functions +#define WEAK __attribute__ ((weak)) + +#define COLOR_RED "\033[1;31;40m" +#define COLOR_GREEN "\033[1;32;40m" +#define COLOR_OLD "\033[0;0;0m" +/* + * print function name, debug messages + * debug mode, -DEBUG + */ +#ifdef EBUG + #define FNAME() do{ fprintf(stderr, COLOR_OLD); \ + fprintf(stderr, "\n%s (%s, line %d)\n", __func__, __FILE__, __LINE__);} while(0) + #define DBG(...) do{ fprintf(stderr, COLOR_OLD); \ + fprintf(stderr, "%s (%s, line %d): ", __func__, __FILE__, __LINE__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n");} while(0) +#else + #define FNAME() do{}while(0) + #define DBG(...) do{}while(0) +#endif //EBUG + +/* + * Memory allocation + */ +#define ALLOC(type, var, size) type * var = ((type *)my_alloc(size, sizeof(type))) +#define MALLOC(type, size) ((type *)my_alloc(size, sizeof(type))) +#define FREE(ptr) do{if(ptr){free(ptr); ptr = NULL;}}while(0) + +#ifndef DBL_EPSILON +#define DBL_EPSILON (2.2204460492503131e-16) +#endif + +void *my_alloc(size_t N, size_t S); + +#endif // COMMON_H__ diff --git a/MMPP_lib/examples/template.c b/MMPP_lib/examples/template.c new file mode 100644 index 0000000..aef2a90 --- /dev/null +++ b/MMPP_lib/examples/template.c @@ -0,0 +1,63 @@ +/* + * This file is part of the libmmpp project. + * Copyright 2019 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "tmcmdlnopts.h" +#include +#include +#include +#include + +// All return states of main(): +enum{ + RET_ALLOK = 0, + RET_NOTFOUND, // none of turrets found or didn't found seeking MCU + RET_ONLYONE, // only one turret found + RET_COMMERR, // communication error + RET_CANTINIT, // can't init turrets + RET_ERROR = 9, // uncoverable error - from libsnippets + RET_HELPCALL = 255 // user call help (or give wrong parameter[s]) - from libsnippets +}; +static glob_pars *G; + +/** + * We REDEFINE the default WEAK function of signal processing + */ +void __attribute__((noreturn)) signals(int sig){ + if(sig){ + signal(sig, SIG_IGN); + DBG("Get signal %d, quit.\n", sig); + } + if(G->pidfile) // remove unnesessary PID file + unlink(G->pidfile); + restore_console(); + mmpp_close(); + exit(sig); +} + +int main(int argc, char **argv){ + initial_setup(); + signal(SIGTERM, signals); // kill (-15) + signal(SIGINT, signals); // ctrl+C + signal(SIGQUIT, SIG_IGN); // ctrl+\ . + signal(SIGTSTP, SIG_IGN); // ctrl+Z + setbuf(stdout, NULL); + G = parse_args(argc, argv); + check4running(NULL, G->pidfile); + ; + signals(0); +} diff --git a/MMPP_lib/examples/templatecmdlnopts.c b/MMPP_lib/examples/templatecmdlnopts.c new file mode 100644 index 0000000..c2872fa --- /dev/null +++ b/MMPP_lib/examples/templatecmdlnopts.c @@ -0,0 +1,86 @@ +/* + * This file is part of the libmmpp project. + * Copyright 2019 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "templatecmdlnopts.h" +#include +#include +#include +#include +#include + +/* + * here are global parameters initialisation + */ +static int help; +static glob_pars G; + +int quiet = 0; // less messages @ stdout + +// DEFAULTS +// default global parameters +glob_pars const Gdefault = { + .pidfile = "/tmp/MMPP_wheels.pid" +}; + +/* + * Define command line options by filling structure: + * name has_arg flag val type argptr help +*/ +static myoption cmdlnopts[] = { + {"help", NO_ARGS, NULL, 'h', arg_none, APTR(&help), N_("show this help")}, + {"quiet", NO_ARGS, NULL, 'q', arg_none, APTR(&quiet), N_("don't show anything @screen from stdout")}, + {"pidfile", NEED_ARG, NULL, 'p', arg_string, APTR(&G.pidfile), N_("PID-file name")}, + {"status", NO_ARGS, NULL, 'S', arg_none, APTR(&G.getstatus), N_("get device status")}, + end_option +}; + + +/** + * Parse command line options and return dynamically allocated structure + * to global parameters + * @param argc - copy of argc from main + * @param argv - copy of argv from main + * @return allocated structure with global parameters + */ +glob_pars *parse_args(int argc, char **argv){ + void *ptr; + ptr = memcpy(&G, &Gdefault, sizeof(G)); assert(ptr); + // format of help: "Usage: progname [args]\n" + // parse arguments + parseargs(&argc, &argv, cmdlnopts); + if(help) showhelp(-1, cmdlnopts); + if(argc > 0){ + WARNX("%d unused parameters:\n", argc); + for(int i = 0; i < argc; ++i) + printf("\t%4d: %s\n", i+1, argv[i]); + } + return &G; +} + +/** + * @brief MSG show coloured message if `quiet` not set + * !! This function adds trailing '\n' to message + * @param s1 - green part of message (may be null) + * @param s2 - normal colored part of messate (may be null) + */ +void MSG(const char *s1, const char *s2){ + if(quiet) return; + if(s1){ + green("%s%s", s1, s2 ? ": " : "\n"); + } + if(s2) printf("%s\n", s2); +} diff --git a/MMPP_lib/examples/templatecmdlnopts.h b/MMPP_lib/examples/templatecmdlnopts.h new file mode 100644 index 0000000..1ecc72c --- /dev/null +++ b/MMPP_lib/examples/templatecmdlnopts.h @@ -0,0 +1,40 @@ +/* + * This file is part of the libmmpp project. + * Copyright 2019 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#ifndef TMCMDLNOPTS_H__ +#define TMCMDLNOPTS_H__ + +#include + +/* + * here are some typedef's for global data + */ +typedef struct{ + char *pidfile; // pid file name + int gettemp; // get MCU temperature + int getstatus; // get status of all devices +} glob_pars; + +// default & global parameters +extern glob_pars const Gdefault; +extern int quiet; + +glob_pars *parse_args(int argc, char **argv); +void MSG(const char *s1, const char *s2); + +#endif // TMCMDLNOPTS_H__ diff --git a/MMPP_lib/examples/wheels.c b/MMPP_lib/examples/wheels.c new file mode 100644 index 0000000..2b55a2a --- /dev/null +++ b/MMPP_lib/examples/wheels.c @@ -0,0 +1,143 @@ +/* + * This file is part of the libmmpp project. + * Copyright 2019 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "wheelscmdlnopts.h" +#include +#include +#include +#include + +// All return states of main(): +enum{ + RET_ALLOK = 0, + RET_NOTFOUND, // none of turrets found or didn't found seeking MCU + RET_ONLYONE, // only one turret found + RET_COMMERR, // communication error + RET_CANTINIT, // can't init turrets + RET_ERROR = 9, // uncoverable error - from libsnippets + RET_HELPCALL = 255 // user call help (or give wrong parameter[s]) - from libsnippets +}; +static glob_pars *G; +static wheel_descr *wheels; +static int found; + +/** + * We REDEFINE the default WEAK function of signal processing + */ +void __attribute__((noreturn)) signals(int sig){ + if(sig){ + signal(sig, SIG_IGN); + DBG("Get signal %d, quit.\n", sig); + } + if(G->pidfile) // remove unnesessary PID file + unlink(G->pidfile); + restore_console(); + del_wheels(wheels, found); + exit(sig); +} + +/** + * @brief find_wheel_by_ID - find wheel by ID + * @param w - array of wheel descriptors + * @param N - length of w + * @param ID - sought wheel ID + * @return NULL if no such wheel or pointer to it + */ +wheel_descr *find_wheel_by_ID(wheel_descr *w, int N, char ID){ + for(int i = 0; i < N; ++i){ + if(w[i].ID == ID) return &w[i]; + } + return NULL; +} + +int main(int argc, char **argv){ + initial_setup(); + signal(SIGTERM, signals); // kill (-15) + signal(SIGINT, signals); // ctrl+C + signal(SIGQUIT, SIG_IGN); // ctrl+\ . + signal(SIGTSTP, SIG_IGN); // ctrl+Z + setbuf(stdout, NULL); + G = parse_args(argc, argv); + check4running(NULL, G->pidfile); + wheel_error werr; + found = find_wheels(&wheels, &werr); + if(found == 0) ERRX(_("No wheels found")); + else green("Found %d wheels\n", found); + if(werr != WHERR_ALLOK){ + WARNX(_("Got wheel error: %d"), werr); + signals(1); + } + if(G->getstatus){ + for(int i = 0; i < found; ++i){ + green("Wheel #%d:\n", i); + printf("\tserial: %s\n", wheels[i].serial); + printf("\tID: %c\n", wheels[i].ID); + printf("\tname: %s\n", wheels[i].name); + printf("\tmaxpos: %d\n\n", wheels[i].maxpos); + } + } + if(G->gohome){ + for(int i = 0; i < found; ++i){ + if(!wheel_home(&wheels[i])) WARNX(_("Can't move wheel %c to home position"), wheels[i].ID); + else{ + green("Wheel %c is moving to home position\n"); + } + } + for(int i = 0; i < found; ++i){ + while(WHEEL_MOVING == wheel_getpos(&wheels[i])){ + usleep(100000); + } + if(wheel_getpos(&wheels[i]) != 1) + WARNX(_("Wheel %c didn't reach home position"), wheels[i].ID); + } + } + int Nw = 0, Ng = 0; + if(G->wh_ids){ + while(G->wh_ids[Nw]) ++Nw; + } + if(G->gotopos){ + while(G->gotopos[Ng]) ++Ng; + } + if(Nw != Ng){ + WARNX(_("Amoung of `--wheel-id` should be equal to amount of `--goto`!")); + }else{ + for(int i = 0; i < Nw; ++i){ + char ID = *G->wh_ids[i]; + DBG("id: %c, goto: %d", ID, *G->gotopos[i]); + wheel_descr *w = find_wheel_by_ID(wheels, found, ID); + if(!w) WARNX(_("No wheel with ID %c found!"), ID); + else{ + int pos = *G->gotopos[i]; + if(!move_wheel(w, pos)){ + WARNX(_("Can't rotate wheel %c to position %d"), ID, pos); + wheel_clear_err(w); + }else{ + while(WHEEL_MOVING == wheel_getpos(w)){ + DBG("still moving"); + usleep(100000); + } + int curpos = wheel_getpos(w); + if(curpos != pos) WARNX(_("Wheel %c can't reach position %d, current position: %d"), ID, pos, curpos); + else green("Wheel %c is on position %d\n", ID, pos); + } + } + }; + } + ; + signals(0); +} diff --git a/MMPP_lib/examples/wheelscmdlnopts.c b/MMPP_lib/examples/wheelscmdlnopts.c new file mode 100644 index 0000000..88e10d9 --- /dev/null +++ b/MMPP_lib/examples/wheelscmdlnopts.c @@ -0,0 +1,87 @@ +/* + * This file is part of the libmmpp project. + * Copyright 2019 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "wheelscmdlnopts.h" +#include +#include +#include +#include +#include + +/* + * here are global parameters initialisation + */ +static int help; +static glob_pars G; + +int quiet = 0; // less messages @ stdout + +// DEFAULTS +// default global parameters +glob_pars const Gdefault = { + .pidfile = "/tmp/MMPP_wheels.pid" +}; + +/* + * Define command line options by filling structure: + * name has_arg flag val type argptr help +*/ +static myoption cmdlnopts[] = { + {"help", NO_ARGS, NULL, 'h', arg_none, APTR(&help), N_("show this help")}, + {"quiet", NO_ARGS, NULL, 'q', arg_none, APTR(&quiet), N_("don't show anything @screen from stdout")}, + {"pidfile", NEED_ARG, NULL, 'p', arg_string, APTR(&G.pidfile), N_("PID-file name")}, + {"status", NO_ARGS, NULL, 's', arg_none, APTR(&G.getstatus), N_("get device status")}, + {"goto", MULT_PAR, NULL, 'g', arg_int, APTR(&G.gotopos), N_("go to given position")}, + {"wheel-id",MULT_PAR, NULL, 'I', arg_string, APTR(&G.wh_ids), N_("name wheel by ID")}, + {"home", NO_ARGS, NULL, 'H', arg_none, APTR(&G.gohome), N_("rotate all wheels to home position")}, + end_option +}; + + +/** + * Parse command line options and return dynamically allocated structure + * to global parameters + * @param argc - copy of argc from main + * @param argv - copy of argv from main + * @return allocated structure with global parameters + */ +glob_pars *parse_args(int argc, char **argv){ + void *ptr; + ptr = memcpy(&G, &Gdefault, sizeof(G)); assert(ptr); + parseargs(&argc, &argv, cmdlnopts); + if(help) showhelp(-1, cmdlnopts); + if(argc > 0){ + WARNX("%d unused parameters:\n", argc); + for(int i = 0; i < argc; ++i) + printf("\t%4d: %s\n", i+1, argv[i]); + } + return &G; +} + +/** + * @brief MSG show coloured message if `quiet` not set + * !! This function adds trailing '\n' to message + * @param s1 - green part of message (may be null) + * @param s2 - normal colored part of messate (may be null) + */ +void MSG(const char *s1, const char *s2){ + if(quiet) return; + if(s1){ + green("%s%s", s1, s2 ? ": " : "\n"); + } + if(s2) printf("%s\n", s2); +} diff --git a/MMPP_lib/examples/wheelscmdlnopts.h b/MMPP_lib/examples/wheelscmdlnopts.h new file mode 100644 index 0000000..9d0fa7e --- /dev/null +++ b/MMPP_lib/examples/wheelscmdlnopts.h @@ -0,0 +1,43 @@ +/* + * This file is part of the libmmpp project. + * Copyright 2019 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#ifndef TMCMDLNOPTS_H__ +#define TMCMDLNOPTS_H__ + +#include + +/* + * here are some typedef's for global data + */ +typedef struct{ + char *pidfile; // pid file name + int gettemp; // get MCU temperature + int getstatus; // get status of all devices + char **wh_ids; // array of wheel ids + int **gotopos; // rotate wheels to given position + int gohome; // turn all wheels to home state +} glob_pars; + +// default & global parameters +extern glob_pars const Gdefault; +extern int quiet; + +glob_pars *parse_args(int argc, char **argv); +void MSG(const char *s1, const char *s2); + +#endif // TMCMDLNOPTS_H__ diff --git a/MMPP_lib/hidmanage.c b/MMPP_lib/hidmanage.c new file mode 100644 index 0000000..87abfbf --- /dev/null +++ b/MMPP_lib/hidmanage.c @@ -0,0 +1,357 @@ +/* + * hidmanage.c - manage HID devices + * + * Copyright 2016 Edward V. Emelianoff + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include "common.h" +#include "hsfw.h" +#include "libmmpp.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool check_and_clear_err(int fd); + +/** + * @brief writereg - write buffer to register & check answer + * @param fd - wheel fd + * @param buf - data to write + * @param l - lenght of `buf` + * @return 0 if all OK + */ +static int writereg(int fd, uint8_t *buf, int l){ + uint8_t reg = buf[0]; + if(ioctl(fd, HIDIOCSFEATURE(l), buf) < 0 || buf[0] != reg){ + DBG("can't write register %d", reg); + return 1; + } + return 0; +} + +/** + * @brief readreg - read register to buf & check answer + * @param fd - wheel fd + * @param buf - buffer for reading + * @param reg - register to read + * @param l - data length + * @return 0 if all OK + */ +static int readreg(int fd, uint8_t *buf, int reg, int l){ + memset(buf, 0, l); + buf[0] = reg; + if(ioctl(fd, HIDIOCGFEATURE(l), buf) < 0 || buf[0] != reg){ + DBG("can't read register %d", reg); + return 1; + } + return 0; +} + +/** + * poll status register until moving stops + * @param fd - turret file descriptor + * @param msg - ==1 to show message + * @return current position or error code + */ +static wheel_status poll_wheelstatus(int fd){ + uint8_t buf[REG_STATUS_LEN]; + int i, stat = 1; + for(i = 0; i < HANDSHAKE_TRIES && stat; ++i){ + stat = readreg(fd, buf, REG_STATUS, REG_STATUS_LEN); + if(!stat){ + if(buf[2] == 0xff || buf[3] == 0xff) stat = 1; + } + if(buf[5]){ + return WHEEL_ERROR; + } + } + if(i == HANDSHAKE_TRIES) return WHEEL_MOVING; + return buf[4]; +} + +// library function for position polling +wheel_status wheel_getpos(wheel_descr *w){ + return poll_wheelstatus(w->fd); +} + +/** + * @brief go_home - blocking go to home position + * @param fd - wheel file descriptor + * @return true if all OK + */ +static bool go_home(int fd){ + poll_wheelstatus(fd); // wait for last moving + uint8_t buf[REG_HOME_LEN]; + int i, stat = 1; + for(i = 0; i < HANDSHAKE_TRIES && stat; ++i){ + memset(buf, 0, REG_HOME_LEN); + buf[0] = REG_HOME; + stat = writereg(fd, buf, REG_HOME_LEN); + if(stat){usleep(100000); continue;} + if((stat = readreg(fd, buf, REG_HOME, REG_HOME_LEN))) continue; + if(buf[1] != 0xff){ + stat = 1; continue; + }else{ + readreg(fd, buf, REG_HOME, REG_HOME_LEN); + break; + } + } + if(i == HANDSHAKE_TRIES) return false; + // now poll REG_STATUS + while(WHEEL_MOVING == poll_wheelstatus(fd)){DBG("still moving");}; + check_and_clear_err(fd); + return true; +} + +// the same as above, but non-blocking and library function +bool wheel_home(wheel_descr *w){ + int fd = w->fd; + poll_wheelstatus(fd); // wait for last moving + uint8_t buf[REG_HOME_LEN]; + int i, stat = 1; + for(i = 0; i < HANDSHAKE_TRIES && stat; ++i){ + memset(buf, 0, REG_HOME_LEN); + buf[0] = REG_HOME; + stat = writereg(fd, buf, REG_HOME_LEN); + if(stat){usleep(100000); continue;} + if((stat = readreg(fd, buf, REG_HOME, REG_HOME_LEN))) continue; + if(buf[1] != 0xff){ + stat = 1; continue; + }else{ + readreg(fd, buf, REG_HOME, REG_HOME_LEN); + break; + } + } + if(i == HANDSHAKE_TRIES) return false; + return true; +} + +/** + * @brief check_and_clear_err - check error state and clear it if need + * @param fd - opened device file descriptor + * @return true if all OK + */ +static bool check_and_clear_err(int fd){ + int i, stat = 1; + uint8_t buf[REG_STATUS_LEN]; + for(i = 0; i < HANDSHAKE_TRIES && stat; ++i){ + stat = readreg(fd, buf, REG_STATUS, REG_STATUS_LEN); + if(stat) usleep(100000); + } + if(i == HANDSHAKE_TRIES) return false; + if(buf[1] != 0xff){ + if(buf[5]){ + stat = 1; + for(i = 0; i < HANDSHAKE_TRIES && stat; ++i){ + memset(buf, 0, sizeof(buf)); + buf[0] = REG_CLERR; + stat = writereg(fd, buf, REG_CLERR_LEN); + usleep(100000); + if(!stat) stat = readreg(fd, buf, REG_STATUS, REG_STATUS_LEN); + if(!stat && buf[5]) stat = 1; + } + if(i == HANDSHAKE_TRIES) return false; + } + readreg(fd, buf, REG_STATUS, REG_STATUS_LEN); + if(buf[1] != 0xff){ + go_home(fd); + readreg(fd, buf, REG_STATUS, REG_STATUS_LEN); + } + } + return true; +} + +// the same - but library function +bool wheel_clear_err(wheel_descr *w){ + return check_and_clear_err(w->fd); +} + +static void get_props(wheel_descr *wheel){ + uint8_t buf[REG_NAME_LEN+1]; + int fd = wheel->fd; + if(fd < 0){ + return; + } + check_and_clear_err(fd); + // get status of wheel + if(readreg(fd, buf, REG_INFO, REG_INFO_LEN)) return; + wheel->ID = buf[5]; + wheel->maxpos = buf[4]; + DBG("Wheel with id '%c' and maxpos %d", wheel->ID, wheel->maxpos); + char *getwname(int id){ + memset(buf, 0, sizeof(buf)); + buf[0] = REG_NAME; + buf[1] = WHEEL_NAME; + buf[2] = id; + if(writereg(fd, buf, REG_NAME_LEN)) return NULL; + if(readreg(fd, buf, REG_NAME, REG_NAME_LEN)) return NULL; + if(buf[6]){ + char *x = strchr((char*)&buf[6], ' '); + if(x) *x = 0; + return (char*)&buf[6]; + } + else return NULL; + } + char *nm = getwname(wheel->ID); + if(nm){ + strncpy(wheel->name, nm, 9); + DBG("Wheel name: %s", wheel->name); + } +} + + +/** + * Find turrets present + * @param wheels (o) - if not NULL - list of wheels found (like "ABC") (allocated here!) + * @param err (o) - status code + * @return amount of devices found + * + * WARNING! If there's more than one turret with wheels having same name + * access by wheel ID could lead undefined behaviour! + */ +int find_wheels(wheel_descr **wheels, wheel_error *err){ +#define RETERR(x) do{if(err){*err = x;} return 0;}while(0) + struct udev *udev; + struct udev_enumerate *enumerate; + struct udev_list_entry *devices, *dev_list_entry; + wheel_descr *Found = NULL; + if(err) *err = WHERR_ALLOK; + // Create the udev object + udev = udev_new(); + int N = 0; + if(!udev){ + RETERR(WHERR_UDEV); + } + // Create a list of the devices in the 'hidraw' subsystem. + enumerate = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(enumerate, "hidraw"); + udev_enumerate_scan_devices(enumerate); + devices = udev_enumerate_get_list_entry(enumerate); + // Check out each device found + udev_list_entry_foreach(dev_list_entry, devices){ + const char *path; + struct udev_device *dev; + path = udev_list_entry_get_name(dev_list_entry); + dev = udev_device_new_from_syspath(udev, path); + const char *devpath = udev_device_get_devnode(dev); + DBG("Device Node Path: %s", devpath); + dev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"); + if(!dev){ + fprintf(stderr, "Unable to find parent usb device for %s\n", devpath); + udev_device_unref(dev); + continue; + } + const char *vid, *pid; + vid = udev_device_get_sysattr_value(dev,"idVendor"); + pid = udev_device_get_sysattr_value(dev, "idProduct"); + DBG(" VID/PID: %s/%s", vid, pid); + if(strcmp(vid, WHEEL_VID) == 0 && strcmp(pid, WHEEL_PID) == 0){ + ++N; + if(!Found){ + Found = MALLOC(wheel_descr, 1); + }else{ + Found = realloc(Found, sizeof(wheel_descr)*N); + if(!Found){ + perror("realloc()"); + exit(-1); + } + } + wheel_descr *curdev = &Found[N-1]; + int fd = open(devpath, O_RDWR|O_NONBLOCK); + if(fd < 0){ + if(err) *err = WHERR_CANTOPEN; + curdev->fd = -1; + }else + curdev->fd = fd; + DBG("%s %s", + udev_device_get_sysattr_value(dev,"manufacturer"), + udev_device_get_sysattr_value(dev,"product")); + curdev->serial = strdup(udev_device_get_sysattr_value(dev, "serial")); + DBG("serial: %s\n", curdev->serial); + get_props(curdev); + } + udev_device_unref(dev); + } + // Free the enumerator object + udev_enumerate_unref(enumerate); + if(wheels){ + *wheels = Found; + }else + free(Found); + return N; +#undef RETERR +} + +/** + * @brief del_wheels - free memory of wheels descriptors + * @param w - array of descriptors + * @param N - length of w + */ +void del_wheels(wheel_descr *w, int N){ + if(N == 0) return; + for(int i = 0; i < N; ++i){ + FREE(w[i].serial); + close(w[i].fd); + } + FREE(w); +} + + +/** + * @brief move_wheel - unblocking move given wheel + * @param w - wheel + * @param filter_pos - target position + * @return true if all OK + */ +bool move_wheel(wheel_descr *w, int filter_pos){ + int wheel_fd = w->fd; + if(wheel_fd < 0) return false; + if(filter_pos == poll_wheelstatus(wheel_fd)){ + DBG("Wheel %c is ON position (%d)", w->ID, filter_pos); + return true; // @ target position + } + uint8_t buf[REG_GOTO_LEN]; + int i, stat = 1; + for(i = 0; i < HANDSHAKE_TRIES && stat; ++i){ + DBG("i=%d",i); + memset(buf, 0, REG_GOTO_LEN); + buf[0] = REG_GOTO; + buf[1] = filter_pos; + stat = writereg(wheel_fd, buf, REG_GOTO_LEN); + usleep(100000); + if(stat) continue; + if((stat = readreg(wheel_fd, buf, REG_GOTO, REG_GOTO_LEN))) continue; + if(buf[1] != 0xff){ + stat = 1; continue; + }else{ + readreg(wheel_fd, buf, REG_GOTO, REG_HOME_LEN); + break; + } + } + if(i == HANDSHAKE_TRIES) return false; + //poll_wheelstatus(wheel_fd); + //check_and_clear_err(wheel_fd); + return true; +} diff --git a/MMPP_lib/hsfw.h b/MMPP_lib/hsfw.h new file mode 100644 index 0000000..c5ef32f --- /dev/null +++ b/MMPP_lib/hsfw.h @@ -0,0 +1,55 @@ +/* + * hsfw.h - functions for work with wheels + * + * Copyright 2016 Edward V. Emelianov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +#pragma once +#ifndef __HSFW_H__ +#define __HSFW_H__ + +#define REG_CLERR (0x02) +#define REG_CLERR_LEN (2) +#define REG_STATUS (0x0a) +#define REG_STATUS_LEN (6) +#define REG_INFO (0x0b) +#define REG_INFO_LEN (7) +#define REG_GOTO (0x14) +#define REG_GOTO_LEN (3) +#define REG_HOME (0x15) +#define REG_HOME_LEN (3) +#define REG_NAME (0x16) +#define REG_NAME_LEN (14) + +// absolute max position (5 for wheels 'A'..'E' & 8 for wheels 'F'..'G') +#define ABS_MAX_POS_A (5) +#define ABS_MAX_POS_B (8) +// end of 5-position wheel descriptor range +#define POS_A_END ('E') +// end of 8-pos range +#define POS_B_END ('H') + +enum name_cmd{ + RESTORE_DEFVALS = 1, + RENAME_FILTER, + FILTER_NAME, + RENAME_WHEEL, + WHEEL_NAME +}; + +int get_max_pos(char filter_id); +#endif // __HSFW_H__ diff --git a/MMPP_lib/libmmpp.h b/MMPP_lib/libmmpp.h index 88852ac..abd062f 100644 --- a/MMPP_lib/libmmpp.h +++ b/MMPP_lib/libmmpp.h @@ -89,12 +89,48 @@ int get_temp(double *t1, double *t2); bool mot_getstatus(int Nmcu, motor_state *s); int init_motors(); int mot_wait(); +ttysend_status tty_sendcmd(char *cmd); char *tty_get(); int tty_send(char *cmd); -ttysend_status tty_sendcmd(char *cmd); char* tty_sendraw(char *string); +/************************************************************************************ + * Wheels management * + ************************************************************************************/ +#define WHEEL_VID "10c4" +#define WHEEL_PID "82cd" +// wheel descriptor +typedef struct{ + int fd; // file descriptor of device + char *serial; // serial number + char ID; // identificator + char name[9]; // wheel name + int maxpos; // max position +} wheel_descr; + +typedef enum{ + WHERR_ALLOK // no errors + ,WHERR_UDEV // udev error + ,WHERR_CANTOPEN // can't open file device +} wheel_error; + +int find_wheels(wheel_descr **wheels, wheel_error *err); +void del_wheels(wheel_descr *w, int N); + +// return value of polling wheel status (if > -1 - current position) +typedef enum{ + WHEEL_MOVING = -2 // still moving + ,WHEEL_ERROR = -1 // communication error + ,WHEEL_POSERR = 0 // wrong wheel position + ,WHEEL_POS1, WHEEL_POS2, WHEEL_POS3, WHEEL_POS4, WHEEL_POS5, WHEEL_POS6 + ,WHEEL_POS7, WHEEL_POS8, WHEEL_POS9, WHEEL_POS10 +} wheel_status; + +wheel_status wheel_getpos(wheel_descr *w); +bool wheel_clear_err(wheel_descr *w); +bool move_wheel(wheel_descr *w, int filter_pos); +bool wheel_home(wheel_descr *w); #endif // LIBMMPP_H__ diff --git a/MMPP_lib/tty_procs.h b/MMPP_lib/tty_procs.h index 989ccc7..16a11d6 100644 --- a/MMPP_lib/tty_procs.h +++ b/MMPP_lib/tty_procs.h @@ -20,6 +20,7 @@ #ifndef TTY_PROCS_H__ #define TTY_PROCS_H__ +#include "common.h" #include #include #include @@ -30,45 +31,6 @@ // read timeout (in seconds) #define TTYTIMEOUT (0.05) -// unused arguments of functions -#define _U_ __attribute__((__unused__)) -// break absent in `case` -#define FALLTHRU __attribute__ ((fallthrough)) -// and synonym for FALLTHRU -#define NOBREAKHERE __attribute__ ((fallthrough)) -// weak functions -#define WEAK __attribute__ ((weak)) - -#define COLOR_RED "\033[1;31;40m" -#define COLOR_GREEN "\033[1;32;40m" -#define COLOR_OLD "\033[0;0;0m" -/* - * print function name, debug messages - * debug mode, -DEBUG - */ -#ifdef EBUG - #define FNAME() do{ fprintf(stderr, COLOR_OLD); \ - fprintf(stderr, "\n%s (%s, line %d)\n", __func__, __FILE__, __LINE__);} while(0) - #define DBG(...) do{ fprintf(stderr, COLOR_OLD); \ - fprintf(stderr, "%s (%s, line %d): ", __func__, __FILE__, __LINE__); \ - fprintf(stderr, __VA_ARGS__); \ - fprintf(stderr, "\n");} while(0) -#else - #define FNAME() do{}while(0) - #define DBG(...) do{}while(0) -#endif //EBUG - -/* - * Memory allocation - */ -#define ALLOC(type, var, size) type * var = ((type *)my_alloc(size, sizeof(type))) -#define MALLOC(type, size) ((type *)my_alloc(size, sizeof(type))) -#define FREE(ptr) do{if(ptr){free(ptr); ptr = NULL;}}while(0) - -#ifndef DBL_EPSILON -#define DBL_EPSILON (2.2204460492503131e-16) -#endif - typedef struct { char *portname; // device filename (should be freed before structure freeing) int speed; // baudrate in human-readable format @@ -82,7 +44,6 @@ typedef struct { bool exclusive; // should device be exclusive opened } TTYdescr; -void *my_alloc(size_t N, size_t S); int str2double(double *num, const char *str); double dtime(); TTYdescr *new_tty(char *comdev, int speed, size_t bufsz);