diff --git a/kicad/tsys01.pro b/kicad/tsys01.pro index f4ce1db..c63d34d 100644 --- a/kicad/tsys01.pro +++ b/kicad/tsys01.pro @@ -1,4 +1,4 @@ -update=Чт 30 янв 2020 17:14:40 +update=Сб 31 июл 2021 22:20:24 version=1 last_client=kicad [pcbnew] diff --git a/src/netdaemon/Commands_list b/src/netdaemon/Commands_list new file mode 100644 index 0000000..e13169b --- /dev/null +++ b/src/netdaemon/Commands_list @@ -0,0 +1,26 @@ +### Serial interface commands (ends with '\n'), small letter for only +local processing: +- **0...7** send message to Nth controller, not broadcast (after numb +er should be CAN command) +- **a** get raw ADC values +- **B** send dummy CAN messages to broadcast address +- **c** show coefficients for all thermosensors +- **D** send dummy CAN messages to master (0) address +- **Ee** end temperature scan +- **Ff** turn sensors off +- **g** get last CAN address +- **Hh** switch I2C to high speed (100kHz) +- **i** reinit CAN with new address (if changed) +- **Jj** get MCU temperature +- **Kk** get values of U and I +- **Ll** switch I2C to low speed (default, 10kHz) +- **Mm** change master id to 0 (**m**) / broadcast (**M**) +- **Oo** turn onboard diagnostic LEDs **O**n or **o**ff (both commands + are local!) +- **P** ping everyone over CAN +- **Rr** reinit I2C +- **Ss** start temperature scan +- **Tt** start single temperature measurement +- **u** check CAN bus status for errors +- **Vv** very low speed +- **Z** get sensors state over CAN diff --git a/src/netdaemon/Makefile b/src/netdaemon/Makefile index ee763e6..76744c4 100644 --- a/src/netdaemon/Makefile +++ b/src/netdaemon/Makefile @@ -1,13 +1,14 @@ # run `make DEF=...` to add extra defines PROGRAM := netdaemon LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all -pthread +LDFLAGS += -lusefull_macros -lm SRCS := $(wildcard *.c) DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111 #DEFINES += -DEBUG # baudrate for USB<->UART converter DEFINES += -DBAUD_RATE=B115200 OBJDIR := mk -CFLAGS += -O2 -Wall -Werror -Wextra -Wno-trampolines +CFLAGS += -O3 -Wall -Werror -Wextra -Wno-trampolines OBJS := $(addprefix $(OBJDIR)/, $(SRCS:%.c=%.o)) DEPS := $(OBJS:.o=.d) CC = gcc @@ -16,7 +17,7 @@ all : $(OBJDIR) $(PROGRAM) $(PROGRAM) : $(OBJS) @echo -e "\t\tLD $(PROGRAM)" - $(CC) $(LDFLAGS) $(OBJS) -o $(PROGRAM) + $(CC) $(OBJS) $(LDFLAGS) -o $(PROGRAM) $(OBJDIR): mkdir $(OBJDIR) @@ -27,7 +28,7 @@ endif $(OBJDIR)/%.o: %.c @echo -e "\t\tCC $<" - $(CC) -MD -c $(LDFLAGS) $(CFLAGS) $(DEFINES) -o $@ $< + $(CC) $< -MD -c $(CFLAGS) $(DEFINES) -o $@ clean: @echo -e "\t\tCLEAN" diff --git a/src/netdaemon/Readme.md b/src/netdaemon/Readme.md index d06f18f..ed9dd88 100644 --- a/src/netdaemon/Readme.md +++ b/src/netdaemon/Readme.md @@ -20,3 +20,9 @@ images storing directory, copy there script 'plot' and run service as Every 15 minutes it will calculate average values of thermal data and plot three graphs: T0.html with top temperatures, T1.html with bottom and Tgrad.html with their differences (T0-T1). + +** Signals + +SIGUSR1 - reread temperatures adjustment file + +SIGUSR2 - dump to logfile all current temperature values and turn sensors off until next reading diff --git a/src/netdaemon/checkfile.c b/src/netdaemon/checkfile.c deleted file mode 100644 index 8b0b2ac..0000000 --- a/src/netdaemon/checkfile.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * daemon.c - functions for running in background like a daemon - * - * Copyright 2013 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 // opendir -#include "checkfile.h" -#include "usefull_macros.h" - -/** - * @brief readPSname - read process name from /proc/PID/cmdline - * @param pid - PID of interesting process - * @return filename or NULL if not found - * don't use this function twice for different names without copying - * its returning by strdup, because `name` contains in static array - */ -char *readPSname(pid_t pid){ - static char name[PATH_MAX]; - char *pp = name, byte, path[PATH_MAX]; - FILE *file; - int cntr = 0; - size_t sz; - snprintf(path, PATH_MAX, PROC_BASE "/%d/cmdline", pid); - file = fopen(path, "r"); - if(!file) return NULL; // there's no such file - do{ // read basename - sz = fread(&byte, 1, 1, file); - if(sz != 1) break; - if(byte != '/') *pp++ = byte; - else{ - pp = name; - cntr = 0; - } - }while(byte && cntr++ < PATH_MAX-1); - name[cntr] = 0; - fclose(file); - return name; -} - -static char *pidfilename_ = NULL; // store the name of pidfile here - -/** - * check wether there is a same running process - * exit if there is a running process or error - * Checking have 3 steps: - * 1) lock executable file - * 2) check pidfile (if you run a copy?) - * 3) check /proc for executables with the same name (no/wrong pidfile) - * @param pidfilename - name of pidfile or NULL if none - * @return 0 if all OK or PID of first running process found - */ -pid_t check4running(char *pidfilename){ - DIR *dir; - FILE *pidfile; - struct dirent *de; - struct stat s_buf; - pid_t pid = 0, self; - char *name, *myname; - self = getpid(); // get self PID - if(!(dir = opendir(PROC_BASE))){ // open /proc directory - ERR(PROC_BASE); - } - if(!(name = readPSname(self))){ // error reading self name - ERR("Can't read self name"); - } - myname = strdup(name); - if(pidfilename && stat(pidfilename, &s_buf) == 0){ // pidfile exists - pidfile = fopen(pidfilename, "r"); - if(pidfile){ - if(fscanf(pidfile, "%d", &pid) > 0){ // read PID of (possibly) running process - if((name = readPSname(pid)) && strncmp(name, myname, 255) == 0){ - fclose(pidfile); - goto procfound; - } - } - fclose(pidfile); - } - } - // There is no pidfile or it consists a wrong record - while((de = readdir(dir))){ // scan /proc - if(!(pid = (pid_t)atoi(de->d_name)) || pid == self) // pass non-PID files and self - continue; - if((name = readPSname(pid)) && strncmp(name, myname, 255) == 0) - goto procfound; - } - pid = 0; // OK, not found -> create pid-file if need - if(pidfilename){ - pidfile = fopen(pidfilename, "w"); - if(!pidfile){ - WARN("Can't open PID file"); - }else{ - fprintf(pidfile, "%d\n", self); // write self PID to pidfile - fclose(pidfile); - if(pidfilename_) FREE(pidfilename_); - pidfilename_ = strdup(pidfilename); - } - } -procfound: - closedir(dir); - free(myname); - return pid; -} - -/** - * @brief unlink_pidfile - remove pidfile @ exit - */ -void unlink_pidfile(){ - if(!pidfilename_) return; - unlink(pidfilename_); - FREE(pidfilename_); -} diff --git a/src/netdaemon/checkfile.h b/src/netdaemon/checkfile.h deleted file mode 100644 index acd7ede..0000000 --- a/src/netdaemon/checkfile.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This file is part of the Zphocus 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 CHECKFILE_H__ -#define CHECKFILE_H__ - -#include // getpid, unlink - -#ifndef PROC_BASE -#define PROC_BASE "/proc" -#endif -#ifndef WEAK -#define WEAK __attribute__ ((weak)) -#endif - -// check that our process is exclusive -pid_t check4running(char *pidfilename); -// read name of process by its PID -char *readPSname(pid_t pid); -void unlink_pidfile(); - - -#endif // CHECKFILE_H__ diff --git a/src/netdaemon/cmdlnopts.c b/src/netdaemon/cmdlnopts.c index 729920a..92669cf 100644 --- a/src/netdaemon/cmdlnopts.c +++ b/src/netdaemon/cmdlnopts.c @@ -22,9 +22,10 @@ #include #include #include -#include +#include + #include "cmdlnopts.h" -#include "usefull_macros.h" +#include "term.h" /* * here are global parameters initialisation @@ -33,20 +34,15 @@ int help; static glob_pars G; // default values for Gdefault & help -#define DEFAULT_COMDEV "/dev/ttyUSB0" -#define DEFAULT_PORT "4444" -#define DEFAULT_PIDFILE "/tmp/TSYS01daemon.pid" +#define DEFAULT_SOCKPATH "\0canbus" +#define DEFAULT_PORT "4444" +#define DEFAULT_PIDFILE "/tmp/TSYS01daemon.pid" // DEFAULTS // default global parameters glob_pars const Gdefault = { - .device = DEFAULT_COMDEV, + .sockname = DEFAULT_SOCKPATH, .port = DEFAULT_PORT, - .terminal = 0, - .savepath = NULL, - .makegraphs = 0, - .rest_pars = NULL, - .rest_pars_num = 0, .adjfilename = "tempadj.txt", .pidfilename = DEFAULT_PIDFILE }; @@ -55,18 +51,18 @@ glob_pars const Gdefault = { * Define command line options by filling structure: * name has_arg flag val type argptr help */ -myoption cmdlnopts[] = { +static myoption cmdlnopts[] = { // common options {"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")}, - {"device", NEED_ARG, NULL, 'i', arg_string, APTR(&G.device), _("serial device name (default: " DEFAULT_COMDEV ")")}, + {"name", NEED_ARG, NULL, 'n', arg_string, APTR(&G.sockname), _("server's UNIX socket name (default: \\0canbus)")}, {"port", NEED_ARG, NULL, 'p', arg_string, APTR(&G.port), _("network port to connect (default: " DEFAULT_PORT ")")}, - {"terminal",NO_ARGS, NULL, 't', arg_int, APTR(&G.terminal), _("run as terminal")}, + //{"terminal",NO_ARGS, NULL, 't', arg_int, APTR(&G.terminal), _("run as terminal")}, {"savepath",NEED_ARG, NULL, 's', arg_string, APTR(&G.savepath), _("path where files would be saved (if none - don't save)")}, {"graphplot",NO_ARGS, NULL, 'g', arg_int, APTR(&G.makegraphs),_("make graphics with gnuplot")}, - {"testadjfile",NO_ARGS, NULL, 'T', arg_int, APTR(&G.testadjfile),_("test format of file with T adjustements and force running proces to re-read it")}, + {"testadjfile",NO_ARGS, NULL, 'T', arg_int, APTR(&G.testadjfile),_("test format of file with T adjustements")}, {"adjname", NEED_ARG, NULL, 'N', arg_string, APTR(&G.adjfilename),_("name of adjustements file (default: tempadj.txt)")}, {"pidfile", NEED_ARG, NULL, 'P', arg_string, APTR(&G.pidfilename),_("name of PID file (default: " DEFAULT_PIDFILE ")")}, - {"dumpoff", NO_ARGS, NULL, 'd', arg_string, APTR(&G.dumpoff), _("dump sensors data & turn all OFF until next request")}, + //{"dumpoff", NO_ARGS, NULL, 'd', arg_string, APTR(&G.dumpoff), _("dump sensors data & turn all OFF until next request")}, end_option }; diff --git a/src/netdaemon/cmdlnopts.h b/src/netdaemon/cmdlnopts.h index cac1130..f1b295a 100644 --- a/src/netdaemon/cmdlnopts.h +++ b/src/netdaemon/cmdlnopts.h @@ -23,16 +23,13 @@ #ifndef __CMDLNOPTS_H__ #define __CMDLNOPTS_H__ -#include "parseargs.h" -#include "term.h" - /* * here are some typedef's for global data */ typedef struct{ - char *device; // serial device name + char *sockname; // server's UNIX socket name char *port; // port to connect - int terminal; // run as terminal + //int terminal; // run as terminal char *savepath; // path where data & graphical files would be saved int makegraphs; // ==1 to make graphics with gnuplot int rest_pars_num; // number of rest parameters @@ -40,7 +37,7 @@ typedef struct{ int testadjfile; // test format of file with adjustments char *adjfilename; // name of adjustements file char *pidfilename; // name of PID file - int dumpoff; // dump sensors data & turn all OFF until next request + //int dumpoff; // dump sensors data & turn all OFF until next request } glob_pars; diff --git a/src/netdaemon/gnuplot.c b/src/netdaemon/gnuplot.c index 346a020..4bc7a30 100644 --- a/src/netdaemon/gnuplot.c +++ b/src/netdaemon/gnuplot.c @@ -20,12 +20,15 @@ * MA 02110-1301, USA. * */ +#include // PATH_MAX #include // file operations +#include #include #include // access() to check file exists -#include // PATH_MAX -#include "usefull_macros.h" +#include + #include "cmdlnopts.h" // glob_pars +#include "sens_place.h" extern glob_pars *G; @@ -94,7 +97,7 @@ static void gnuplot(char *path, char *fname){ DBG("Run %s", buf); if(system(buf)){ WARNX(_("Can't run `%s`"), buf); - }else LOG("created chart %s", fname); + }else LOGMSG("created chart %s", fname); } void plot(double data[2][NCHANNEL_MAX+1][NCTRLR_MAX+1], char *savepath){ diff --git a/src/netdaemon/main.c b/src/netdaemon/main.c index 1900d85..c14369e 100644 --- a/src/netdaemon/main.c +++ b/src/netdaemon/main.c @@ -19,15 +19,17 @@ * MA 02110-1301, USA. */ #include +#include +#include #include //prctl #include #include // wait +#include -#include "checkfile.h" +//#include "checkfile.h" #include "cmdlnopts.h" #include "socket.h" #include "term.h" -#include "usefull_macros.h" glob_pars *G; // non-static: some another files should know about it! static pid_t childpid = 0; // PID of child - to kill it if need @@ -35,29 +37,25 @@ static pid_t childpid = 0; // PID of child - to kill it if need // default signals handler void signals(int signo){ restore_console(); - restore_tty(); if(childpid){ - const char *s = signum_to_signame(signo); - if(s) LOG("exit with status %d (or by signal %s)", signo, s); - else LOG("exit with status %d", signo); + LOGERR("Exit with status %d", signo); } exit(signo); } // report about some ignored signals static void repsig(int signo){ - const char *s = signum_to_signame(signo); - WARNX("PID: %d, received signal %d, %s (%s)", getpid(), signo, s ? s : "", strsignal(signo)); + WARNX("PID: %d, received signal %d (%s)", getpid(), signo, strsignal(signo)); } // SIGUSR1 handler - re-read Tadj file static void refreshAdj(_U_ int signo){ DBG("refresh adj"); if(childpid){ // I am a master - LOG("Force child %d to re-read adj-file", childpid); + LOGMSG("Force child %d to re-read adj-file", childpid); kill(childpid, SIGUSR1); }else{ // I am a child - LOG("Re-read adj-file"); + LOGMSG("Re-read adj-file"); read_adj_file(G->adjfilename); } } @@ -65,38 +63,38 @@ static void refreshAdj(_U_ int signo){ static void logT(_U_ int signo){ for(int i = 0; i < 3; ++i){ const char *s = gotstr(i); - if(s && *s) LOG("Sensors group #%d:\n%s", i, s); + if(s && *s) LOGMSG("Sensors group #%d:\n%s", i, s); } - LOG("Turn sensors off"); + LOGMSG("Turn sensors off"); TurnOFF(); } int main(int argc, char **argv){ initial_setup(); + char *self = strdup(argv[0]); G = parse_args(argc, argv); if(G->makegraphs && !G->savepath){ ERRX(_("Point the path to graphical files")); } - pid_t runningproc = check4running(G->pidfilename); - if(G->dumpoff){ + check4running(self, G->pidfilename); + /*if(G->dumpoff){ if(runningproc) kill(runningproc, SIGUSR2); else ERRX("There's no running daemon"); return 0; - } + }*/ int raf = read_adj_file(G->adjfilename); if(G->testadjfile){ if(raf == 0){ green("Format of file %s is right\n", G->adjfilename); - if(runningproc){ // fore running process to re-read it + /*if(runningproc){ // fore running process to re-read it kill(runningproc, SIGUSR1); - } + }*/ return 0; } return 1; } - if(runningproc) ERRX("Found running process, pid=%d.", runningproc); if(G->rest_pars_num) - Cl_createlog(G->rest_pars[0]); + OPENLOG(G->rest_pars[0], LOGLEVEL_DBG, 1); // ignore almost all possible signals for(int sig = 0; sig < 256; ++sig) signal(sig, repsig); signal(SIGTERM, signals); // kill (-15) - quit @@ -108,35 +106,34 @@ int main(int argc, char **argv){ signal(SIGUSR1, refreshAdj); // refresh adjustements signal(SIGUSR2, logT); // print all current temperatures into logfile and turn off sensors #ifndef EBUG - if(!G->terminal){ - if(daemon(1, 0)){ - ERR("daemon()"); - } - while(1){ // guard for dead processes - childpid = fork(); - if(childpid){ - LOG("create child with PID %d\n", childpid); - DBG("Created child with PID %d\n", childpid); - while(childpid != waitpid(childpid, NULL, 0)); - WARNX("Child %d died\n", childpid); - sleep(10); - }else{ - prctl(PR_SET_PDEATHSIG, SIGTERM); // send SIGTERM to child when parent dies - break; // go out to normal functional - } + if(daemon(1, 0)){ + ERR("daemon()"); + } + while(1){ // guard for dead processes + childpid = fork(); + if(childpid){ + LOGMSG("create child with PID %d\n", childpid); + DBG("Created child with PID %d\n", childpid); + while(childpid != waitpid(childpid, NULL, 0)); + WARNX("Child %d died\n", childpid); + sleep(10); + }else{ + prctl(PR_SET_PDEATHSIG, SIGTERM); // send SIGTERM to child when parent dies + break; // go out to normal functional } } #endif - DBG("dev: %s", G->device); - try_connect(G->device); + DBG("sockname: %s", G->sockname); + if(!try_connect(G->sockname)) ERR("Can't connect to UNIX socket"); if(check_sensors()){ - LOG("No CAN-controllers detected"); + LOGWARN("No CAN-controllers detected"); if(!poll_sensors(0)){ // there's not main controller connected to given terminal - LOG("Opened device is not main controller"); - if(!G->terminal) signals(15); + LOGERR("Opened device is not main controller"); + ERRX("Opened device is not main controller"); } } - if(G->terminal) run_terminal(); - else daemonize(G->port); + //if(G->terminal) run_terminal(); + //else + daemonize(G->port); return 0; } diff --git a/src/netdaemon/parseargs.c b/src/netdaemon/parseargs.c deleted file mode 100644 index b235752..0000000 --- a/src/netdaemon/parseargs.c +++ /dev/null @@ -1,497 +0,0 @@ -/* geany_encoding=koi8-r - * parseargs.c - parsing command line arguments & print help - * - * Copyright 2013 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 // printf -#include // getopt_long -#include // calloc, exit, strtoll -#include // assert -#include // strdup, strchr, strlen -#include // strcasecmp -#include // INT_MAX & so on -#include // gettext -#include // isalpha -#include "parseargs.h" -#include "usefull_macros.h" - -char *helpstring = "%s\n"; - -/** - * Change standard help header - * MAY consist ONE "%s" for progname - * @param str (i) - new format - */ -void change_helpstring(char *s){ - int pcount = 0, scount = 0; - char *str = s; - // check `helpstring` and set it to default in case of error - for(; pcount < 2; str += 2){ - if(!(str = strchr(str, '%'))) break; - if(str[1] != '%') pcount++; // increment '%' counter if it isn't "%%" - else{ - str += 2; // pass next '%' - continue; - } - if(str[1] == 's') scount++; // increment "%s" counter - }; - if(pcount > 1 || pcount != scount){ // amount of pcount and/or scount wrong - /// " " - ERRX(_("Wrong helpstring!")); - } - helpstring = s; -} - -/** - * Carefull atoll/atoi - * @param num (o) - returning value (or NULL if you wish only check number) - allocated by user - * @param str (i) - string with number must not be NULL - * @param t (i) - T_INT for integer or T_LLONG for long long (if argtype would be wided, may add more) - * @return TRUE if conversion sone without errors, FALSE otherwise - */ -static bool myatoll(void *num, char *str, argtype t){ - long long tmp, *llptr; - int *iptr; - char *endptr; - assert(str); - assert(num); - tmp = strtoll(str, &endptr, 0); - if(endptr == str || *str == '\0' || *endptr != '\0') - return FALSE; - switch(t){ - case arg_longlong: - llptr = (long long*) num; - *llptr = tmp; - break; - case arg_int: - default: - if(tmp < INT_MIN || tmp > INT_MAX){ - /// " " - WARNX(_("Integer out of range")); - return FALSE; - } - iptr = (int*)num; - *iptr = (int)tmp; - } - return TRUE; -} - -// the same as myatoll but for double -// There's no NAN & INF checking here (what if they would be needed?) -static bool myatod(void *num, const char *str, argtype t){ - double tmp, *dptr; - float *fptr; - char *endptr; - assert(str); - tmp = strtod(str, &endptr); - if(endptr == str || *str == '\0' || *endptr != '\0') - return FALSE; - switch(t){ - case arg_double: - dptr = (double *) num; - *dptr = tmp; - break; - case arg_float: - default: - fptr = (float *) num; - *fptr = (float)tmp; - break; - } - return TRUE; -} - -/** - * Get index of current option in array options - * @param opt (i) - returning val of getopt_long - * @param options (i) - array of options - * @return index in array - */ -static int get_optind(int opt, myoption *options){ - int oind; - myoption *opts = options; - assert(opts); - for(oind = 0; opts->name && opts->val != opt; oind++, opts++); - if(!opts->name || opts->val != opt) // no such parameter - showhelp(-1, options); - return oind; -} - -/** - * reallocate new value in array of multiple repeating arguments - * @arg paptr - address of pointer to array (**void) - * @arg type - its type (for realloc) - * @return pointer to new (next) value - */ -void *get_aptr(void *paptr, argtype type){ - int i = 1; - void **aptr = *((void***)paptr); - if(aptr){ // there's something in array - void **p = aptr; - while(*p++) ++i; - } - size_t sz = 0; - switch(type){ - default: - case arg_none: - /// " !" - ERRX("Can't use multiple args with arg_none!"); - break; - case arg_int: - sz = sizeof(int); - break; - case arg_longlong: - sz = sizeof(long long); - break; - case arg_double: - sz = sizeof(double); - break; - case arg_float: - sz = sizeof(float); - break; - case arg_string: - sz = 0; - break; - /* case arg_function: - sz = sizeof(argfn *); - break;*/ - } - aptr = realloc(aptr, (i + 1) * sizeof(void*)); - *((void***)paptr) = aptr; - aptr[i] = NULL; - if(sz){ - aptr[i - 1] = malloc(sz); - }else - aptr[i - 1] = &aptr[i - 1]; - return aptr[i - 1]; -} - - -/** - * Parse command line arguments - * ! If arg is string, then value will be strdup'ed! - * - * @param argc (io) - address of argc of main(), return value of argc stay after `getopt` - * @param argv (io) - address of argv of main(), return pointer to argv stay after `getopt` - * BE CAREFUL! if you wanna use full argc & argv, save their original values before - * calling this function - * @param options (i) - array of `myoption` for arguments parcing - * - * @exit: in case of error this function show help & make `exit(-1)` - */ -void parseargs(int *argc, char ***argv, myoption *options){ - char *short_options, *soptr; - struct option *long_options, *loptr; - size_t optsize, i; - myoption *opts = options; - // check whether there is at least one options - assert(opts); - assert(opts[0].name); - // first we count how much values are in opts - for(optsize = 0; opts->name; optsize++, opts++); - // now we can allocate memory - short_options = calloc(optsize * 3 + 1, 1); // multiply by three for '::' in case of args in opts - long_options = calloc(optsize + 1, sizeof(struct option)); - opts = options; loptr = long_options; soptr = short_options; - // in debug mode check the parameters are not repeated -#ifdef EBUG - char **longlist = MALLOC(char*, optsize); - char *shortlist = MALLOC(char, optsize); -#endif - // fill short/long parameters and make a simple checking - for(i = 0; i < optsize; i++, loptr++, opts++){ - // check - assert(opts->name); // check name -#ifdef EBUG - longlist[i] = strdup(opts->name); -#endif - if(opts->has_arg){ - assert(opts->type != arg_none); // check error with arg type - assert(opts->argptr); // check pointer - } - if(opts->type != arg_none) // if there is a flag without arg, check its pointer - assert(opts->argptr); - // fill long_options - // don't do memcmp: what if there would be different alignment? - loptr->name = opts->name; - loptr->has_arg = (opts->has_arg < MULT_PAR) ? opts->has_arg : 1; - loptr->flag = opts->flag; - loptr->val = opts->val; - // fill short options if they are: - if(!opts->flag && opts->val){ -#ifdef EBUG - shortlist[i] = (char) opts->val; -#endif - *soptr++ = opts->val; - if(loptr->has_arg) // add ':' if option has required argument - *soptr++ = ':'; - if(loptr->has_arg == 2) // add '::' if option has optional argument - *soptr++ = ':'; - } - } - // sort all lists & check for repeating -#ifdef EBUG - int cmpstringp(const void *p1, const void *p2){ - return strcmp(* (char * const *) p1, * (char * const *) p2); - } - int cmpcharp(const void *p1, const void *p2){ - return (int)(*(char * const)p1 - *(char *const)p2); - } - qsort(longlist, optsize, sizeof(char *), cmpstringp); - qsort(shortlist,optsize, sizeof(char), cmpcharp); - char *prevl = longlist[0], prevshrt = shortlist[0]; - for(i = 1; i < optsize; ++i){ - if(longlist[i]){ - if(prevl){ - if(strcmp(prevl, longlist[i]) == 0) ERRX("double long arguments: --%s", prevl); - } - prevl = longlist[i]; - } - if(shortlist[i]){ - if(prevshrt){ - if(prevshrt == shortlist[i]) ERRX("double short arguments: -%c", prevshrt); - } - prevshrt = shortlist[i]; - } - } -#endif - // now we have both long_options & short_options and can parse `getopt_long` - while(1){ - int opt; - int oindex = 0, optind = 0; // oindex - number of option in argv, optind - number in options[] - if((opt = getopt_long(*argc, *argv, short_options, long_options, &oindex)) == -1) break; - if(opt == '?'){ - opt = optopt; - optind = get_optind(opt, options); - if(options[optind].has_arg == NEED_ARG || options[optind].has_arg == MULT_PAR) - showhelp(optind, options); // need argument - } - else{ - if(opt == 0 || oindex > 0) optind = oindex; - else optind = get_optind(opt, options); - } - opts = &options[optind]; - // if(opt == 0 && opts->has_arg == NO_ARGS) continue; // only long option changing integer flag - // now check option - if(opts->has_arg == NEED_ARG || opts->has_arg == MULT_PAR) - if(!optarg) showhelp(optind, options); // need argument - void *aptr; - if(opts->has_arg == MULT_PAR){ - aptr = get_aptr(opts->argptr, opts->type); - }else - aptr = opts->argptr; - bool result = TRUE; - // even if there is no argument, but argptr != NULL, think that optarg = "1" - if(!optarg) optarg = "1"; - switch(opts->type){ - default: - case arg_none: - if(opts->argptr) *((int*)aptr) += 1; // increment value - break; - case arg_int: - result = myatoll(aptr, optarg, arg_int); - break; - case arg_longlong: - result = myatoll(aptr, optarg, arg_longlong); - break; - case arg_double: - result = myatod(aptr, optarg, arg_double); - break; - case arg_float: - result = myatod(aptr, optarg, arg_float); - break; - case arg_string: - result = (*((void**)aptr) = (void*)strdup(optarg)); - break; - case arg_function: - result = ((argfn)aptr)(optarg); - break; - } - if(!result){ - showhelp(optind, options); - } - } - *argc -= optind; - *argv += optind; -} - -/** - * compare function for qsort - * first - sort by short options; second - sort arguments without sort opts (by long options) - */ -static int argsort(const void *a1, const void *a2){ - const myoption *o1 = (myoption*)a1, *o2 = (myoption*)a2; - const char *l1 = o1->name, *l2 = o2->name; - int s1 = o1->val, s2 = o2->val; - int *f1 = o1->flag, *f2 = o2->flag; - // check if both options has short arg - if(f1 == NULL && f2 == NULL && s1 && s2){ // both have short arg - return (s1 - s2); - }else if((f1 != NULL || !s1) && (f2 != NULL || !s2)){ // both don't have short arg - sort by long - return strcmp(l1, l2); - }else{ // only one have short arg -- return it - if(f2 || !s2) return -1; // a1 have short - it is 'lesser' - else return 1; - } -} - -/** - * Show help information based on myoption->help values - * @param oindex (i) - if non-negative, show only help by myoption[oindex].help - * @param options (i) - array of `myoption` - * - * @exit: run `exit(-1)` !!! - */ -void showhelp(int oindex, myoption *options){ - int max_opt_len = 0; // max len of options substring - for right indentation - const int bufsz = 255; - char buf[bufsz+1]; - myoption *opts = options; - assert(opts); - assert(opts[0].name); // check whether there is at least one options - if(oindex > -1){ // print only one message - opts = &options[oindex]; - printf(" "); - if(!opts->flag && isalpha(opts->val)) printf("-%c, ", opts->val); - printf("--%s", opts->name); - if(opts->has_arg == 1) printf("=arg"); - else if(opts->has_arg == 2) printf("[=arg]"); - printf(" %s\n", _(opts->help)); - exit(-1); - } - // header, by default is just "progname\n" - printf("\n"); - if(strstr(helpstring, "%s")) // print progname - printf(helpstring, __progname); - else // only text - printf("%s", helpstring); - printf("\n"); - // count max_opt_len - do{ - int L = strlen(opts->name); - if(max_opt_len < L) max_opt_len = L; - }while((++opts)->name); - max_opt_len += 14; // format: '-S , --long[=arg]' - get addition 13 symbols - opts = options; - // count amount of options - int N; for(N = 0; opts->name; ++N, ++opts); - if(N == 0) exit(-2); - // Now print all help (sorted) - opts = options; - qsort(opts, N, sizeof(myoption), argsort); - do{ - int p = sprintf(buf, " "); // a little indent - if(!opts->flag && opts->val) // .val is short argument - p += snprintf(buf+p, bufsz-p, "-%c, ", opts->val); - p += snprintf(buf+p, bufsz-p, "--%s", opts->name); - if(opts->has_arg == 1) // required argument - p += snprintf(buf+p, bufsz-p, "=arg"); - else if(opts->has_arg == 2) // optional argument - p += snprintf(buf+p, bufsz-p, "[=arg]"); - assert(p < max_opt_len); // there would be magic if p >= max_opt_len - printf("%-*s%s\n", max_opt_len+1, buf, _(opts->help)); // write options & at least 2 spaces after - ++opts; - }while(--N); - printf("\n\n"); - exit(-1); -} - -/** - * get suboptions from parameter string - * @param str - parameter string - * @param opt - pointer to suboptions structure - * @return TRUE if all OK - */ -bool get_suboption(char *str, mysuboption *opt){ - int findsubopt(char *par, mysuboption *so){ - int idx = 0; - if(!par) return -1; - while(so[idx].name){ - if(strcasecmp(par, so[idx].name) == 0) return idx; - ++idx; - } - return -1; // badarg - } - bool opt_setarg(mysuboption *so, int idx, char *val){ - mysuboption *soptr = &so[idx]; - bool result = FALSE; - void *aptr = soptr->argptr; - switch(soptr->type){ - default: - case arg_none: - if(soptr->argptr) *((int*)aptr) += 1; // increment value - result = TRUE; - break; - case arg_int: - result = myatoll(aptr, val, arg_int); - break; - case arg_longlong: - result = myatoll(aptr, val, arg_longlong); - break; - case arg_double: - result = myatod(aptr, val, arg_double); - break; - case arg_float: - result = myatod(aptr, val, arg_float); - break; - case arg_string: - result = (*((void**)aptr) = (void*)strdup(val)); - break; - case arg_function: - result = ((argfn)aptr)(val); - break; - } - return result; - } - char *tok; - bool ret = FALSE; - char *tmpbuf; - tok = strtok_r(str, ":,", &tmpbuf); - do{ - char *val = strchr(tok, '='); - int noarg = 0; - if(val == NULL){ // no args - val = "1"; - noarg = 1; - }else{ - *val++ = '\0'; - if(!*val || *val == ':' || *val == ','){ // no argument - delimeter after = - val = "1"; noarg = 1; - } - } - int idx = findsubopt(tok, opt); - if(idx < 0){ - /// " : %s" - WARNX(_("Wrong parameter: %s"), tok); - goto returning; - } - if(noarg && opt[idx].has_arg == NEED_ARG){ - /// "%s: !" - WARNX(_("%s: argument needed!"), tok); - goto returning; - } - if(!opt_setarg(opt, idx, val)){ - /// " \"%s\" \"%s\"" - WARNX(_("Wrong argument \"%s\" of parameter \"%s\""), val, tok); - goto returning; - } - }while((tok = strtok_r(NULL, ":,", &tmpbuf))); - ret = TRUE; -returning: - return ret; -} diff --git a/src/netdaemon/parseargs.h b/src/netdaemon/parseargs.h deleted file mode 100644 index 537fc5b..0000000 --- a/src/netdaemon/parseargs.h +++ /dev/null @@ -1,124 +0,0 @@ -/* geany_encoding=koi8-r - * parseargs.h - headers for parsing command line arguments - * - * Copyright 2013 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. - */ -#pragma once -#ifndef __PARSEARGS_H__ -#define __PARSEARGS_H__ - -#include // bool -#include - -#ifndef TRUE - #define TRUE true -#endif - -#ifndef FALSE - #define FALSE false -#endif - -// macro for argptr -#define APTR(x) ((void*)x) - -// if argptr is a function: -typedef bool(*argfn)(void *arg); - -/* - * type of getopt's argument - * WARNING! - * My function change value of flags by pointer, so if you want to use another type - * make a latter conversion, example: - * char charg; - * int iarg; - * myoption opts[] = { - * {"value", 1, NULL, 'v', arg_int, &iarg, "char val"}, ..., end_option}; - * ..(parse args).. - * charg = (char) iarg; - */ -typedef enum { - arg_none = 0, // no arg - arg_int, // integer - arg_longlong, // long long - arg_double, // double - arg_float, // float - arg_string, // char * - arg_function // parse_args will run function `bool (*fn)(char *optarg, int N)` -} argtype; - -/* - * Structure for getopt_long & help - * BE CAREFUL: .argptr is pointer to data or pointer to function, - * conversion depends on .type - * - * ATTENTION: string `help` prints through macro PRNT(), bu default it is gettext, - * but you can redefine it before `#include "parseargs.h"` - * - * if arg is string, then value wil be strdup'ed like that: - * char *str; - * myoption opts[] = {{"string", 1, NULL, 's', arg_string, &str, "string val"}, ..., end_option}; - * *(opts[1].str) = strdup(optarg); - * in other cases argptr should be address of some variable (or pointer to allocated memory) - * - * NON-NULL argptr should be written inside macro APTR(argptr) or directly: (void*)argptr - * - * !!!LAST VALUE OF ARRAY SHOULD BE `end_option` or ZEROS !!! - * - */ -typedef enum{ - NO_ARGS = 0, // first three are the same as in getopt_long - NEED_ARG = 1, - OPT_ARG = 2, - MULT_PAR -} hasarg; - -typedef struct{ - // these are from struct option: - const char *name; // long option's name - hasarg has_arg; // 0 - no args, 1 - nesessary arg, 2 - optionally arg, 4 - need arg & key can repeat (args are stored in null-terminated array) - int *flag; // NULL to return val, pointer to int - to set its value of val (function returns 0) - int val; // short opt name (if flag == NULL) or flag's value - // and these are mine: - argtype type; // type of argument - void *argptr; // pointer to variable to assign optarg value or function `bool (*fn)(char *optarg, int N)` - const char *help; // help string which would be shown in function `showhelp` or NULL -} myoption; - -/* - * Suboptions structure, almost the same like myoption - * used in parse_subopts() - */ -typedef struct{ - const char *name; - hasarg has_arg; - argtype type; - void *argptr; -} mysuboption; - -// last string of array (all zeros) -#define end_option {0,0,0,0,0,0,0} -#define end_suboption {0,0,0,0} - -extern const char *__progname; - -void showhelp(int oindex, myoption *options); -void parseargs(int *argc, char ***argv, myoption *options); -void change_helpstring(char *s); -bool get_suboption(char *str, mysuboption *opt); - -#endif // __PARSEARGS_H__ diff --git a/src/netdaemon/plot b/src/netdaemon/plot old mode 100644 new mode 100755 diff --git a/src/netdaemon/scripts/getdata b/src/netdaemon/scripts/getdata new file mode 100755 index 0000000..78d20a8 --- /dev/null +++ b/src/netdaemon/scripts/getdata @@ -0,0 +1,15 @@ +#!/bin/bash +# receive data from mirtemp & store it + +export http_proxy="" + +# don't call curl if there's no connection to server +curl --http0.9 http://mirtemp.sao.ru:4444/Tmean || exit 1 + +cd $1 +DIR="$(date +%y.%m.%d)" +mkdir $DIR 2>/dev/null +FNAME="${DIR}/$(date +%H:%M)" +for Tdat in T0 T1 T2; do + curl --http0.9 http://mirtemp.sao.ru:4444/${Tdat} -o ${FNAME}_${Tdat}.dat > /dev/null 2>/dev/null +done diff --git a/src/netdaemon/scripts/plot/Archive_one_month b/src/netdaemon/scripts/plot/Archive_one_month new file mode 100755 index 0000000..824c251 --- /dev/null +++ b/src/netdaemon/scripts/plot/Archive_one_month @@ -0,0 +1,12 @@ +#!/bin/bash +# +# ./Archive_one_month , like: +# +# ./Archive_one_month 21.03 + +if [ $# -ne 1 ]; then + echo -e "Usage: $0 , like\n\t$0 21.03" + exit 1 +fi + +for x in $(seq 1 31); do D=$(printf "$1.%02d" $x); echo "tar $D"; tar -zcf ${D}.tgz $D; done diff --git a/src/netdaemon/scripts/plot/findallextr b/src/netdaemon/scripts/plot/findallextr new file mode 100755 index 0000000..8a46450 --- /dev/null +++ b/src/netdaemon/scripts/plot/findallextr @@ -0,0 +1,9 @@ +#!/bin/bash +if [ $# != 1 ]; then + echo "USAGE: $0 # (# is 0 or 1)" + echo -e "\t to print all extrems for given sensors group" + exit 1 +fi + + +find . -name "*T${1}.dat" -exec ./findextrems {} \; diff --git a/src/netdaemon/scripts/plot/findextrems b/src/netdaemon/scripts/plot/findextrems new file mode 100755 index 0000000..ff5ed65 --- /dev/null +++ b/src/netdaemon/scripts/plot/findextrems @@ -0,0 +1,13 @@ +#!/bin/bash + +if [ $# != 1 ]; then + echo "USAGE: $0 file.dat" + echo -e "\t to print extremal delta T" + exit 1 +fi + +MIN=$(cat $1 | awk '{print $4}' |sort -n | head -1) +MAX=$(cat $1 | awk '{print $4}' |sort -n | tail -1) + +echo "$1: $(echo $MAX - $MIN | bc -l)" + diff --git a/src/netdaemon/scripts/plot/plotALLjpg b/src/netdaemon/scripts/plot/plotALLjpg new file mode 100755 index 0000000..1eccb94 --- /dev/null +++ b/src/netdaemon/scripts/plot/plotALLjpg @@ -0,0 +1,54 @@ +#!/bin/bash +# +# run it like +# for f in *.dat; do ../plotALLjpg $f; done +# +OUT=tmpfile.txt +awk '{print $2 "\t" $3 "\t" $4}' $1 > $OUT +DATE=$(echo $1 | sed -e 's|/| |' -e 's|_.*||') +TX=$(echo $1 | sed 's|.*_\(.*\).dat|\1|') #' +TXO=$(echo $1 | sed 's|\.dat|.jpg|') #' +if [ $TX = "T0" ]; then Tname="TOP side" +else Tname="BOTTOM side" +fi +VAL=$(head -n1 $1 | awk '{print $4}') +echo -e "30\t30\t$VAL\n-30\t-30\t$VAL" >> $OUT +cat << EOF > gnutplt +#!/usr/bin/gnuplot +set contour +unset surface +set cntrparam order 4 +set cntrparam bspline +#set cntrparam levels auto 6 +#set cntrparam levels incremental -30,0.1,30 +set view map +set size square +set xrange [-40:40] +set yrange [-40:40] +set dgrid3d 100,100,4 +set table "contour.txt" +splot '$OUT' u 1:2:3 +unset table +unset contour +set surface +set table "dgrid.txt" +splot '$OUT' u 1:2:3 +unset table +reset +set terminal jpeg enhanced size 1024,768 +set output "$TXO" +set size square +set xrange [-30:30] +set yrange [-30:30] +set xlabel "X, dm" +set ylabel "Y, dm" +set title "Mirror temperature $TX for $DATE ($Tname)" +set pm3d map +unset key +circle(x,y,z) = x**2+y**2 > 900 ? NaN : z +splot 'dgrid.txt' u 1:2:(circle(\$1,\$2,\$3)) w pm3d, 'contour.txt' u 1:2:(circle(\$1,\$2,\$3)) w l lc rgb "black", '$OUT' u 1:2:(circle(\$1,\$2,\$3)):3 with labels font ",8" +EOF + +chmod 755 gnutplt +./gnutplt +rm -f gnutplt $OUT contour.txt dgrid.txt diff --git a/src/netdaemon/scripts/plot/plotjpg b/src/netdaemon/scripts/plot/plotjpg new file mode 100755 index 0000000..0a6327f --- /dev/null +++ b/src/netdaemon/scripts/plot/plotjpg @@ -0,0 +1,53 @@ +#!/bin/bash +# +# run it like +# ./plot 19.12.25/11\:20_T0.dat +# +OUT=tmpfile.txt +awk '{print $2 "\t" $3 "\t" $4}' $1 > $OUT +DATE=$(echo $1 | sed -e 's|/| |' -e 's|_.*||') +TX=$(echo $1 | sed 's|.*_\(.*\).dat|\1|') +if [ $TX = "T0" ]; then Tname="TOP side" +else Tname="BOTTOM side" +fi +VAL=$(head -n1 $1 | awk '{print $4}') +echo -e "30\t30\t$VAL\n-30\t-30\t$VAL" >> $OUT +cat << EOF > gnutplt +#!/usr/bin/gnuplot +set contour +unset surface +set cntrparam order 4 +set cntrparam bspline +#set cntrparam levels auto 6 +#set cntrparam levels incremental -30,0.1,30 +set view map +set size square +set xrange [-40:40] +set yrange [-40:40] +set dgrid3d 100,100,4 +set table "contour.txt" +splot '$OUT' u 1:2:3 +unset table +unset contour +set surface +set table "dgrid.txt" +splot '$OUT' u 1:2:3 +unset table +reset +set terminal jpeg enhanced size 1024,768 +set output "$TX.jpg" +set size square +set xrange [-30:30] +set yrange [-30:30] +set xlabel "X, dm" +set ylabel "Y, dm" +set title "Mirror temperature $TX for $DATE ($Tname)" +set pm3d map +unset key +circle(x,y,z) = x**2+y**2 > 900 ? NaN : z +splot 'dgrid.txt' u 1:2:(circle(\$1,\$2,\$3)) w pm3d, 'contour.txt' u 1:2:(circle(\$1,\$2,\$3)) w l lc rgb "black", '$OUT' u 1:2:(circle(\$1,\$2,\$3)):3 with labels font ",8" +EOF + +chmod 755 gnutplt +./gnutplt +rm -f gnutplt $OUT contour.txt dgrid.txt diff --git a/src/netdaemon/sens_place.c b/src/netdaemon/sens_place.c index 80f6635..8498973 100644 --- a/src/netdaemon/sens_place.c +++ b/src/netdaemon/sens_place.c @@ -16,10 +16,13 @@ * along with this program. If not, see . */ +#include // fabs +#include +#include +#include +#include + #include "sens_place.h" -#include "stdbool.h" -#include "usefull_macros.h" -#include "math.h" // fabs /** Sensor place Dt X Y Z @@ -294,13 +297,13 @@ cont: if(fabs(Tadj[i]) > DBL_EPSILON){ printf("\tTadj[%02d] = %g\n", i, Tadj[i]); } - if(fabs(sensors[i].Tadj - Tadj[i]) > 0.001) LOG("Tadj[%d] = %g", i, Tadj[i]); + if(fabs(sensors[i].Tadj - Tadj[i]) > 0.001) LOGMSG("Tadj[%d] = %g", i, Tadj[i]); sensors[i].Tadj = Tadj[i]; } return 0; reperr: red("Error in string %d:\n", strnum); printf("%s\n", adjf); - LOG("Erroneous adjustment file %s in line %d", fname, strnum); + LOGWARN("Erroneous adjustment file %s in line %d", fname, strnum); return 1; } diff --git a/src/netdaemon/socket.c b/src/netdaemon/socket.c index ed490a0..30143b1 100644 --- a/src/netdaemon/socket.c +++ b/src/netdaemon/socket.c @@ -20,20 +20,22 @@ * MA 02110-1301, USA. * */ -#include "usefull_macros.h" +#include // inet_ntop +#include // INT_xxx +#include // addrinfo +#include +#include // pthread_kill +#include +#include +#include // syscall +#include // daemon +#include + +#include "cmdlnopts.h" // glob_pars +#include "gnuplot.h" // plot graphs +#include "sens_place.h" // sensors coordinates #include "socket.h" #include "term.h" -#include // addrinfo -#include // inet_ntop -#include -#include // INT_xxx -#include // pthread_kill -#include // daemon -#include // syscall - -#include "sens_place.h" // sensors coordinates -#include "gnuplot.h" // plot graphs -#include "cmdlnopts.h" // glob_pars #define BUFLEN (10240) // Max amount of connections @@ -191,14 +193,14 @@ static void *handle_socket(void *asock){ } pthread_mutex_lock(&mutex); if(!send_data(sock, webquery, Nsens)){ - LOG("can't send data, some error occured"); + LOGWARN("can't send data, some error occured"); } pthread_mutex_unlock(&mutex); }else if(strncmp("Tmean", found, 5) == 0){ // send user meanT L = snprintf(tbuf, 128, "%.2f\n", meanT); if(L != write(sock, tbuf, L)) WARN("write()"); }else if(strncmp("ReBoOt", found, 6) == 0){ - LOG("Reboot command from %s", C.host); + LOGWARN("Reboot command from %s", C.host); L = write(sock, "Reboot system\n", 14); if(0 != system("sudo reboot")) WARN("Can't reboot"); // here can be more parsers @@ -234,7 +236,7 @@ static void *server(void *asock){ struct in_addr ipAddr = pV4Addr->sin_addr; char str[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &ipAddr, str, INET_ADDRSTRLEN); - //LOG("get connection from %s", str); + LOGMSG("get connection from %s", str); red("Got connection from %s\n", str); pthread_t handler_thread; conn C = {.sockn = newsock}; @@ -246,7 +248,7 @@ static void *server(void *asock){ pthread_detach(handler_thread); // don't care about thread state } } - LOG("server(): UNREACHABLE CODE REACHED!"); + LOGERR("server(): UNREACHABLE CODE REACHED!"); } typedef double Item; @@ -368,7 +370,7 @@ static void daemon_(int sock){ if(pthread_create(&sock_thread, NULL, server, (void*) &sock)){ ERR("pthread_create()"); } - double tgot = 0., tlastoff = dtime(); + double tgot = 0.;//, tlastoff = dtime(); do{ if(pthread_kill(sock_thread, 0) == ESRCH){ // died WARNX("Sockets thread died"); @@ -381,12 +383,12 @@ static void daemon_(int sock){ if(TurnOff){ TurnOff = FALSE; turn_all_off(); - tlastoff = dtime(); - } + //tlastoff = dtime(); + }/* if(dtime() - tlastoff > T_OFF_INTERVAL){ turn_all_off(); tlastoff = dtime(); - } + }*/ if(dtime() - tgot < T_INTERVAL) continue; // get data int i; @@ -428,7 +430,7 @@ static void daemon_(int sock){ memcpy(strT, bufs, sizeof(strT)); pthread_mutex_unlock(&mutex); }while(1); - LOG("daemon_(): UNREACHABLE CODE REACHED!"); + LOGERR("daemon_(): UNREACHABLE CODE REACHED!"); } /** @@ -474,7 +476,7 @@ void daemonize(char *port){ freeaddrinfo(res); daemon_(sock); close(sock); - LOG("daemonize(): UNREACHABLE CODE REACHED!"); + LOGERR("daemonize(): UNREACHABLE CODE REACHED!"); signals(0); } diff --git a/src/netdaemon/tempadj.txt b/src/netdaemon/tempadj.txt index 29def47..e0761e6 100644 --- a/src/netdaemon/tempadj.txt +++ b/src/netdaemon/tempadj.txt @@ -1 +1,6 @@ #No dT (Treal = T - dT) +251 -0.7 +320 0.12 +330 0.15 +360 0.15 +520 0.05 diff --git a/src/netdaemon/term.c b/src/netdaemon/term.c index 6a8688b..3f401be 100644 --- a/src/netdaemon/term.c +++ b/src/netdaemon/term.c @@ -18,11 +18,23 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ -#include "usefull_macros.h" -#include "term.h" -#include // strncasecmp -#include // time(NULL) +#include #include // INT_MAX, INT_MIN +#include +#include +#include +#include +#include +#include // strncasecmp +#include +#include +#include +#include +#include // unix socket +#include // time(NULL) +#include + +#include "term.h" #define BUFLEN 1024 @@ -30,76 +42,79 @@ time_t tmeasured[2][NCHANNEL_MAX+1][NCTRLR_MAX+1]; // last temperatures read: [Ngroup][Nsensor][Ncontroller] double t_last[2][NCHANNEL_MAX+1][NCTRLR_MAX+1]; +static int sock = -1; // server UNIX-socket fd /** - * read strings from terminal (ending with '\n') with timeout - * @param L - its length + * wait for answer from socket + * @param sock - socket fd + * @return 0 in case of error or timeout, 1 in case of socket ready + */ +static int waittoread(int sock){ + fd_set fds; + struct timeval timeout; + int rc; + timeout.tv_sec = 0; + timeout.tv_usec = 100; + FD_ZERO(&fds); + FD_SET(sock, &fds); + do{ + rc = select(sock+1, &fds, NULL, NULL, &timeout); + if(rc < 0){ + if(errno != EINTR){ + WARN("select()"); + return 0; + } + continue; + } + break; + }while(1); + if(FD_ISSET(sock, &fds)){ + //DBG("FD_ISSET"); + return 1; + } + return 0; +} + +/** + * read string from server, remove trailing '\n' * @return NULL if nothing was read or pointer to static buffer */ static char *read_string(){ - size_t r = 0, l; static char buf[BUFLEN]; - int LL = BUFLEN - 1; - char *ptr = NULL; - static char *optr = NULL; - if(optr && *optr){ - ptr = optr; - optr = strchr(optr, '\n'); - if(optr) ++optr; - //DBG("got data, roll to next; ptr=%s\noptr=%s",ptr,optr); - return ptr; + if(!waittoread(sock)) return NULL; + int n = read(sock, buf, BUFLEN-1); + if(n == 0){ + LOGERR("UNIX-socket server disconnected"); + ERRX("Server disconnected"); } - ptr = buf; - double d0 = dtime(); - do{ - if((l = read_tty(ptr, LL))){ - r += l; LL -= l; ptr += l; - if(ptr[-1] == '\n') break; - d0 = dtime(); - } - }while(dtime() - d0 < WAIT_TMOUT && LL); - if(r){ - buf[r] = 0; - //DBG("r=%zd, got string: %s", r, buf); - optr = strchr(buf, '\n'); - if(optr) ++optr; - return buf; - } - return NULL; + buf[n] = 0; + if(buf[n-1] == '\n') buf[n-1] = 0; + LOGDBG("SERIAL: %s", buf); + DBG("SERIAL: %s", buf); + return buf; } /** - * Try to connect to `device` at BAUD_RATE speed - * @return connection speed if success or 0 + * Try to connect to UNIX socket `path` + * @return FALSE if failed */ -void try_connect(char *device){ - if(!device) return; - char tmpbuf[4096]; - fflush(stdout); - tty_init(device); - read_tty(tmpbuf, 4096); // clear rbuf - LOG("Connected to %s", device); -} - -/** - * run terminal emulation: send user's commands and show answers - */ -void run_terminal(){ - green(_("Work in terminal mode without echo\n")); - int rb; - char buf[BUFLEN]; - size_t l; - setup_con(); - while(1){ - if((l = read_tty(buf, BUFLEN - 1))){ - buf[l] = 0; - printf("%s", buf); - } - if((rb = read_console())){ - buf[0] = (char) rb; - write_tty(buf, 1); - } +int try_connect(char *path){ + if(!path) return FALSE; + struct sockaddr_un saddr = {0}; + saddr.sun_family = AF_UNIX; + strncpy(saddr.sun_path, path, 106); // if sun_path[0] == 0 we don't create a file + if((sock = socket(AF_UNIX, SOCK_SEQPACKET, 0)) < 0){ // or SOCK_STREAM? + WARN("socket()"); + LOGERR("socket()"); + return FALSE; } + if(connect(sock, &saddr, sizeof(saddr)) == -1){ + WARN("connect()"); + LOGERR("connect()"); + return FALSE; + } + LOGMSG("Connected to server"); + return TRUE; } /** @@ -172,18 +187,22 @@ static int send_cmd(int N, char cmd){ // clear all incomint data while(read_string()); DBG("send cmd %s", buf); - if(write_tty(buf, n)) return 1; + if(n != send(sock, buf, n, 0)) return 1; if(N == 0) return 0; - if((rtn = read_string())){ - DBG("read_string: %s", rtn); - if(*rtn == cmd) return 0; + double t0 = dtime(); + while(dtime() - t0 < T_POLLING_TMOUT){ + if((rtn = read_string())){ + DBG("read_string: %s", rtn); + if(*rtn == cmd) return 0; + } } + DBG("No answer"); return 1; } /** * Poll sensor for new dataportion - * @param N - number of controller (1..7) + * @param N - number of controller (0..7) * @return: 0 if no data received or controller is absent, number of data points if valid data received */ int poll_sensors(int N){ @@ -229,8 +248,12 @@ int check_sensors(){ for(i = 1; i <= NCTRLR_MAX; ++i){ //red("i = %d\n", i); double t0 = dtime(); + if(send_cmd(i, CMD_PING)){ + usleep(100000); + --i; + continue; + } while(dtime() - t0 < POLLING_TMOUT){ - if(send_cmd(i, CMD_PING)) continue; if((ans = read_string())){ //DBG("got: %s", ans); if(0 == strncmp(ans, ANS_PONG, sizeof(ANS_PONG)-1)){ @@ -238,7 +261,7 @@ int check_sensors(){ if(i == ans[sizeof(ANS_PONG)-1] - '0'){ ++found; green(_("Found controller #%d\n"), i); - LOG("Found controller #%d", i); + LOGMSG("Found controller #%d", i); break; } } diff --git a/src/netdaemon/term.h b/src/netdaemon/term.h index f5dd504..5ea2f0a 100644 --- a/src/netdaemon/term.h +++ b/src/netdaemon/term.h @@ -22,6 +22,8 @@ #ifndef __TERM_H__ #define __TERM_H__ +#include + #include "sens_place.h" // NCTRLR // Terminal timeout (seconds) @@ -56,8 +58,7 @@ typedef enum{ TRANS_TIMEOUT // no data in WAIT_TMOUT } trans_status; -void run_terminal(); -void try_connect(char *device); +int try_connect(char *path); int poll_sensors(int N); int check_sensors(); void turn_all_off(); diff --git a/src/netdaemon/usefull_macros.c b/src/netdaemon/usefull_macros.c deleted file mode 100644 index 26b5d5b..0000000 --- a/src/netdaemon/usefull_macros.c +++ /dev/null @@ -1,593 +0,0 @@ -/* - * usefull_macros.h - a set of usefull functions: memory, color etc - * - * Copyright 2013 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 "usefull_macros.h" -#include -#include // PATH_MAX -#include - -/** - * function for different purposes that need to know time intervals - * @return double value: time in seconds - */ -double dtime(){ - double t; - struct timeval tv; - gettimeofday(&tv, NULL); - t = tv.tv_sec + ((double)tv.tv_usec)/1e6; - return t; -} - -/******************************************************************************\ - * Coloured terminal -\******************************************************************************/ -int globErr = 0; // errno for WARN/ERR - -// pointers to coloured output printf -int (*red)(const char *fmt, ...); -int (*green)(const char *fmt, ...); -int (*_WARN)(const char *fmt, ...); - -/* - * format red / green messages - * name: r_pr_, g_pr_ - * @param fmt ... - printf-like format - * @return number of printed symbols - */ -int r_pr_(const char *fmt, ...){ - va_list ar; int i; - printf(RED); - va_start(ar, fmt); - i = vprintf(fmt, ar); - va_end(ar); - printf(OLDCOLOR); - return i; -} -int g_pr_(const char *fmt, ...){ - va_list ar; int i; - printf(GREEN); - va_start(ar, fmt); - i = vprintf(fmt, ar); - va_end(ar); - printf(OLDCOLOR); - return i; -} -/* - * print red error/warning messages (if output is a tty) - * @param fmt ... - printf-like format - * @return number of printed symbols - */ -int r_WARN(const char *fmt, ...){ - va_list ar; int i = 1; - fprintf(stderr, RED); - va_start(ar, fmt); - if(globErr){ - errno = globErr; - vwarn(fmt, ar); - errno = 0; - }else - i = vfprintf(stderr, fmt, ar); - va_end(ar); - i++; - fprintf(stderr, OLDCOLOR "\n"); - return i; -} - -static const char stars[] = "****************************************"; -/* - * notty variants of coloured printf - * name: s_WARN, r_pr_notty - * @param fmt ... - printf-like format - * @return number of printed symbols - */ -int s_WARN(const char *fmt, ...){ - va_list ar; int i; - i = fprintf(stderr, "\n%s\n", stars); - va_start(ar, fmt); - if(globErr){ - errno = globErr; - vwarn(fmt, ar); - errno = 0; - }else - i = +vfprintf(stderr, fmt, ar); - va_end(ar); - i += fprintf(stderr, "\n%s\n", stars); - i += fprintf(stderr, "\n"); - return i; -} -int r_pr_notty(const char *fmt, ...){ - va_list ar; int i; - i = printf("\n%s\n", stars); - va_start(ar, fmt); - i += vprintf(fmt, ar); - va_end(ar); - i += printf("\n%s\n", stars); - return i; -} - -/** - * Run this function in the beginning of main() to setup locale & coloured output - */ -void initial_setup(){ - // setup coloured output - if(isatty(STDOUT_FILENO)){ // make color output in tty - red = r_pr_; green = g_pr_; - }else{ // no colors in case of pipe - red = r_pr_notty; green = printf; - } - if(isatty(STDERR_FILENO)) _WARN = r_WARN; - else _WARN = s_WARN; - // Setup locale - setlocale(LC_ALL, ""); - setlocale(LC_NUMERIC, "C"); -#if defined GETTEXT_PACKAGE && defined LOCALEDIR - bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); - textdomain(GETTEXT_PACKAGE); -#endif -} - -/******************************************************************************\ - * Memory -\******************************************************************************/ -/* - * safe memory allocation for macro ALLOC - * @param N - number of elements to allocate - * @param S - size of single element (typically sizeof) - * @return pointer to allocated memory area - */ -void *my_alloc(size_t N, size_t S){ - void *p = calloc(N, S); - if(!p) ERR("malloc"); - //assert(p); - return p; -} - -/** - * Mmap file to a memory area - * - * @param filename (i) - name of file to mmap - * @return stuct with mmap'ed file or die - */ -mmapbuf *My_mmap(char *filename){ - int fd; - char *ptr; - size_t Mlen; - struct stat statbuf; - /// " !" - if(!filename){ - WARNX(_("No filename given!")); - return NULL; - } - if((fd = open(filename, O_RDONLY)) < 0){ - /// " %s " - WARN(_("Can't open %s for reading"), filename); - return NULL; - } - if(fstat (fd, &statbuf) < 0){ - /// " stat %s" - WARN(_("Can't stat %s"), filename); - close(fd); - return NULL; - } - Mlen = statbuf.st_size; - if((ptr = mmap (0, Mlen, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED){ - /// " mmap" - WARN(_("Mmap error for input")); - close(fd); - return NULL; - } - /// " mmap' " - if(close(fd)) WARN(_("Can't close mmap'ed file")); - mmapbuf *ret = MALLOC(mmapbuf, 1); - ret->data = ptr; - ret->len = Mlen; - return ret; -} - -void My_munmap(mmapbuf *b){ - if(munmap(b->data, b->len)){ - /// " munmap" - WARN(_("Can't munmap")); - } - FREE(b); -} - - -/******************************************************************************\ - * Terminal in no-echo mode -\******************************************************************************/ -static struct termios oldt, newt; // terminal flags -static int console_changed = 0; -// run on exit: -void restore_console(){ - if(console_changed) - tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // return terminal to previous state - console_changed = 0; -} - -// initial setup: -void setup_con(){ - if(console_changed) return; - tcgetattr(STDIN_FILENO, &oldt); - newt = oldt; - newt.c_lflag &= ~(ICANON | ECHO); - if(tcsetattr(STDIN_FILENO, TCSANOW, &newt) < 0){ - /// " " - WARN(_("Can't setup console")); - tcsetattr(STDIN_FILENO, TCSANOW, &oldt); - signals(0); //quit? - } - console_changed = 1; -} - -/** - * Read character from console without echo - * @return char readed - */ -int read_console(){ - int rb; - struct timeval tv; - int retval; - fd_set rfds; - FD_ZERO(&rfds); - FD_SET(STDIN_FILENO, &rfds); - tv.tv_sec = 0; tv.tv_usec = 10000; - retval = select(1, &rfds, NULL, NULL, &tv); - if(!retval) rb = 0; - else { - if(FD_ISSET(STDIN_FILENO, &rfds)) rb = getchar(); - else rb = 0; - } - return rb; -} - -/** - * getchar() without echo - * wait until at least one character pressed - * @return character readed - */ -int mygetchar(){ // getchar() without need of pressing ENTER - int ret; - do ret = read_console(); - while(ret == 0); - return ret; -} - - -/******************************************************************************\ - * TTY with select() -\******************************************************************************/ -static struct termio oldtty, tty; // TTY flags -static int comfd = -1; // TTY fd - -// run on exit: -void restore_tty(){ - if(comfd == -1) return; - ioctl(comfd, TCSANOW, &oldtty ); // return TTY to previous state - close(comfd); - comfd = -1; -} - -#ifndef BAUD_RATE -#define BAUD_RATE B9600 -#endif -// init: -void tty_init(char *comdev){ - DBG("\nOpen port %s ...", comdev); - do{ - comfd = open(comdev,O_RDWR|O_NOCTTY|O_NONBLOCK); - }while (comfd == -1 && errno == EINTR); - if(comfd < 0){ - WARN("Can't use port %s",comdev); - signals(-1); // quit? - } - // make exclusive open - if(ioctl(comfd, TIOCEXCL)){ - WARN(_("Can't do exclusive open")); - close(comfd); - signals(2); - } - DBG(" OK\nGet current settings... "); - if(ioctl(comfd,TCGETA,&oldtty) < 0){ // Get settings - /// " " - WARN(_("Can't get settings")); - signals(-1); - } - tty = oldtty; - tty.c_lflag = 0; // ~(ICANON | ECHO | ECHOE | ISIG) - tty.c_oflag = 0; - tty.c_cflag = BAUD_RATE|CS8|CREAD|CLOCAL; // 9.6k, 8N1, RW, ignore line ctrl - tty.c_cc[VMIN] = 0; // non-canonical mode - tty.c_cc[VTIME] = 5; - if(ioctl(comfd,TCSETA,&tty) < 0){ - /// " " - WARN(_("Can't set settings")); - signals(-1); - } - DBG(" OK"); -} - -/** - * Read data from TTY - * @param buff (o) - buffer for data read - * @param length - buffer len - * @return amount of bytes read - */ -size_t read_tty(char *buff, size_t length){ - ssize_t L = 0, l; - char *ptr = buff; - fd_set rfds; - struct timeval tv; - int retval; - do{ - l = 0; - FD_ZERO(&rfds); - FD_SET(comfd, &rfds); - // wait for 100ms - tv.tv_sec = 0; tv.tv_usec = 100000; - retval = select(comfd + 1, &rfds, NULL, NULL, &tv); - if (retval < 1) break; // retval == 0 if there's no data, retval == 1 for EINTR - if(FD_ISSET(comfd, &rfds)){ - if((l = read(comfd, ptr, length)) < 1){ - //return 0; - ERR("USB disconnected!"); - } - ptr += l; L += l; - length -= l; - } - }while(l); - return (size_t)L; -} - -int write_tty(char *buff, size_t length){ - ssize_t L = write(comfd, buff, length); - if((size_t)L != length){ - /// " !" - ERR("Write error"); - } - return 0; -} - - -/** - * Safely convert data from string to double - * - * @param num (o) - double number read from string - * @param str (i) - input string - * @return 1 if success, 0 if fails - */ -int str2double(double *num, const char *str){ - double res; - char *endptr; - if(!str) return 0; - res = strtod(str, &endptr); - if(endptr == str || *str == '\0' || *endptr != '\0'){ - /// " double!" - WARNX("Wrong double number format!"); - return FALSE; - } - if(num) *num = res; // you may run it like myatod(NULL, str) to test wether str is double number - return TRUE; -} - -//////////// logging - -static Cl_log log = {0}; -/** - * @brief Cl_createlog - create log file: init mutex, test file open ability - * @param log - log structure - * @return 0 if all OK - */ -int Cl_createlog(char *logname){ - if(log.logpath){ - FREE(log.logpath); - pthread_mutex_destroy(&log.mutex); - } - FILE *logfd = fopen(logname, "a"); - if(!logfd){ - WARN("Can't open log file"); - return 2; - } - log.logpath = strdup(logname); - fclose(logfd); - if(pthread_mutex_init(&log.mutex, NULL)){ - WARN("Can't init log mutes"); - return 3; - } - return 0; -} - -/** - * @brief Cl_putlog - put message to log file with/without timestamp - * @param timest - ==1 to put timestamp - * @param log - pointer to log structure - * @param lvl - message loglevel (if lvl > loglevel, message won't be printed) - * @param fmt - format and the rest part of message - * @return amount of symbols saved in file - */ -int Cl_putlogt(const char *fmt, ...){ - if(pthread_mutex_lock(&log.mutex)){ - WARN("Can't lock log mutex"); - return 0; - } - int i = 0; - FILE *logfd = fopen(log.logpath, "a"); - if(!logfd) goto rtn; - char strtm[128]; - time_t t = time(NULL); - struct tm *curtm = localtime(&t); - strftime(strtm, 128, "%Y/%m/%d-%H:%M:%S", curtm); - i = fprintf(logfd, "%s\t", strtm); - va_list ar; - va_start(ar, fmt); - i += vfprintf(logfd, fmt, ar); - va_end(ar); - i += fprintf(logfd, "\n"); - fclose(logfd); -rtn: - pthread_mutex_unlock(&log.mutex); - return i; -} - - -/* - * Copyright (c) 1988, 1993, 1994, 2017 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -/* - * 2017-10-14 Niklas Hamb?chen - * - Extracted signal names mapping from kill.c - * - * Copyright (C) 2014 Sami Kerola - * Copyright (C) 2014 Karel Zak - * Copyright (C) 2017 Niklas Hamb?chen - */ - -// https://github.com/karelzak/util-linux/blob/master/lib/signames.c -static const struct ul_signal_name { - const char *name; - int val; -} ul_signames[] = { - /* POSIX signals */ - { "HUP", SIGHUP }, /* 1 */ - { "INT", SIGINT }, /* 2 */ - { "QUIT", SIGQUIT }, /* 3 */ - { "ILL", SIGILL }, /* 4 */ -#ifdef SIGTRAP - { "TRAP", SIGTRAP }, /* 5 */ -#endif - { "ABRT", SIGABRT }, /* 6 */ -#ifdef SIGIOT - { "IOT", SIGIOT }, /* 6, same as SIGABRT */ -#endif -#ifdef SIGEMT - { "EMT", SIGEMT }, /* 7 (mips,alpha,sparc*) */ -#endif -#ifdef SIGBUS - { "BUS", SIGBUS }, /* 7 (arm,i386,m68k,ppc), 10 (mips,alpha,sparc*) */ -#endif - { "FPE", SIGFPE }, /* 8 */ - { "KILL", SIGKILL }, /* 9 */ - { "USR1", SIGUSR1 }, /* 10 (arm,i386,m68k,ppc), 30 (alpha,sparc*), 16 (mips) */ - { "SEGV", SIGSEGV }, /* 11 */ - { "USR2", SIGUSR2 }, /* 12 (arm,i386,m68k,ppc), 31 (alpha,sparc*), 17 (mips) */ - { "PIPE", SIGPIPE }, /* 13 */ - { "ALRM", SIGALRM }, /* 14 */ - { "TERM", SIGTERM }, /* 15 */ -#ifdef SIGSTKFLT - { "STKFLT", SIGSTKFLT }, /* 16 (arm,i386,m68k,ppc) */ -#endif - { "CHLD", SIGCHLD }, /* 17 (arm,i386,m68k,ppc), 20 (alpha,sparc*), 18 (mips) */ -#ifdef SIGCLD - { "CLD", SIGCLD }, /* same as SIGCHLD (mips) */ -#endif - { "CONT", SIGCONT }, /* 18 (arm,i386,m68k,ppc), 19 (alpha,sparc*), 25 (mips) */ - { "STOP", SIGSTOP }, /* 19 (arm,i386,m68k,ppc), 17 (alpha,sparc*), 23 (mips) */ - { "TSTP", SIGTSTP }, /* 20 (arm,i386,m68k,ppc), 18 (alpha,sparc*), 24 (mips) */ - { "TTIN", SIGTTIN }, /* 21 (arm,i386,m68k,ppc,alpha,sparc*), 26 (mips) */ - { "TTOU", SIGTTOU }, /* 22 (arm,i386,m68k,ppc,alpha,sparc*), 27 (mips) */ -#ifdef SIGURG - { "URG", SIGURG }, /* 23 (arm,i386,m68k,ppc), 16 (alpha,sparc*), 21 (mips) */ -#endif -#ifdef SIGXCPU - { "XCPU", SIGXCPU }, /* 24 (arm,i386,m68k,ppc,alpha,sparc*), 30 (mips) */ -#endif -#ifdef SIGXFSZ - { "XFSZ", SIGXFSZ }, /* 25 (arm,i386,m68k,ppc,alpha,sparc*), 31 (mips) */ -#endif -#ifdef SIGVTALRM - { "VTALRM", SIGVTALRM }, /* 26 (arm,i386,m68k,ppc,alpha,sparc*), 28 (mips) */ -#endif -#ifdef SIGPROF - { "PROF", SIGPROF }, /* 27 (arm,i386,m68k,ppc,alpha,sparc*), 29 (mips) */ -#endif -#ifdef SIGWINCH - { "WINCH", SIGWINCH }, /* 28 (arm,i386,m68k,ppc,alpha,sparc*), 20 (mips) */ -#endif -#ifdef SIGIO - { "IO", SIGIO }, /* 29 (arm,i386,m68k,ppc), 23 (alpha,sparc*), 22 (mips) */ -#endif -#ifdef SIGPOLL - { "POLL", SIGPOLL }, /* same as SIGIO */ -#endif -#ifdef SIGINFO - { "INFO", SIGINFO }, /* 29 (alpha) */ -#endif -#ifdef SIGLOST - { "LOST", SIGLOST }, /* 29 (arm,i386,m68k,ppc,sparc*) */ -#endif -#ifdef SIGPWR - { "PWR", SIGPWR }, /* 30 (arm,i386,m68k,ppc), 29 (alpha,sparc*), 19 (mips) */ -#endif -#ifdef SIGUNUSED - { "UNUSED", SIGUNUSED }, /* 31 (arm,i386,m68k,ppc) */ -#endif -#ifdef SIGSYS - { "SYS", SIGSYS }, /* 31 (mips,alpha,sparc*) */ -#endif -}; - -const char *signum_to_signame(int signum){ - size_t n; - static char buf[32]; - if(signum < 1) return NULL; -#if defined SIGRTMIN && defined SIGRTMAX - if(signum >= SIGRTMIN && signum <= SIGRTMAX){ - snprintf(buf, 32, "SIGRT%d", signum - SIGRTMIN); - return buf; - } -#endif - for (n = 0; n < ARRAY_SIZE(ul_signames); n++) { - if(ul_signames[n].val == signum){ - snprintf(buf, 32, "SIG%s", ul_signames[n].name); - return buf; - } - } - return NULL; -} diff --git a/src/netdaemon/usefull_macros.h b/src/netdaemon/usefull_macros.h deleted file mode 100644 index e453eaa..0000000 --- a/src/netdaemon/usefull_macros.h +++ /dev/null @@ -1,148 +0,0 @@ -/* - * usefull_macros.h - a set of usefull macros: memory, color etc - * - * Copyright 2013 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. - */ - -#pragma once -#ifndef __USEFULL_MACROS_H__ -#define __USEFULL_MACROS_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if defined GETTEXT_PACKAGE && defined LOCALEDIR -/* - * GETTEXT - */ -#include -#define _(String) gettext(String) -#define gettext_noop(String) String -#define N_(String) gettext_noop(String) -#else -#define _(String) (String) -#define N_(String) (String) -#endif -#include -#include -#include -#include -#include -#include -#include - - -// unused arguments with -Wall -Werror -#define _U_ __attribute__((__unused__)) - -/* - * Coloured messages output - */ -#define RED "\033[1;31;40m" -#define GREEN "\033[1;32;40m" -#define OLDCOLOR "\033[0;0;0m" - -#ifndef FALSE -#define FALSE (0) -#endif - -#ifndef TRUE -#define TRUE (1) -#endif - -/* - * ERROR/WARNING messages - */ -extern int globErr; -extern void signals(int sig); -#define ERR(...) do{globErr=errno; Cl_putlogt(__VA_ARGS__); _WARN(__VA_ARGS__); signals(9);}while(0) -#define ERRX(...) do{globErr=0; Cl_putlogt(__VA_ARGS__); _WARN(__VA_ARGS__); signals(9);}while(0) -#define WARN(...) do{globErr=errno; Cl_putlogt(__VA_ARGS__); _WARN(__VA_ARGS__);}while(0) -#define WARNX(...) do{globErr=0; Cl_putlogt(__VA_ARGS__); _WARN(__VA_ARGS__);}while(0) -#define LOG(...) do{Cl_putlogt(__VA_ARGS__); }while(0) -/* - * print function name, debug messages - * debug mode, -DEBUG - */ -#ifdef EBUG - #define FNAME() printf("\n%s (%s, line %d)\n", __func__, __FILE__, __LINE__) - #define DBG(...) do{printf("%s (%s, line %d): ", __func__, __FILE__, __LINE__); \ - printf(__VA_ARGS__); printf("\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 - -double dtime(); - -// functions for color output in tty & no-color in pipes -extern int (*red)(const char *fmt, ...); -extern int (*_WARN)(const char *fmt, ...); -extern int (*green)(const char *fmt, ...); -void * my_alloc(size_t N, size_t S); -void initial_setup(); - -// mmap file -typedef struct{ - char *data; - size_t len; -} mmapbuf; -mmapbuf *My_mmap(char *filename); -void My_munmap(mmapbuf *b); - -void restore_console(); -void setup_con(); -int read_console(); -int mygetchar(); - -void restore_tty(); -void tty_init(char *comdev); -size_t read_tty(char *buff, size_t length); -int write_tty(char *buff, size_t length); - -int str2double(double *num, const char *str); - -typedef struct{ - char *logpath; // full path to logfile - pthread_mutex_t mutex; // log mutex -} Cl_log; - -int Cl_createlog(char *logname); -int Cl_putlogt(const char *fmt, ...); - -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) -const char *signum_to_signame(int signum); -#endif // __USEFULL_MACROS_H__ diff --git a/src/netdaemon/www/T.html b/src/netdaemon/www/T.html new file mode 100644 index 0000000..2b61315 --- /dev/null +++ b/src/netdaemon/www/T.html @@ -0,0 +1,1021 @@ + + + +Gnuplot Canvas Graph + + + + + + + + + + + + +
+ +
+ + +
+ + + +
+ + + + + + + + + +
#unzoomrezoomzoom text?
+
+ + + +
 
 
+
+ + +
+ + Sorry, your browser seems not to support the HTML 5 canvas element + +
+
+
+ + + diff --git a/src/netdaemon/www/T0 b/src/netdaemon/www/T0 new file mode 100644 index 0000000..7a4db87 --- /dev/null +++ b/src/netdaemon/www/T0 @@ -0,0 +1,45 @@ +19 -7 -2.85 +17 -22 -2.99 +15 -13 -2.92 +22 -17 -2.80 +25 -10 -2.81 +27 -4 -2.73 +27 4 -2.66 +25 10 -2.58 +10 25 -2.66 +17 22 -2.69 +-10 -25 -3.02 +-4 -27 -3.11 +-3 -20 -3.09 +-10 -17 -3.03 +-15 -13 -2.94 +-19 -7 -2.84 +-20 0 -2.78 +-19 7 -2.84 +-15 13 -2.83 +-10 17 -2.76 +-3 20 -2.75 +3 20 -2.79 +10 17 -2.76 +9 9 -2.74 +3 13 -2.73 +-9 9 -2.76 +-3 13 -2.70 +-13 3 -2.77 +-13 -3 -2.82 +-9 -9 -2.86 +-3 -13 -2.94 +3 -13 -2.90 +3 -20 -3.03 +4 -27 -3.08 +10 -17 -3.02 +10 -25 -2.97 +9 -9 -2.85 +3 -5 -2.80 +-3 -5 -2.81 +-6 0 -2.75 +-3 5 -2.78 +3 5 -2.66 +6 0 -2.73 +13 -3 -2.73 +13 3 -2.67 diff --git a/src/netdaemon/www/T0.html b/src/netdaemon/www/T0.html new file mode 100644 index 0000000..1431218 --- /dev/null +++ b/src/netdaemon/www/T0.html @@ -0,0 +1,55395 @@ + + + +Gnuplot Canvas Graph + + + + + + + + + + + + +
+ +
+ + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
#unzoomrezoomzoom text?
123456
78
+
+ + + +
 
 
+
+ + +
+ + Sorry, your browser seems not to support the HTML 5 canvas element + +
+
+
+ + + diff --git a/src/netdaemon/www/T1 b/src/netdaemon/www/T1 new file mode 100644 index 0000000..8a3a93f --- /dev/null +++ b/src/netdaemon/www/T1 @@ -0,0 +1,14 @@ +17 -10 -2.46 +24 -14 -2.01 +-17 -10 -2.50 +-17 10 -2.44 +0 20 -2.38 +11 7 -2.40 +0 13 -2.36 +-11 7 -2.41 +-11 -7 -2.47 +0 -13 -2.56 +0 -20 -2.56 +0 -27 -2.34 +11 -7 -2.48 +0 -6 -2.52 diff --git a/src/netdaemon/www/T1.html b/src/netdaemon/www/T1.html new file mode 100644 index 0000000..a72fc7b --- /dev/null +++ b/src/netdaemon/www/T1.html @@ -0,0 +1,54820 @@ + + + +Gnuplot Canvas Graph + + + + + + + + + + + + +
+ +
+ + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
#unzoomrezoomzoom text?
123456
7
+
+ + + +
 
 
+
+ + +
+ + Sorry, your browser seems not to support the HTML 5 canvas element + +
+
+
+ + + diff --git a/src/netdaemon/www/index.html b/src/netdaemon/www/index.html new file mode 100644 index 0000000..ee44256 --- /dev/null +++ b/src/netdaemon/www/index.html @@ -0,0 +1,20 @@ + + + +Mirror temperatures main controller + + +

:

+
    +
  • ;
  • +
  • ;
  • + +
+ + +
+\copyright .., eddy@sao.ru +
+ + + \ No newline at end of file diff --git a/src/netdaemon/www/js/README b/src/netdaemon/www/js/README new file mode 100644 index 0000000..fa4c432 --- /dev/null +++ b/src/netdaemon/www/js/README @@ -0,0 +1,108 @@ + Notes on using the gnuplot canvas terminal driver to create web pages + ===================================================================== + + +1) Using UTF-8 characters in your plots + +I expect that eventually web browsers will learn to draw text onto the HTML +canvas element using their native font-handling code. But until then we +have to refer to an external character drawing library. + +The gnuplot package includes two versions of a script to draw characters on +the canvas. The first one, canvastext.js, was written by Jim Studt. It only +knows about the 7-bit ascii characters. The second one, canvasmath.js, is +an expanded version of Jim Studt's script that I wrote to handle UTF8. +It contains glyphs for the first two unicode code pages (latin-1), the greek +alphabet, and select math and physics symbols. You can use this to replace +canvastext.js if you like, or refer to it explicitly in plots that need +non-ascii characters. + + +2) Browser dependencies + +As of this time (May 2009) the HTML canvas element is supported by the +latest versions of Opera, Safari, Firefox, and Konqueror. However, each +of these has quirks. + +For instance, only Firefox makes it easy to click and drag with the middle +or right mouse buttons; the other browsers try to pop up various menus instead. +We try to override this, but it doesn't always work. + +Conversely, Opera and Safari make it easy to use hot keys ('e' for refresh, +'r' to toggle the ruler, etc), but I have not managed to get this to work +in Firefox. + +If you run into problems, please try several browsers before concluding that it +is a gnuplot problem. If you figure out a work-around for one of these browser +quirks, please tell me so that we can try to incorporate into gnuplot output. + + +3) Creating a basic web page with a single mouseable plot + +The canvas terminal driver itself will create a basic html document containing +a mousable plot. The command options to do this are + set term canvas standalone mousing jsdir "http://myserver" + set output 'myplot.html' +This document contains + - a reference to style sheet gnuplot_mouse.css + - a reference to support script gnuplot_mouse.js + - a javascript function named 'gnuplot_canvas' + - a canvas element named 'gnuplot_canvas' that will be drawn in by the + javascript function of the same name + - an html table containing the readout for mouse coordinates, as well + as clickable icons for additional mousing operations +The *.css and *.js references point back to whatever source URL you specified +in the jsdir option to the 'set term' command. For example: + +In order for viewers to use your plot document, they must be able to access the +*.css and *.js files via the URL embedded in the document. + + +4) Creating a web page with multiple mouseable plots + +In order to embed multiple plots in a single document, you must +provide your own html framework. You can use the one created by the canvas +driver in standalone mode as a starting point, including the references to +gnuplot_mouse.css and gnuplot_mouse.js. However, instead of a single +javascript routine named gnuplot_canvas() that always draws the same plot, +you must provide a wrapper routine with the same name that connects the +mousing code to whichever plot is currently active. Here is an example: +- create the individual plots as separate javascript files + set term canvas name 'plot1' + set output 'plot1.js' + plot something + set term canvas name 'plot2' + set output 'plot2.js' + plot something_else +- create your html wrapper, including a script block such as the one below. + You must use these specific variable names, as they are referenced by the + javascript code produced by the canvas terminal. + +- add one or more mousing output tables. As a model, you can use either the one + in a standalone plot or the file .../demo/html/mousebox.template. + The table id and text span ids in the "mousebox" table must match the ones + by your individual plots in order for mousing readout to work. +- each of the individual plots in the document should be explicitly called when + the document is loaded. For example, for each plot there could be a block + of html similar to the following: + + + + Alternatively, the onload attributes could be set in the html element + + + + - Ethan A Merritt (sfeam@users.sourceforge.net) + May 2009 + diff --git a/src/netdaemon/www/js/canvasmath.js b/src/netdaemon/www/js/canvasmath.js new file mode 100644 index 0000000..b50688c --- /dev/null +++ b/src/netdaemon/www/js/canvasmath.js @@ -0,0 +1,413 @@ +/* + * $Id: canvasmath.js,v 1.11 2016/08/07 00:06:31 sfeam Exp $ + */ +// The canvastext.js code was released to the public domain by Jim Studt, 2007. +// He may keep some sort of up to date copy at http://www.federated.com/~jim/canvastext/ +// Mar 2009 Ethan A Merritt (EAM) Modify code to work in non-ascii environments. +// Add Latin-1, Hershey simplex Greek, and math symbols with unicode indexing. +// Apr 2012 Ethan A Merritt Composite widths, tab, newline +// Nov 2012 Ethan A Merritt Additional UTF8 mathematical operators +// +var CanvasTextFunctions = { }; + +CanvasTextFunctions.letters = { + ' ': { width: 16, points: [] }, + '!': { width: 10, points: [[5,21],[5,7],[-1,-1],[5,2],[4,1],[5,0],[6,1],[5,2]] }, + '"': { width: 16, points: [[4,21],[4,14],[-1,-1],[12,21],[12,14]] }, + '#': { width: 21, points: [[11,25],[4,-7],[-1,-1],[17,25],[10,-7],[-1,-1],[4,12],[18,12],[-1,-1],[3,6],[17,6]] }, + '$': { width: 20, points: [[8,25],[8,-4],[-1,-1],[12,25],[12,-4],[-1,-1],[17,18],[15,20],[12,21],[8,21],[5,20],[3,18],[3,16],[4,14],[5,13],[7,12],[13,10],[15,9],[16,8],[17,6],[17,3],[15,1],[12,0],[8,0],[5,1],[3,3]] }, + '%': { width: 24, points: [[21,21],[3,0],[-1,-1],[8,21],[10,19],[10,17],[9,15],[7,14],[5,14],[3,16],[3,18],[4,20],[6,21],[8,21],[10,20],[13,19],[16,19],[19,20],[21,21],[-1,-1],[17,7],[15,6],[14,4],[14,2],[16,0],[18,0],[20,1],[21,3],[21,5],[19,7],[17,7]] }, + '&': { width: 26, points: [[23,12],[23,13],[22,14],[21,14],[20,13],[19,11],[17,6],[15,3],[13,1],[11,0],[7,0],[5,1],[4,2],[3,4],[3,6],[4,8],[5,9],[12,13],[13,14],[14,16],[14,18],[13,20],[11,21],[9,20],[8,18],[8,16],[9,13],[11,10],[16,3],[18,1],[20,0],[22,0],[23,1],[23,2]] }, + '\'': { width: 10, points: [[5,19],[4,20],[5,21],[6,20],[6,18],[5,16],[4,15]] }, + '(': { width: 14, points: [[11,25],[9,23],[7,20],[5,16],[4,11],[4,7],[5,2],[7,-2],[9,-5],[11,-7]] }, + ')': { width: 14, points: [[3,25],[5,23],[7,20],[9,16],[10,11],[10,7],[9,2],[7,-2],[5,-5],[3,-7]] }, + '*': { width: 16, points: [[8,21],[8,9],[-1,-1],[3,18],[13,12],[-1,-1],[13,18],[3,12]] }, + '+': { width: 20, points: [[10,15],[10,3],[-1,-1],[4,9],[16,9]] }, + ',': { width: 10, points: [[6,1],[5,0],[4,1],[5,2],[6,1],[6,-1],[5,-3],[4,-4]] }, + '-': { width: 20, points: [[4,9],[16,9]] }, + '.': { width: 10, points: [[5,2],[4,1],[5,0],[6,1],[5,2]] }, + '/': { width: 20, points: [[19,25],[1,-7]] }, + '0': { width: 20, points: [[9,21],[6,20],[4,17],[3,12],[3,9],[4,4],[6,1],[9,0],[11,0],[14,1],[16,4],[17,9],[17,12],[16,17],[14,20],[11,21],[9,21]] }, + '1': { width: 20, points: [[6,17],[8,18],[11,21],[11,0]] }, + '2': { width: 20, points: [[4,16],[4,17],[5,19],[6,20],[8,21],[12,21],[14,20],[15,19],[16,17],[16,15],[15,13],[13,10],[3,0],[17,0]] }, + '3': { width: 20, points: [[5,21],[16,21],[10,13],[13,13],[15,12],[16,11],[17,8],[17,6],[16,3],[14,1],[11,0],[8,0],[5,1],[4,2],[3,4]] }, + '4': { width: 20, points: [[13,21],[3,7],[18,7],[-1,-1],[13,21],[13,0]] }, + '5': { width: 20, points: [[15,21],[5,21],[4,12],[5,13],[8,14],[11,14],[14,13],[16,11],[17,8],[17,6],[16,3],[14,1],[11,0],[8,0],[5,1],[4,2],[3,4]] }, + '6': { width: 20, points: [[16,18],[15,20],[12,21],[10,21],[7,20],[5,17],[4,12],[4,7],[5,3],[7,1],[10,0],[11,0],[14,1],[16,3],[17,6],[17,7],[16,10],[14,12],[11,13],[10,13],[7,12],[5,10],[4,7]] }, + '7': { width: 20, points: [[17,21],[7,0],[-1,-1],[3,21],[17,21]] }, + '8': { width: 20, points: [[8,21],[5,20],[4,18],[4,16],[5,14],[7,13],[11,12],[14,11],[16,9],[17,7],[17,4],[16,2],[15,1],[12,0],[8,0],[5,1],[4,2],[3,4],[3,7],[4,9],[6,11],[9,12],[13,13],[15,14],[16,16],[16,18],[15,20],[12,21],[8,21]] }, + '9': { width: 20, points: [[16,14],[15,11],[13,9],[10,8],[9,8],[6,9],[4,11],[3,14],[3,15],[4,18],[6,20],[9,21],[10,21],[13,20],[15,18],[16,14],[16,9],[15,4],[13,1],[10,0],[8,0],[5,1],[4,3]] }, + ':': { width: 10, points: [[5,14],[4,13],[5,12],[6,13],[5,14],[-1,-1],[5,2],[4,1],[5,0],[6,1],[5,2]] }, + ';': { width: 10, points: [[5,14],[4,13],[5,12],[6,13],[5,14],[-1,-1],[6,1],[5,0],[4,1],[5,2],[6,1],[6,-1],[5,-3],[4,-4]] }, + '<': { width: 24, points: [[20,18],[4,9],[20,0]] }, + '=': { width: 24, points: [[3,12],[20,12],[-1,-1],[3,6],[20,6]] }, + '>': { width: 24, points: [[4,18],[20,9],[4,0]] }, + '?': { width: 18, points: [[3,16],[3,17],[4,19],[5,20],[7,21],[11,21],[13,20],[14,19],[15,17],[15,15],[14,13],[13,12],[9,10],[9,7],[-1,-1],[9,2],[8,1],[9,0],[10,1],[9,2]] }, + '@': { width: 27, points: [[18,13],[17,15],[15,16],[12,16],[10,15],[9,14],[8,11],[8,8],[9,6],[11,5],[14,5],[16,6],[17,8],[-1,-1],[12,16],[10,14],[9,11],[9,8],[10,6],[11,5],[-1,-1],[18,16],[17,8],[17,6],[19,5],[21,5],[23,7],[24,10],[24,12],[23,15],[22,17],[20,19],[18,20],[15,21],[12,21],[9,20],[7,19],[5,17],[4,15],[3,12],[3,9],[4,6],[5,4],[7,2],[9,1],[12,0],[15,0],[18,1],[20,2],[21,3],[-1,-1],[19,16],[18,8],[18,6],[19,5]] }, + 'A': { width: 18, points: [[9,21],[1,0],[-1,-1],[9,21],[17,0],[-1,-1],[4,7],[14,7]] }, + 'B': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,15],[17,13],[16,12],[13,11],[-1,-1],[4,11],[13,11],[16,10],[17,9],[18,7],[18,4],[17,2],[16,1],[13,0],[4,0]] }, + 'C': { width: 21, points: [[18,16],[17,18],[15,20],[13,21],[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5]] }, + 'D': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[11,21],[14,20],[16,18],[17,16],[18,13],[18,8],[17,5],[16,3],[14,1],[11,0],[4,0]] }, + 'E': { width: 19, points: [[4,21],[4,0],[-1,-1],[4,21],[17,21],[-1,-1],[4,11],[12,11],[-1,-1],[4,0],[17,0]] }, + 'F': { width: 18, points: [[4,21],[4,0],[-1,-1],[4,21],[17,21],[-1,-1],[4,11],[12,11]] }, + 'G': { width: 21, points: [[18,16],[17,18],[15,20],[13,21],[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[18,8],[-1,-1],[13,8],[18,8]] }, + 'H': { width: 22, points: [[4,21],[4,0],[-1,-1],[18,21],[18,0],[-1,-1],[4,11],[18,11]] }, + 'I': { width: 8, points: [[4,21],[4,0]] }, + 'J': { width: 16, points: [[12,21],[12,5],[11,2],[10,1],[8,0],[6,0],[4,1],[3,2],[2,5],[2,7]] }, + 'K': { width: 21, points: [[4,21],[4,0],[-1,-1],[18,21],[4,7],[-1,-1],[9,12],[18,0]] }, + 'L': { width: 17, points: [[4,21],[4,0],[-1,-1],[4,0],[16,0]] }, + 'M': { width: 24, points: [[4,21],[4,0],[-1,-1],[4,21],[12,0],[-1,-1],[20,21],[12,0],[-1,-1],[20,21],[20,0]] }, + 'N': { width: 22, points: [[4,21],[4,0],[-1,-1],[4,21],[18,0],[-1,-1],[18,21],[18,0]] }, + 'O': { width: 22, points: [[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[19,8],[19,13],[18,16],[17,18],[15,20],[13,21],[9,21]] }, + 'P': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,14],[17,12],[16,11],[13,10],[4,10]] }, + 'Q': { width: 22, points: [[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[19,8],[19,13],[18,16],[17,18],[15,20],[13,21],[9,21],[-1,-1],[12,4],[18,-2]] }, + 'R': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,15],[17,13],[16,12],[13,11],[4,11],[-1,-1],[11,11],[18,0]] }, + 'S': { width: 20, points: [[17,18],[15,20],[12,21],[8,21],[5,20],[3,18],[3,16],[4,14],[5,13],[7,12],[13,10],[15,9],[16,8],[17,6],[17,3],[15,1],[12,0],[8,0],[5,1],[3,3]] }, + 'T': { width: 16, points: [[8,21],[8,0],[-1,-1],[1,21],[15,21]] }, + 'U': { width: 22, points: [[4,21],[4,6],[5,3],[7,1],[10,0],[12,0],[15,1],[17,3],[18,6],[18,21]] }, + 'V': { width: 18, points: [[1,21],[9,0],[-1,-1],[17,21],[9,0]] }, + 'W': { width: 24, points: [[2,21],[7,0],[-1,-1],[12,21],[7,0],[-1,-1],[12,21],[17,0],[-1,-1],[22,21],[17,0]] }, + 'X': { width: 20, points: [[3,21],[17,0],[-1,-1],[17,21],[3,0]] }, + 'Y': { width: 18, points: [[1,21],[9,11],[9,0],[-1,-1],[17,21],[9,11]] }, + 'Z': { width: 20, points: [[17,21],[3,0],[-1,-1],[3,21],[17,21],[-1,-1],[3,0],[17,0]] }, + '[': { width: 14, points: [[4,25],[4,-7],[-1,-1],[5,25],[5,-7],[-1,-1],[4,25],[11,25],[-1,-1],[4,-7],[11,-7]] }, + '\\': { width: 14, points: [[0,21],[14,-3]] }, + ']': { width: 14, points: [[9,25],[9,-7],[-1,-1],[10,25],[10,-7],[-1,-1],[3,25],[10,25],[-1,-1],[3,-7],[10,-7]] }, + '^': { width: 16, points: [[5,15],[8,19],[11,15],[-1,-1],[2,12],[8,18],[14,12]] }, + '_': { width: 16, points: [[0,-2],[16,-2]] }, + '`': { width: 10, points: [[6,21],[5,20],[4,18],[4,16],[5,15],[6,16],[5,17]] }, + 'a': { width: 19, points: [[15,14],[15,0],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, + 'b': { width: 19, points: [[4,21],[4,0],[-1,-1],[4,11],[6,13],[8,14],[11,14],[13,13],[15,11],[16,8],[16,6],[15,3],[13,1],[11,0],[8,0],[6,1],[4,3]] }, + 'c': { width: 18, points: [[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, + 'd': { width: 19, points: [[15,21],[15,0],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, + 'e': { width: 18, points: [[3,8],[15,8],[15,10],[14,12],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, + 'f': { width: 12, points: [[10,21],[8,21],[6,20],[5,17],[5,0],[-1,-1],[2,14],[9,14]] }, + 'g': { width: 19, points: [[15,14],[15,-2],[14,-5],[13,-6],[11,-7],[8,-7],[6,-6],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, + 'h': { width: 19, points: [[4,21],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0]] }, + 'i': { width: 8, points: [[3,21],[4,20],[5,21],[4,22],[3,21],[-1,-1],[4,14],[4,0]] }, + 'j': { width: 10, points: [[5,21],[6,20],[7,21],[6,22],[5,21],[-1,-1],[6,14],[6,-3],[5,-6],[3,-7],[1,-7]] }, + 'k': { width: 17, points: [[4,21],[4,0],[-1,-1],[14,14],[4,4],[-1,-1],[8,8],[15,0]] }, + 'l': { width: 8, points: [[4,21],[4,0]] }, + 'm': { width: 30, points: [[4,14],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0],[-1,-1],[15,10],[18,13],[20,14],[23,14],[25,13],[26,10],[26,0]] }, + 'n': { width: 19, points: [[4,14],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0]] }, + 'o': { width: 19, points: [[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3],[16,6],[16,8],[15,11],[13,13],[11,14],[8,14]] }, + 'p': { width: 19, points: [[4,14],[4,-7],[-1,-1],[4,11],[6,13],[8,14],[11,14],[13,13],[15,11],[16,8],[16,6],[15,3],[13,1],[11,0],[8,0],[6,1],[4,3]] }, + 'q': { width: 19, points: [[15,14],[15,-7],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, + 'r': { width: 13, points: [[4,14],[4,0],[-1,-1],[4,8],[5,11],[7,13],[9,14],[12,14]] }, + 's': { width: 17, points: [[14,11],[13,13],[10,14],[7,14],[4,13],[3,11],[4,9],[6,8],[11,7],[13,6],[14,4],[14,3],[13,1],[10,0],[7,0],[4,1],[3,3]] }, + 't': { width: 12, points: [[5,21],[5,4],[6,1],[8,0],[10,0],[-1,-1],[2,14],[9,14]] }, + 'u': { width: 19, points: [[4,14],[4,4],[5,1],[7,0],[10,0],[12,1],[15,4],[-1,-1],[15,14],[15,0]] }, + 'v': { width: 16, points: [[2,14],[8,0],[-1,-1],[14,14],[8,0]] }, + 'w': { width: 22, points: [[3,14],[7,0],[-1,-1],[11,14],[7,0],[-1,-1],[11,14],[15,0],[-1,-1],[19,14],[15,0]] }, + 'x': { width: 17, points: [[3,14],[14,0],[-1,-1],[14,14],[3,0]] }, + 'y': { width: 16, points: [[2,14],[8,0],[-1,-1],[14,14],[8,0],[6,-4],[4,-6],[2,-7],[1,-7]] }, + 'z': { width: 17, points: [[14,14],[3,0],[-1,-1],[3,14],[14,14],[-1,-1],[3,0],[14,0]] }, + '{': { width: 14, points: [[9,25],[7,24],[6,23],[5,21],[5,19],[6,17],[7,16],[8,14],[8,12],[6,10],[-1,-1],[7,24],[6,22],[6,20],[7,18],[8,17],[9,15],[9,13],[8,11],[4,9],[8,7],[9,5],[9,3],[8,1],[7,0],[6,-2],[6,-4],[7,-6],[-1,-1],[6,8],[8,6],[8,4],[7,2],[6,1],[5,-1],[5,-3],[6,-5],[7,-6],[9,-7]] }, + '|': { width: 8, points: [[4,25],[4,-7]] }, + '}': { width: 14, points: [[5,25],[7,24],[8,23],[9,21],[9,19],[8,17],[7,16],[6,14],[6,12],[8,10],[-1,-1],[7,24],[8,22],[8,20],[7,18],[6,17],[5,15],[5,13],[6,11],[10,9],[6,7],[5,5],[5,3],[6,1],[7,0],[8,-2],[8,-4],[7,-6],[-1,-1],[8,8],[6,6],[6,4],[7,2],[8,1],[9,-1],[9,-3],[8,-5],[7,-6],[5,-7]] }, + '~': { width: 24, points: [[3,6],[3,8],[4,11],[6,12],[8,12],[10,11],[14,8],[16,7],[18,7],[20,8],[21,10],[-1,-1],[3,8],[4,10],[6,11],[8,11],[10,10],[14,7],[16,6],[18,6],[20,7],[21,10],[21,12]] }, + +// latin-1 as diacritical + base character + +192: { width: 0, points: [[4,28],[10,24],[-99,'A']] }, +193: { width: 0, points: [[8,24],[14,28],[-99,'A']] }, +194: { width: 0, points: [[5,24],[9,27],[13,24],[-99,'A']] }, +195: { width: 0, points: [[4,24],[5,26],[6,27],[8,27], [10,24],[12,24],[13,25],[14,27],[-99,'A']] }, +196: { width: 0, points: [[6,25],[6,26],[7,26],[7,25],[-1,-1],[12,25],[12,26],[13,26],[13,25],[-99,'A']] }, +197: { width: 0, points: [[8,27],[6,25],[6,23],[8,21],[10,21],[12,23],[12,25],[10,27],[8,27],[-99,'A']]}, +198: { width: 11, points: [[3,0],[14,21],[-1,-1],[7,8],[14,8],[-99,'E']] }, +199: { width: 0, points: [[10,0],[10,-3],[11,-3],[12,-4],[12,-6],[11,-7],[8,-7],[-99,'C']] }, +200: { width: 0, points: [[8,28],[14,24],[-99,'E']] }, +201: { width: 0, points: [[6,24],[12,28],[-99,'E']] }, +202: { width: 0, points: [[5,24],[9,27],[13,24],[-99,'E']] }, +203: { width: 0, points: [[6,25],[6,26],[7,26],[7,25],[-1,-1],[12,25],[12,26],[13,26],[13,25],[-99,'E']] }, +204: { width: 0, points: [[-99,'I']] }, +205: { width: 0, points: [[-99,'I']] }, +206: { width: 0, points: [[-99,'I']] }, +207: { width: 0, points: [[-99,'I']] }, +209: { width: 0, points: [[6,24],[7,26],[8,27],[10,27], [12,24],[14,24],[15,25],[16,27],[-99,'N']] }, +210: { width: 0, points: [[8,28],[14,24],[-99,'O']] }, +211: { width: 0, points: [[6,24],[12,28],[-99,'O']] }, +212: { width: 0, points: [[7,24],[11,27],[15,24],[-99,'O']] }, +213: { width: 0, points: [[6,24],[7,26],[8,27],[10,27], [12,24],[14,24],[15,25],[16,27],[-99,'O']] }, +214: { width: 0, points: [[8,25],[8,26],[9,26],[9,25],[-1,-1],[14,25],[14,26],[15,26],[15,25],[-99,'O']] }, +216: { width: 0, points: [[3,0],[19,21],[-99,'O']] }, +217: { width: 0, points: [[8,28],[14,24],[-99,'U']] }, +218: { width: 0, points: [[6,24],[12,28],[-99,'U']] }, +219: { width: 0, points: [[7,24],[11,27],[15,24],[-99,'U']] }, +220: { width: 0, points: [[8,25],[8,26],[9,26],[9,25],[-1,-1],[14,25],[14,26],[15,26],[15,25],[-99,'U']] }, +224: { width: 0, points: [[8,21],[14,17],[-99,'a']] }, +225: { width: 0, points: [[6,17],[12,21],[-99,'a']] }, +226: { width: 0, points: [[5,17],[9,20],[13,17],[-99,'a']] }, +227: { width: 0, points: [[4,17],[5,19],[6,20],[8,20], [10,17],[12,17],[13,18],[14,20],[-99,'a']] }, +228: { width: 0, points: [[6,18],[6,19],[7,19],[7,18],[-1,-1],[12,18],[12,19],[13,19],[13,18],[-99,'a']] }, +229: { width: 0, points: [[9,21],[7,19],[7,18],[9,16],[10,16],[12,18],[12,19],[10,21],[8,21],[-99,'a']] }, +231: { width: 0, points: [[10,0],[10,-3],[11,-3],[12,-4],[12,-6],[11,-7],[8,-7],[-99,'c']] }, +232: { width: 0, points: [[8,21],[14,17],[-99,'e']] }, +233: { width: 0, points: [[6,17],[12,21],[-99,'e']] }, +234: { width: 0, points: [[5,17],[9,20],[13,17],[-99,'e']] }, +235: { width: 0, points: [[6,18],[6,19],[7,19],[7,18],[-1,-1],[12,18],[12,19],[13,19],[13,18],[-99,'e']] }, +236: { width: 8, points: [[4,14],[4,0],[-1,-1],[1,21],[4,18]] }, +237: { width: 8, points: [[4,14],[4,0],[-1,-1],[7,21],[4,18]] }, +238: { width: 8, points: [[4,14],[4,0],[-1,-1],[1,17],[4,20],[7,17]] }, +239: { width: 10, points: [[5,14],[5,0],[-1,-1],[2,18],[2,19],[3,19],[3,18],[-1,-1],[7,18],[7,19],[8,19],[8,18]] }, +241: { width: 0, points: [[5,17],[6,19],[7,20],[9,20], [11,17],[13,17],[14,18],[15,20],[-99,'n']] }, +242: { width: 0, points: [[8,21],[14,17],[-99,'o']] }, +243: { width: 0, points: [[6,17],[12,21],[-99,'o']] }, +244: { width: 0, points: [[5,17],[9,20],[13,17],[-99,'o']] }, +245: { width: 0, points: [[4,17],[5,19],[6,20],[8,20], [10,17],[12,17],[13,18],[14,20],[-99,'o']] }, +246: { width: 0, points: [[6,18],[6,19],[7,19],[7,18],[-1,-1],[12,18],[12,19],[13,19],[13,18],[-99,'o']] }, +247: { width: 20, points: [[4,9],[16,9],[-1,-1],[10,14],[10,13],[11,13],[11,14],[-1,-1],[10,4],[10,5],[11,5],[11,4]] }, +248: { width: 0, points: [[3,0],[17,14],[-99,'o']] }, +249: { width: 0, points: [[8,21],[14,17],[-99,'u']] }, +250: { width: 0, points: [[6,17],[12,21],[-99,'u']] }, +251: { width: 0, points: [[5,17],[9,20],[13,17],[-99,'u']] }, +252: { width: 0, points: [[6,18],[6,19],[7,19],[7,18],[-1,-1],[12,18],[12,19],[13,19],[13,18],[-99,'u']] }, +253: { width: 0, points: [[12,21],[6,17],[-99,'y']] }, +255: { width: 0, points: [[6,18],[6,19],[7,19],[7,18],[-1,-1],[12,18],[12,19],[13,19],[13,18],[-99,'y']] }, + +// Hershey simplex greek font + +913: { width: 18, points: [[9,22],[1,1],[-1,-1],[9,22],[17,1],[-1,-1],[4,8],[14,8]] }, +914: { width: 21, points: [[4,22],[4,1],[-1,-1],[4,22],[13,22],[16,21],[17,20],[18,18],[18,16],[17,14],[16,13],[13,12],[-1,-1],[4,12],[13,12],[16,11],[17,10],[18,8],[18,5],[17,3],[16,2],[13,1],[4,1]] }, +915: { width: 17, points: [[4,22],[4,1],[-1,-1],[4,22],[16,22]] }, +916: { width: 18, points: [[9,22],[1,1],[-1,-1],[9,22],[17,1],[-1,-1],[1,1],[17,1]] }, +917: { width: 19, points: [[4,22],[4,1],[-1,-1],[4,22],[17,22],[-1,-1],[4,12],[12,12],[-1,-1],[4,1],[17,1]] }, +918: { width: 20, points: [[17,22],[3,1],[-1,-1],[3,22],[17,22],[-1,-1],[3,1],[17,1]] }, +919: { width: 22, points: [[4,22],[4,1],[-1,-1],[18,22],[18,1],[-1,-1],[4,12],[18,12]] }, +920: { width: 22, points: [[9,22],[7,21],[5,19],[4,17],[3,14],[3,9],[4,6],[5,4],[7,2],[9,1],[13,1],[15,2],[17,4],[18,6],[19,9],[19,14],[18,17],[17,19],[15,21],[13,22],[9,22],[-1,-1],[8,12],[14,12]] }, +921: { width: 8, points: [[4,22],[4,1]] }, +922: { width: 21, points: [[4,22],[4,1],[-1,-1],[18,22],[4,8],[-1,-1],[9,13],[18,1]] }, +923: { width: 18, points: [[9,22],[1,1],[-1,-1],[9,22],[17,1]] }, +924: { width: 24, points: [[4,22],[4,1],[-1,-1],[4,22],[12,1],[-1,-1],[20,22],[12,1],[-1,-1],[20,22],[20,1]] }, +925: { width: 22, points: [[4,22],[4,1],[-1,-1],[4,22],[18,1],[-1,-1],[18,22],[18,1]] }, +926: { width: 18, points: [[2,22],[16,22],[-1,-1],[6,12],[12,12],[-1,-1],[2,1],[16,1]] }, +927: { width: 22, points: [[9,22],[7,21],[5,19],[4,17],[3,14],[3,9],[4,6],[5,4],[7,2],[9,1],[13,1],[15,2],[17,4],[18,6],[19,9],[19,14],[18,17],[17,19],[15,21],[13,22],[9,22]] }, +928: { width: 22, points: [[4,22],[4,1],[-1,-1],[18,22],[18,1],[-1,-1],[4,22],[18,22]] }, +929: { width: 21, points: [[4,22],[4,1],[-1,-1],[4,22],[13,22],[16,21],[17,20],[18,18],[18,15],[17,13],[16,12],[13,11],[4,11]] }, +931: { width: 18, points: [[2,22],[9,12],[2,1],[-1,-1],[2,22],[16,22],[-1,-1],[2,1],[16,1]] }, +932: { width: 16, points: [[8,22],[8,1],[-1,-1],[1,22],[15,22]] }, +933: { width: 18, points: [[2,17],[2,19],[3,21],[4,22],[6,22],[7,21],[8,19],[9,15],[9,1],[-1,-1],[16,17],[16,19],[15,21],[14,22],[12,22],[11,21],[10,19],[9,15]] }, +934: { width: 20, points: [[10,22],[10,1],[-1,-1],[8,17],[5,16],[4,15],[3,13],[3,10],[4,8],[5,7],[8,6],[12,6],[15,7],[16,8],[17,10],[17,13],[16,15],[15,16],[12,17],[8,17]] }, +935: { width: 20, points: [[3,22],[17,1],[-1,-1],[3,1],[17,22]] }, +936: { width: 22, points: [[11,22],[11,1],[-1,-1],[2,16],[3,16],[4,15],[5,11],[6,9],[7,8],[10,7],[12,7],[15,8],[16,9],[17,11],[18,15],[19,16],[20,16]] }, +937: { width: 20, points: [[3,1],[7,1],[4,8],[3,12],[3,16],[4,19],[6,21],[9,22],[11,22],[14,21],[16,19],[17,16],[17,12],[16,8],[13,1],[17,1]] }, +945: { width: 21, points: [[9,15],[7,14],[5,12],[4,10],[3,7],[3,4],[4,2],[6,1],[8,1],[10,2],[13,5],[15,8],[17,12],[18,15],[-1,-1],[9,15],[11,15],[12,14],[13,12],[15,4],[16,2],[17,1],[18,1]] }, +946: { width: 19, points: [[12,22],[10,21],[8,19],[6,15],[5,12],[4,8],[3,2],[2,-6],[-1,-1],[12,22],[14,22],[16,20],[16,17],[15,15],[14,14],[12,13],[9,13],[-1,-1],[9,13],[11,12],[13,10],[14,8],[14,5],[13,3],[12,2],[10,1],[8,1],[6,2],[5,3],[4,6]] }, +947: { width: 19, points: [[1,12],[3,14],[5,15],[6,15],[8,14],[9,13],[10,10],[10,6],[9,1],[-1,-1],[17,15],[16,12],[15,10],[9,1],[7,-3],[6,-6]] }, +948: { width: 18, points: [[11,15],[8,15],[6,14],[4,12],[3,9],[3,6],[4,3],[5,2],[7,1],[9,1],[11,2],[13,4],[14,7],[14,10],[13,13],[11,15],[9,17],[8,19],[8,21],[9,22],[11,22],[13,21],[15,19]] }, +949: { width: 16, points: [[13,13],[12,14],[10,15],[7,15],[5,14],[5,12],[6,10],[9,9],[-1,-1],[9,9],[5,8],[3,6],[3,4],[4,2],[6,1],[9,1],[11,2],[13,4]] }, +950: { width: 15, points: [[10,22],[8,21],[7,20],[7,19],[8,18],[11,17],[14,17],[-1,-1],[14,17],[10,15],[7,13],[4,10],[3,7],[3,5],[4,3],[6,1],[9,-1],[10,-3],[10,-5],[9,-6],[7,-6],[6,-4]] }, +951: { width: 20, points: [[1,11],[2,13],[4,15],[6,15],[7,14],[7,12],[6,8],[4,1],[-1,-1],[6,8],[8,12],[10,14],[12,15],[14,15],[16,13],[16,10],[15,5],[12,-6]] }, +952: { width: 17, points: [[10,22],[8,21],[6,18],[5,16],[4,13],[3,8],[3,4],[4,2],[5,1],[7,1],[9,2],[11,5],[12,7],[13,10],[14,15],[14,19],[13,21],[12,22],[10,22],[-1,-1],[4,12],[13,12]] }, +953: { width: 11, points: [[6,15],[4,8],[3,4],[3,2],[4,1],[6,1],[8,3],[9,5]] }, +954: { width: 18, points: [[6,15],[2,1],[-1,-1],[16,14],[15,15],[14,15],[12,14],[8,10],[6,9],[5,9],[-1,-1],[5,9],[7,8],[8,7],[10,2],[11,1],[12,1],[13,2]] }, +955: { width: 16, points: [[1,22],[3,22],[5,21],[6,20],[14,1],[-1,-1],[8,15],[2,1]] }, +956: { width: 21, points: [[7,15],[1,-6],[-1,-1],[6,11],[5,6],[5,3],[7,1],[9,1],[11,2],[13,4],[15,8],[-1,-1],[17,15],[15,8],[14,4],[14,2],[15,1],[17,1],[19,3],[20,5]] }, +957: { width: 18, points: [[3,15],[6,15],[5,9],[4,4],[3,1],[-1,-1],[16,15],[15,12],[14,10],[12,7],[9,4],[6,2],[3,1]] }, +958: { width: 16, points: [[10,22],[8,21],[7,20],[7,19],[8,18],[11,17],[14,17],[-1,-1],[11,17],[8,16],[6,15],[5,13],[5,11],[7,9],[10,8],[12,8],[-1,-1],[10,8],[6,7],[4,6],[3,4],[3,2],[5,0],[9,-2],[10,-3],[10,-5],[8,-6],[6,-6]] }, +959: { width: 17, points: [[8,15],[6,14],[4,12],[3,9],[3,6],[4,3],[5,2],[7,1],[9,1],[11,2],[13,4],[14,7],[14,10],[13,13],[12,14],[10,15],[8,15]] }, +960: { width: 18, points: [[6,15],[2,1],[-1,-1],[16,14],[15,15],[14,15],[12,14],[8,10],[6,9],[5,9],[-1,-1],[5,9],[7,8],[8,7],[10,2],[11,1],[12,1],[13,2]] }, +960: { width: 22, points: [[9,15],[5,1],[-1,-1],[14,15],[15,9],[16,4],[17,1],[-1,-1],[2,12],[4,14],[7,15],[20,15]] }, +961: { width: 18, points: [[4,9],[4,6],[5,3],[6,2],[8,1],[10,1],[12,2],[14,4],[15,7],[15,10],[14,13],[13,14],[11,15],[9,15],[7,14],[5,12],[4,9],[0,-6]] }, +962: { width: 18, points: [[15,13],[14,14],[11,15],[8,15],[5,14],[4,13],[3,11],[3,9],[4,7],[6,5],[10,2],[11,0],[11,-2],[10,-3],[8,-3]] }, +963: { width: 18, points: [[3,15],[6,15],[5,9],[4,4],[3,1],[-1,-1],[16,15],[15,12],[14,10],[12,7],[9,4],[6,2],[3,1]] }, +963: { width: 20, points: [[18,15],[8,15],[6,14],[4,12],[3,9],[3,6],[4,3],[5,2],[7,1],[9,1],[11,2],[13,4],[14,7],[14,10],[13,13],[12,14],[10,15]] }, +964: { width: 20, points: [[11,15],[8,1],[-1,-1],[2,12],[4,14],[7,15],[18,15]] }, +965: { width: 20, points: [[1,11],[2,13],[4,15],[6,15],[7,14],[7,12],[5,6],[5,3],[7,1],[9,1],[12,2],[14,4],[16,8],[17,12],[17,15]] }, +966: { width: 22, points: [[8,14],[6,13],[4,11],[3,8],[3,5],[4,3],[5,2],[7,1],[10,1],[13,2],[16,4],[18,7],[19,10],[19,13],[17,15],[15,15],[13,13],[11,9],[9,4],[6,-6]] }, +967: { width: 18, points: [[2,15],[4,15],[6,13],[12,-4],[14,-6],[16,-6],[-1,-1],[17,15],[16,13],[14,10],[4,-1],[2,-4],[1,-6]] }, +968: { width: 23, points: [[16,22],[8,-6],[-1,-1],[1,11],[2,13],[4,15],[6,15],[7,14],[7,12],[6,7],[6,4],[7,2],[9,1],[11,1],[14,2],[16,4],[18,7],[20,12],[21,15]] }, +969: { width: 23, points: [[8,15],[6,14],[4,11],[3,8],[3,5],[4,2],[5,1],[7,1],[9,2],[11,5],[-1,-1],[12,9],[11,5],[12,2],[13,1],[15,1],[17,2],[19,5],[20,8],[20,11],[19,14],[18,15]] }, +977: { width: 21, points: [[1,11],[2,13],[4,15],[6,15],[7,14],[7,12],[6,7],[6,4],[7,2],[8,1],[10,1],[12,2],[14,5],[15,7],[16,10],[17,15],[17,18],[16,21],[14,22],[12,22],[11,20],[11,18],[12,15],[14,12],[16,10],[19,8]] }, +981: { width: 20, points: [[14,22],[6,-6],[-1,-1],[9,15],[6,14],[4,12],[3,9],[3,6],[4,4],[6,2],[9,1],[11,1],[14,2],[16,4],[17,7],[17,10],[16,12],[14,14],[11,15],[9,15]] }, +982: { width: 23, points: [[9,14],[6,13],[4,11],[3,8],[3,5],[4,2],[5,1],[7,1],[9,2],[11,5],[-1,-1],[12,9],[11,5],[12,2],[13,1],[15,1],[17,2],[19,5],[20,8],[20,11],[19,14],[18,15],[-1,-1],[2,14],[4,15],[20,15]] }, +986: { width: 18, points: [[15,13],[14,14],[11,15],[8,15],[5,14],[4,13],[3,11],[3,9],[4,7],[6,5],[10,2],[11,0],[11,-2],[10,-3],[8,-3]] }, + +// Hershey math symbols + +8501: { width: 20, points: [[3,19],[4,17],[16,5],[17,3],[17,1],[-1,-1],[4,16],[16,4],[-1,-1],[3,19],[3,17],[4,15],[16,3],[17,1],[-1,-1],[8,12],[4,8],[3,6],[3,4],[4,2],[3,1],[-1,-1],[3,6],[5,2],[-1,-1],[4,8],[4,6],[5,4],[5,2],[3,1],[-1,-1],[11,9],[16,14],[-1,-1],[14,19],[14,16],[15,14],[17,14],[17,16],[15,17],[14,19],[-1,-1],[14,19],[15,16],[17,14]] }, +8592: { width: 26, points: [[7,12],[4,10],[7,8],[-1,-1],[10,15],[5,10],[10,5],[-1,-1],[5,10],[22,10]] }, +8593: { width: 16, points: [[6,16],[8,19],[10,16],[-1,-1],[3,13],[8,18],[13,13],[-1,-1],[8,18],[8,1]] }, +8594: { width: 26, points: [[19,12],[22,10],[19,8],[-1,-1],[16,15],[21,10],[16,5],[-1,-1],[4,10],[21,10]] }, +8595: { width: 16, points: [[6,4],[8,1],[10,4],[-1,-1],[3,7],[8,2],[13,7],[-1,-1],[8,19],[8,2]] }, +8706: { width: 19, points: [[15,10],[14,13],[13,14],[11,15],[9,15],[6,14],[4,11],[3,8],[3,5],[4,3],[5,2],[7,1],[9,1],[12,2],[14,4],[15,7],[16,12],[16,17],[15,20],[14,21],[12,22],[9,22],[7,21],[6,20],[6,19],[7,19],[7,20],[-1,-1],[9,15],[7,14],[5,11],[4,8],[4,4],[5,2],[-1,-1],[9,1],[11,2],[13,4],[14,7],[15,12],[15,17],[14,20],[12,22]] }, +8710: { width: 20, points: [[2,1],[10,22],[-1,-1],[17,1],[10,20],[-1,-1],[18,1],[10,22],[-1,-1],[2,1],[18,1],[-1,-1],[3,2],[17,2]] }, +8711: { width: 20, points: [[2,22],[10,1],[-1,-1],[3,22],[10,3],[-1,-1],[18,22],[10,1],[-1,-1],[2,22],[18,22],[-1,-1],[3,21],[17,21]] }, +8712: { width: 24, points: [[20,18],[13,18],[9,17],[7,16],[5,14],[4,11],[4,9],[5,6],[7,4],[9,3],[13,2],[20,2],[-1,-1],[4,10],[16,10]] }, +8713: { width: 24, points: [[20,18],[13,18],[9,17],[7,16],[5,14],[4,11],[4,9],[5,6],[7,4],[9,3],[13,2],[20,2],[-1,-1],[4,10],[16,10],[-1,-1],[18,19],[6,1]] }, +8719: { width: 34, points: [[7,26],[7,-6],[-1,-1],[8,26],[8,-6],[-1,-1],[26,26],[26,-6],[-1,-1],[27,26],[27,-6],[-1,-1],[3,26],[31,26],[-1,-1],[3,-6],[12,-6],[-1,-1],[22,-6],[31,-6]] }, +8721: { width: 31, points: [[5,26],[15,12],[4,-6],[-1,-1],[4,26],[14,12],[-1,-1],[3,26],[14,11],[-1,-1],[3,26],[26,26],[28,19],[25,26],[-1,-1],[5,-5],[26,-5],[-1,-1],[4,-6],[26,-6],[28,1],[25,-6]] }, +8728: { width: 16, points: [[7,13],[5,11],[5,9],[7,7],[9,7],[11,9],[11,11],[9,13],[7,13]] }, +8729: { width: 16, points: [[7,13],[5,11],[5,9],[7,7],[9,7],[11,9],[11,11],[9,13],[7,13],[-1,-1],[7,12],[6,11],[6,9],[7,8],[9,8],[10,9],[10,11],[9,12],[7,12],[-1,-1],[8,11],[7,10],[8,9],[9,10],[8,11]] }, +8730: { width: 22, points: [[3,15],[7,15],[13,3],[-1,-1],[6,15],[13,1],[-1,-1],[22,26],[13,1]] }, +8733: { width: 25, points: [[21,5],[19,5],[17,6],[15,8],[12,12],[11,13],[9,14],[7,14],[5,13],[4,11],[4,9],[5,7],[7,6],[9,6],[11,7],[12,8],[15,12],[17,14],[19,15],[21,15]] }, +8734: { width: 25, points: [[22,9],[21,7],[19,6],[17,6],[15,7],[14,8],[11,12],[10,13],[8,14],[6,14],[4,13],[3,11],[3,9],[4,7],[6,6],[8,6],[10,7],[11,8],[14,12],[15,13],[17,14],[19,14],[21,13],[22,11],[22,9]] }, +8745: { width: 24, points: [[4,2],[4,9],[5,13],[6,15],[8,17],[11,18],[13,18],[16,17],[18,15],[19,13],[20,9],[20,2]] }, +8746: { width: 24, points: [[4,18],[4,11],[5,7],[6,5],[8,3],[11,2],[13,2],[16,3],[18,5],[19,7],[20,11],[20,18]] }, +8747: { width: 24, points: [[21,25],[20,24],[21,23],[22,24],[22,25],[21,26],[19,26],[17,25],[15,23],[14,21],[13,18],[12,14],[10,2],[9,-2],[8,-4],[-1,-1],[16,24],[15,22],[14,18],[12,6],[11,2],[10,-1],[9,-3],[7,-5],[5,-6],[3,-6],[2,-5],[2,-4],[3,-3],[4,-4],[3,-5]] }, +8771: { width: 24, points: [[3,10],[3,11],[4,13],[6,14],[8,14],[10,13],[14,11],[16,10],[18,10],[20,11],[22,13],[22,14],[-1,-1],[3,6],[22,6]] }, +8776: { width: 24, points: [[3,10],[3,11],[4,13],[6,14],[8,14],[10,13],[14,11],[16,10],[18,10],[20,11],[22,13],[22,14],[-1,-1],[3,4],[3,5],[4,7],[6,8],[8,8],[10,7],[14,5],[16,4],[18,4],[20,5],[22,7],[22,8]] }, +8800: { width: 24, points: [[18,19],[6,1],[-1,-1],[3,12],[20,12],[-1,-1],[3,6],[20,6]] }, +8804: { width: 24, points: [[20,4],[4,11],[20,18],[-1,-1],[4,0],[20,0]] }, +8805: { width: 24, points: [[4,4],[20,11],[4,18],[-1,-1],[4,0],[20,0]] }, +8834: { width: 24, points: [[20,18],[13,18],[9,17],[7,16],[5,14],[4,11],[4,9],[5,6],[7,4],[9,3],[13,2],[20,2]] }, +8835: { width: 24, points: [[4,18],[11,18],[15,17],[17,16],[19,14],[20,11],[20,9],[19,6],[17,4],[15,3],[11,2],[4,2]] }, +8838: { width: 24, points: [[20,20],[13,20],[9,19],[7,18],[5,16],[4,13],[4,11],[5,8],[7,6],[9,5],[13,4],[20,4],[-1,-1],[4,0],[20,0]] }, +8839: { width: 24, points: [[4,20],[11,20],[15,19],[17,18],[19,16],[20,13],[20,11],[19,8],[17,6],[15,5],[11,4],[4,4],[-1,-1],[4,0],[20,0]] }, +8853: { width: 20, points: [[9,17],[6,16],[4,14],[3,11],[3,9],[4,6],[6,4],[9,3],[11,3],[14,4],[16,6],[17,9],[17,11],[16,14],[14,16],[11,17],[9,17],[-1,-1],[5,10],[15,10],[-1,-1],[10,5],[10,15]] }, +8855: { width: 20, points: [[9,17],[6,16],[4,14],[3,11],[3,9],[4,6],[6,4],[9,3],[11,3],[14,4],[16,6],[17,9],[17,11],[16,14],[14,16],[11,17],[9,17],[-1,-1],[6,6],[14,14],[-1,-1],[14,6],[6,14]] }, +8857: { width: 20, points: [[9,17],[6,16],[4,14],[3,11],[3,9],[4,6],[6,4],[9,3],[11,3],[14,4],[16,6],[17,9],[17,11],[16,14],[14,16],[11,17],[9,17],[-1,-1],[10,11],[9,10],[10,9],[11,10],[10,11]] }, +9737: { width: 27, points: [[12,22],[9,21],[6,19],[4,16],[3,13],[3,10],[4,7],[6,4],[9,2],[12,1],[15,1],[18,2],[21,4],[23,7],[24,10],[24,13],[23,16],[21,19],[18,21],[15,22],[12,22],[-1,-1],[13,13],[12,12],[12,11],[13,10],[14,10],[15,11],[15,12],[14,13],[13,13],[-1,-1],[13,12],[13,11],[14,11],[14,12],[13,12]] }, + +// Miscellaneous glyphs (EAM) + +176: { width: 14, points: [[6,22],[4,21],[3,19],[3,17],[4,15],[6,14],[8,14],[10,15],[11,17],[11,19],[10,21],[8,22],[6,22]] }, +177: { width: 20, points: [[10,15],[10,5],[-1,-1],[4,10],[16,10],[-1,-1],[4,2],[16,2]] }, +181: { width: 0, points: [[-99,956]] }, +183: { width: 10, points: [[5,11],[4,10],[5,9],[6,10],[5,11]] }, +188: { width: 23, points: [[4,0],[16,20],[-1,-1],[2,18],[4,20],[4,10],[-1,-1],[21,3],[12,3],[17,12],[18,12],[18,-1]] }, +189: { width: 23, points: [[4,0],[16,20],[-1,-1],[2,18],[4,20],[4,10],[-1,-1],[14,8],[16,10],[18,10],[20,8],[20,6],[14,1],[14,0],[21,0]] }, +215: { width: 20, points: [[4,16],[16,4],[-1,-1],[4,4],[16,16]] }, +402: { width: 15, points: [[15,21],[14,20],[15,19],[16,20],[16,21],[15,22],[13,22],[11,21],[10,20],[9,18],[8,15],[5,1],[4,-3],[3,-5],[-1,-1],[13,22],[11,20],[10,18],[9,14],[7,5],[6,1],[5,-2],[4,-4],[3,-5],[1,-6],[-1,-6],[-2,-5],[-2,-4],[-1,-3],[0,-4],[-1,-5],[-1,-1],[4,15],[14,15]] }, +8242: { width: 8, points: [[5,24],[3,16],[-1,-1],[3,16],[6,24]] }, +8243: { width: 11, points: [[4,24],[2,16],[-1,-1],[2,16],[5,24],[-1,-1],[9,24],[7,16],[-1,-1],[7,16],[10,24]] }, +8463: { width: 21, points: [[9,22],[3,1],[-1,-1],[5,7],[7,11],[9,13],[11,14],[13,14],[15,14],[16,12],[16,10],[14,5],[14,2],[15,1],[19,3],[-1,-1],[7,21],[9,22],[-1,-1],[4,15],[14,20]] }, +8491: { width: 0, points: [[-99,197]] }, +8495: { width: 18, points: [[4,6],[8,7],[11,8],[14,10],[15,12],[14,14],[12,15],[9,15],[6,14],[4,11],[3,8],[3,5],[4,3],[5,2],[7,1],[9,1],[12,2],[14,4],[-1,-1],[9,15],[7,14],[5,11],[4,8],[4,4],[5,2]] }, +8722: { width: 20, points: [[2,9],[18,9]] }, + +// Used to indicate missing glyph +129: { width: 18, points: [[2,-2],[2,21],[16,21],[16,-2],[2,-2]] } +}; + + +CanvasTextFunctions.letter = function (ch) +{ + glyph = CanvasTextFunctions.letters[ch]; + + // EAM - Draw an empty box for undefined glyphs + if (glyph == undefined) + glyph = CanvasTextFunctions.letters[129]; + + return glyph; +} + +CanvasTextFunctions.ascent = function( font, size) +{ + return size; +} + +CanvasTextFunctions.descent = function( font, size) +{ + return 7.0*size/25.0; +} + +CanvasTextFunctions.measure = function( font, size, str) +{ + var total = 0; + var len = str.length; + + for ( i = 0; i < len; i++) { + var index = str.charAt(i); + // EAM deal with non-ascii characaters + if (index > '~') + index = str.charCodeAt(i); + + var c = CanvasTextFunctions.letter(index); + if (!c) break; + total += c.width * size / 25.0; + if (c.width == 0) { + for ( j = 0; j < c.points.length; j++) { + var a = c.points[j]; + if (a[0] == -99) + total += CanvasTextFunctions.letter(a[1]).width * size / 25.0; + } + } + } + return total; +} + +CanvasTextFunctions.draw = function(ctx,font,size,x,y,str) +{ + var total = 0; + var len = str.length; + var mag = size / 25.0; + var composite = 0; + var xorig = x; + + ctx.save(); + ctx.lineCap = "round"; + ctx.lineWidth = 2.0 * mag; + + for (var i = 0; i < len; i++) { + var index = str.charAt(i); + + // EAM tab and newline + if (index == ' ') { + var tabstop = 48 * mag; + x = tabstop * Math.ceil(x/tabstop); + continue; + } else if (index < ' ') { + x = xorig; y += size; + continue; + } + // EAM deal with non-ascii characaters + if (index > '~') + index = str.charCodeAt(i); + + // EAM allow composing characters + if (composite != 0) { + index = composite; + composite = 0; + } + + var c = CanvasTextFunctions.letter(index); + if ( !c) continue; + + ctx.beginPath(); + + var penUp = 1; + var needStroke = 0; + for (var j = 0; j < c.points.length; j++) { + var a = c.points[j]; + if ( a[0] == -1 && a[1] == -1) { + penUp = 1; + continue; + } + if ( a[0] == -99 ) { + composite = a[1]; + i--; + break; + } + if ( penUp) { + ctx.moveTo( x + a[0]*mag, y - a[1]*mag); + penUp = false; + } else { + ctx.lineTo( x + a[0]*mag, y - a[1]*mag); + } + } + ctx.stroke(); + x += c.width*mag; + } + ctx.restore(); + return total; +} + +CanvasTextFunctions.enable = function( ctx) +{ + ctx.drawText = function(font,size,x,y,text) { return CanvasTextFunctions.draw( ctx, font,size,x,y,text); }; + ctx.measureText = function(font,size,text) { return CanvasTextFunctions.measure( font,size,text); }; + ctx.fontAscent = function(font,size) { return CanvasTextFunctions.ascent(font,size); } + ctx.fontDescent = function(font,size) { return CanvasTextFunctions.descent(font,size); } + + ctx.drawTextRight = function(font,size,x,y,text) { + var w = CanvasTextFunctions.measure(font,size,text); + return CanvasTextFunctions.draw( ctx, font,size,x-w,y,text); + }; + ctx.drawTextCenter = function(font,size,x,y,text) { + var w = CanvasTextFunctions.measure(font,size,text); + return CanvasTextFunctions.draw( ctx, font,size,x-w/2,y,text); + }; +} + diff --git a/src/netdaemon/www/js/canvastext.js b/src/netdaemon/www/js/canvastext.js new file mode 100644 index 0000000..4a18cdf --- /dev/null +++ b/src/netdaemon/www/js/canvastext.js @@ -0,0 +1,211 @@ +/* + * $Id: canvastext.js,v 1.3 2009/02/22 19:14:05 sfeam Exp $ + */ +// The canvastext.js code was released to the public domain by Jim Studt, 2007. +// He may keep some sort of up to date copy at http://www.federated.com/~jim/canvastext/ +// Feb 2009 Ethan A Merritt (EAM) Modify code to work in non-ascii environments. +// +var CanvasTextFunctions = { }; + +CanvasTextFunctions.letters = { + ' ': { width: 16, points: [] }, + '!': { width: 10, points: [[5,21],[5,7],[-1,-1],[5,2],[4,1],[5,0],[6,1],[5,2]] }, + '"': { width: 16, points: [[4,21],[4,14],[-1,-1],[12,21],[12,14]] }, + '#': { width: 21, points: [[11,25],[4,-7],[-1,-1],[17,25],[10,-7],[-1,-1],[4,12],[18,12],[-1,-1],[3,6],[17,6]] }, + '$': { width: 20, points: [[8,25],[8,-4],[-1,-1],[12,25],[12,-4],[-1,-1],[17,18],[15,20],[12,21],[8,21],[5,20],[3,18],[3,16],[4,14],[5,13],[7,12],[13,10],[15,9],[16,8],[17,6],[17,3],[15,1],[12,0],[8,0],[5,1],[3,3]] }, + '%': { width: 24, points: [[21,21],[3,0],[-1,-1],[8,21],[10,19],[10,17],[9,15],[7,14],[5,14],[3,16],[3,18],[4,20],[6,21],[8,21],[10,20],[13,19],[16,19],[19,20],[21,21],[-1,-1],[17,7],[15,6],[14,4],[14,2],[16,0],[18,0],[20,1],[21,3],[21,5],[19,7],[17,7]] }, + '&': { width: 26, points: [[23,12],[23,13],[22,14],[21,14],[20,13],[19,11],[17,6],[15,3],[13,1],[11,0],[7,0],[5,1],[4,2],[3,4],[3,6],[4,8],[5,9],[12,13],[13,14],[14,16],[14,18],[13,20],[11,21],[9,20],[8,18],[8,16],[9,13],[11,10],[16,3],[18,1],[20,0],[22,0],[23,1],[23,2]] }, + '\'': { width: 10, points: [[5,19],[4,20],[5,21],[6,20],[6,18],[5,16],[4,15]] }, + '(': { width: 14, points: [[11,25],[9,23],[7,20],[5,16],[4,11],[4,7],[5,2],[7,-2],[9,-5],[11,-7]] }, + ')': { width: 14, points: [[3,25],[5,23],[7,20],[9,16],[10,11],[10,7],[9,2],[7,-2],[5,-5],[3,-7]] }, + '*': { width: 16, points: [[8,21],[8,9],[-1,-1],[3,18],[13,12],[-1,-1],[13,18],[3,12]] }, + '+': { width: 26, points: [[13,18],[13,0],[-1,-1],[4,9],[22,9]] }, + ',': { width: 10, points: [[6,1],[5,0],[4,1],[5,2],[6,1],[6,-1],[5,-3],[4,-4]] }, + '-': { width: 26, points: [[4,9],[22,9]] }, + '.': { width: 10, points: [[5,2],[4,1],[5,0],[6,1],[5,2]] }, + '/': { width: 22, points: [[20,25],[2,-7]] }, + '0': { width: 20, points: [[9,21],[6,20],[4,17],[3,12],[3,9],[4,4],[6,1],[9,0],[11,0],[14,1],[16,4],[17,9],[17,12],[16,17],[14,20],[11,21],[9,21]] }, + '1': { width: 20, points: [[6,17],[8,18],[11,21],[11,0]] }, + '2': { width: 20, points: [[4,16],[4,17],[5,19],[6,20],[8,21],[12,21],[14,20],[15,19],[16,17],[16,15],[15,13],[13,10],[3,0],[17,0]] }, + '3': { width: 20, points: [[5,21],[16,21],[10,13],[13,13],[15,12],[16,11],[17,8],[17,6],[16,3],[14,1],[11,0],[8,0],[5,1],[4,2],[3,4]] }, + '4': { width: 20, points: [[13,21],[3,7],[18,7],[-1,-1],[13,21],[13,0]] }, + '5': { width: 20, points: [[15,21],[5,21],[4,12],[5,13],[8,14],[11,14],[14,13],[16,11],[17,8],[17,6],[16,3],[14,1],[11,0],[8,0],[5,1],[4,2],[3,4]] }, + '6': { width: 20, points: [[16,18],[15,20],[12,21],[10,21],[7,20],[5,17],[4,12],[4,7],[5,3],[7,1],[10,0],[11,0],[14,1],[16,3],[17,6],[17,7],[16,10],[14,12],[11,13],[10,13],[7,12],[5,10],[4,7]] }, + '7': { width: 20, points: [[17,21],[7,0],[-1,-1],[3,21],[17,21]] }, + '8': { width: 20, points: [[8,21],[5,20],[4,18],[4,16],[5,14],[7,13],[11,12],[14,11],[16,9],[17,7],[17,4],[16,2],[15,1],[12,0],[8,0],[5,1],[4,2],[3,4],[3,7],[4,9],[6,11],[9,12],[13,13],[15,14],[16,16],[16,18],[15,20],[12,21],[8,21]] }, + '9': { width: 20, points: [[16,14],[15,11],[13,9],[10,8],[9,8],[6,9],[4,11],[3,14],[3,15],[4,18],[6,20],[9,21],[10,21],[13,20],[15,18],[16,14],[16,9],[15,4],[13,1],[10,0],[8,0],[5,1],[4,3]] }, + ':': { width: 10, points: [[5,14],[4,13],[5,12],[6,13],[5,14],[-1,-1],[5,2],[4,1],[5,0],[6,1],[5,2]] }, + ';': { width: 10, points: [[5,14],[4,13],[5,12],[6,13],[5,14],[-1,-1],[6,1],[5,0],[4,1],[5,2],[6,1],[6,-1],[5,-3],[4,-4]] }, + '<': { width: 24, points: [[20,18],[4,9],[20,0]] }, + '=': { width: 26, points: [[4,12],[22,12],[-1,-1],[4,6],[22,6]] }, + '>': { width: 24, points: [[4,18],[20,9],[4,0]] }, + '?': { width: 18, points: [[3,16],[3,17],[4,19],[5,20],[7,21],[11,21],[13,20],[14,19],[15,17],[15,15],[14,13],[13,12],[9,10],[9,7],[-1,-1],[9,2],[8,1],[9,0],[10,1],[9,2]] }, + '@': { width: 27, points: [[18,13],[17,15],[15,16],[12,16],[10,15],[9,14],[8,11],[8,8],[9,6],[11,5],[14,5],[16,6],[17,8],[-1,-1],[12,16],[10,14],[9,11],[9,8],[10,6],[11,5],[-1,-1],[18,16],[17,8],[17,6],[19,5],[21,5],[23,7],[24,10],[24,12],[23,15],[22,17],[20,19],[18,20],[15,21],[12,21],[9,20],[7,19],[5,17],[4,15],[3,12],[3,9],[4,6],[5,4],[7,2],[9,1],[12,0],[15,0],[18,1],[20,2],[21,3],[-1,-1],[19,16],[18,8],[18,6],[19,5]] }, + 'A': { width: 18, points: [[9,21],[1,0],[-1,-1],[9,21],[17,0],[-1,-1],[4,7],[14,7]] }, + 'B': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,15],[17,13],[16,12],[13,11],[-1,-1],[4,11],[13,11],[16,10],[17,9],[18,7],[18,4],[17,2],[16,1],[13,0],[4,0]] }, + 'C': { width: 21, points: [[18,16],[17,18],[15,20],[13,21],[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5]] }, + 'D': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[11,21],[14,20],[16,18],[17,16],[18,13],[18,8],[17,5],[16,3],[14,1],[11,0],[4,0]] }, + 'E': { width: 19, points: [[4,21],[4,0],[-1,-1],[4,21],[17,21],[-1,-1],[4,11],[12,11],[-1,-1],[4,0],[17,0]] }, + 'F': { width: 18, points: [[4,21],[4,0],[-1,-1],[4,21],[17,21],[-1,-1],[4,11],[12,11]] }, + 'G': { width: 21, points: [[18,16],[17,18],[15,20],[13,21],[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[18,8],[-1,-1],[13,8],[18,8]] }, + 'H': { width: 22, points: [[4,21],[4,0],[-1,-1],[18,21],[18,0],[-1,-1],[4,11],[18,11]] }, + 'I': { width: 8, points: [[4,21],[4,0]] }, + 'J': { width: 16, points: [[12,21],[12,5],[11,2],[10,1],[8,0],[6,0],[4,1],[3,2],[2,5],[2,7]] }, + 'K': { width: 21, points: [[4,21],[4,0],[-1,-1],[18,21],[4,7],[-1,-1],[9,12],[18,0]] }, + 'L': { width: 17, points: [[4,21],[4,0],[-1,-1],[4,0],[16,0]] }, + 'M': { width: 24, points: [[4,21],[4,0],[-1,-1],[4,21],[12,0],[-1,-1],[20,21],[12,0],[-1,-1],[20,21],[20,0]] }, + 'N': { width: 22, points: [[4,21],[4,0],[-1,-1],[4,21],[18,0],[-1,-1],[18,21],[18,0]] }, + 'O': { width: 22, points: [[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[19,8],[19,13],[18,16],[17,18],[15,20],[13,21],[9,21]] }, + 'P': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,14],[17,12],[16,11],[13,10],[4,10]] }, + 'Q': { width: 22, points: [[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[19,8],[19,13],[18,16],[17,18],[15,20],[13,21],[9,21],[-1,-1],[12,4],[18,-2]] }, + 'R': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,15],[17,13],[16,12],[13,11],[4,11],[-1,-1],[11,11],[18,0]] }, + 'S': { width: 20, points: [[17,18],[15,20],[12,21],[8,21],[5,20],[3,18],[3,16],[4,14],[5,13],[7,12],[13,10],[15,9],[16,8],[17,6],[17,3],[15,1],[12,0],[8,0],[5,1],[3,3]] }, + 'T': { width: 16, points: [[8,21],[8,0],[-1,-1],[1,21],[15,21]] }, + 'U': { width: 22, points: [[4,21],[4,6],[5,3],[7,1],[10,0],[12,0],[15,1],[17,3],[18,6],[18,21]] }, + 'V': { width: 18, points: [[1,21],[9,0],[-1,-1],[17,21],[9,0]] }, + 'W': { width: 24, points: [[2,21],[7,0],[-1,-1],[12,21],[7,0],[-1,-1],[12,21],[17,0],[-1,-1],[22,21],[17,0]] }, + 'X': { width: 20, points: [[3,21],[17,0],[-1,-1],[17,21],[3,0]] }, + 'Y': { width: 18, points: [[1,21],[9,11],[9,0],[-1,-1],[17,21],[9,11]] }, + 'Z': { width: 20, points: [[17,21],[3,0],[-1,-1],[3,21],[17,21],[-1,-1],[3,0],[17,0]] }, + '[': { width: 14, points: [[4,25],[4,-7],[-1,-1],[5,25],[5,-7],[-1,-1],[4,25],[11,25],[-1,-1],[4,-7],[11,-7]] }, + '\\': { width: 14, points: [[0,21],[14,-3]] }, + ']': { width: 14, points: [[9,25],[9,-7],[-1,-1],[10,25],[10,-7],[-1,-1],[3,25],[10,25],[-1,-1],[3,-7],[10,-7]] }, + '^': { width: 16, points: [[5,15],[8,19],[11,15],[-1,-1],[2,12],[8,18],[14,12]] }, + '_': { width: 16, points: [[0,-2],[16,-2]] }, + '`': { width: 10, points: [[6,21],[5,20],[4,18],[4,16],[5,15],[6,16],[5,17]] }, + 'a': { width: 19, points: [[15,14],[15,0],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, + 'b': { width: 19, points: [[4,21],[4,0],[-1,-1],[4,11],[6,13],[8,14],[11,14],[13,13],[15,11],[16,8],[16,6],[15,3],[13,1],[11,0],[8,0],[6,1],[4,3]] }, + 'c': { width: 18, points: [[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, + 'd': { width: 19, points: [[15,21],[15,0],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, + 'e': { width: 18, points: [[3,8],[15,8],[15,10],[14,12],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, + 'f': { width: 12, points: [[10,21],[8,21],[6,20],[5,17],[5,0],[-1,-1],[2,14],[9,14]] }, + 'g': { width: 19, points: [[15,14],[15,-2],[14,-5],[13,-6],[11,-7],[8,-7],[6,-6],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, + 'h': { width: 19, points: [[4,21],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0]] }, + 'i': { width: 8, points: [[3,21],[4,20],[5,21],[4,22],[3,21],[-1,-1],[4,14],[4,0]] }, + 'j': { width: 10, points: [[5,21],[6,20],[7,21],[6,22],[5,21],[-1,-1],[6,14],[6,-3],[5,-6],[3,-7],[1,-7]] }, + 'k': { width: 17, points: [[4,21],[4,0],[-1,-1],[14,14],[4,4],[-1,-1],[8,8],[15,0]] }, + 'l': { width: 8, points: [[4,21],[4,0]] }, + 'm': { width: 30, points: [[4,14],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0],[-1,-1],[15,10],[18,13],[20,14],[23,14],[25,13],[26,10],[26,0]] }, + 'n': { width: 19, points: [[4,14],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0]] }, + 'o': { width: 19, points: [[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3],[16,6],[16,8],[15,11],[13,13],[11,14],[8,14]] }, + 'p': { width: 19, points: [[4,14],[4,-7],[-1,-1],[4,11],[6,13],[8,14],[11,14],[13,13],[15,11],[16,8],[16,6],[15,3],[13,1],[11,0],[8,0],[6,1],[4,3]] }, + 'q': { width: 19, points: [[15,14],[15,-7],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, + 'r': { width: 13, points: [[4,14],[4,0],[-1,-1],[4,8],[5,11],[7,13],[9,14],[12,14]] }, + 's': { width: 17, points: [[14,11],[13,13],[10,14],[7,14],[4,13],[3,11],[4,9],[6,8],[11,7],[13,6],[14,4],[14,3],[13,1],[10,0],[7,0],[4,1],[3,3]] }, + 't': { width: 12, points: [[5,21],[5,4],[6,1],[8,0],[10,0],[-1,-1],[2,14],[9,14]] }, + 'u': { width: 19, points: [[4,14],[4,4],[5,1],[7,0],[10,0],[12,1],[15,4],[-1,-1],[15,14],[15,0]] }, + 'v': { width: 16, points: [[2,14],[8,0],[-1,-1],[14,14],[8,0]] }, + 'w': { width: 22, points: [[3,14],[7,0],[-1,-1],[11,14],[7,0],[-1,-1],[11,14],[15,0],[-1,-1],[19,14],[15,0]] }, + 'x': { width: 17, points: [[3,14],[14,0],[-1,-1],[14,14],[3,0]] }, + 'y': { width: 16, points: [[2,14],[8,0],[-1,-1],[14,14],[8,0],[6,-4],[4,-6],[2,-7],[1,-7]] }, + 'z': { width: 17, points: [[14,14],[3,0],[-1,-1],[3,14],[14,14],[-1,-1],[3,0],[14,0]] }, + '{': { width: 14, points: [[9,25],[7,24],[6,23],[5,21],[5,19],[6,17],[7,16],[8,14],[8,12],[6,10],[-1,-1],[7,24],[6,22],[6,20],[7,18],[8,17],[9,15],[9,13],[8,11],[4,9],[8,7],[9,5],[9,3],[8,1],[7,0],[6,-2],[6,-4],[7,-6],[-1,-1],[6,8],[8,6],[8,4],[7,2],[6,1],[5,-1],[5,-3],[6,-5],[7,-6],[9,-7]] }, + '|': { width: 8, points: [[4,25],[4,-7]] }, + '}': { width: 14, points: [[5,25],[7,24],[8,23],[9,21],[9,19],[8,17],[7,16],[6,14],[6,12],[8,10],[-1,-1],[7,24],[8,22],[8,20],[7,18],[6,17],[5,15],[5,13],[6,11],[10,9],[6,7],[5,5],[5,3],[6,1],[7,0],[8,-2],[8,-4],[7,-6],[-1,-1],[8,8],[6,6],[6,4],[7,2],[8,1],[9,-1],[9,-3],[8,-5],[7,-6],[5,-7]] }, + '~': { width: 24, points: [[3,6],[3,8],[4,11],[6,12],[8,12],[10,11],[14,8],[16,7],[18,7],[20,8],[21,10],[-1,-1],[3,8],[4,10],[6,11],[8,11],[10,10],[14,7],[16,6],[18,6],[20,7],[21,10],[21,12]] }, + +// Miscellaneous glyphs (EAM) +129: { width: 18, points: [[2,-2],[2,21],[16,21],[16,-2],[2,-2]] }, +176: { width: 8, points: [[3,22],[1,20],[1,18],[3,16],[5,16],[7,18],[7,20],[5,22],[3,22]]}, +197: { width: 18, points: [[9,18],[1,0],[-1,-1],[9,18],[17,0],[-1,-1],[4,7],[14,7],[-1,-1], [8,22],[6,20],[6,18],[8,16],[10,16],[12,18],[12,20],[10,22],[8,22]]}, +215: { width: 26, points: [[6,17],[20,3],[-1,-1],[6,3],[20,17]] } +}; + +CanvasTextFunctions.letter = function (ch) +{ + glyph = CanvasTextFunctions.letters[ch]; + + // EAM - Draw an empty box for undefined glyphs + if (glyph == undefined) + glyph = CanvasTextFunctions.letters[129]; + + return glyph; +} + +CanvasTextFunctions.ascent = function( font, size) +{ + return size; +} + +CanvasTextFunctions.descent = function( font, size) +{ + return 7.0*size/25.0; +} + +CanvasTextFunctions.measure = function( font, size, str) +{ + var total = 0; + var len = str.length; + + for ( i = 0; i < len; i++) { + var index = str.charAt(i); + // EAM deal with non-ascii characaters + if (index > '~') + index = str.charCodeAt(i); + + var c = CanvasTextFunctions.letter(index); + if (c) total += c.width * size / 25.0; + } + return total; +} + +CanvasTextFunctions.draw = function(ctx,font,size,x,y,str) +{ + var total = 0; + var len = str.length; + var mag = size / 25.0; + + ctx.save(); + ctx.lineCap = "round"; + ctx.lineWidth = 2.0 * mag; + + for ( i = 0; i < len; i++) { + var index = str.charAt(i); + // EAM deal with non-ascii characaters + if (index > '~') + index = str.charCodeAt(i); + + var c = CanvasTextFunctions.letter(index); + if ( !c) continue; + + ctx.beginPath(); + + var penUp = 1; + var needStroke = 0; + for ( j = 0; j < c.points.length; j++) { + var a = c.points[j]; + if ( a[0] == -1 && a[1] == -1) { + penUp = 1; + continue; + } + if ( penUp) { + ctx.moveTo( x + a[0]*mag, y - a[1]*mag); + penUp = false; + } else { + ctx.lineTo( x + a[0]*mag, y - a[1]*mag); + } + } + ctx.stroke(); + x += c.width*mag; + } + ctx.restore(); + return total; +} + +CanvasTextFunctions.enable = function( ctx) +{ + ctx.drawText = function(font,size,x,y,text) { return CanvasTextFunctions.draw( ctx, font,size,x,y,text); }; + ctx.measureText = function(font,size,text) { return CanvasTextFunctions.measure( font,size,text); }; + ctx.fontAscent = function(font,size) { return CanvasTextFunctions.ascent(font,size); } + ctx.fontDescent = function(font,size) { return CanvasTextFunctions.descent(font,size); } + + ctx.drawTextRight = function(font,size,x,y,text) { + var w = CanvasTextFunctions.measure(font,size,text); + return CanvasTextFunctions.draw( ctx, font,size,x-w,y,text); + }; + ctx.drawTextCenter = function(font,size,x,y,text) { + var w = CanvasTextFunctions.measure(font,size,text); + return CanvasTextFunctions.draw( ctx, font,size,x-w/2,y,text); + }; +} + diff --git a/src/netdaemon/www/js/gnuplot_common.js b/src/netdaemon/www/js/gnuplot_common.js new file mode 100644 index 0000000..d49fc63 --- /dev/null +++ b/src/netdaemon/www/js/gnuplot_common.js @@ -0,0 +1,170 @@ +/* + * $Id: gnuplot_common.js,v 1.10 2012/05/03 20:35:22 sfeam Exp $ + */ +// Shared routines for gnuplot's HTML5 canvas terminal driver. + +var gnuplot = { }; + +gnuplot.common_version = "3 May 2012"; + +gnuplot.L = function (x,y) { + if (gnuplot.zoomed) { + var zoom = gnuplot.zoomXY(x/10.0,y/10.0); + ctx.lineTo(zoom.x,zoom.y); + } else + ctx.lineTo(x/10.0,y/10.0); +} +gnuplot.M = function (x,y) { + if (gnuplot.zoomed) { + var zoom = gnuplot.zoomXY(x/10.0,y/10.0); + ctx.moveTo(zoom.x,zoom.y); + } else + ctx.moveTo(x/10.0,y/10.0); +} +gnuplot.R = function (x,y,w,h) { + if (gnuplot.zoomed) { + var dx, dy, dw, dh; + var zoom = gnuplot.zoomXY(x/10.0,y/10.0); + if (zoom.x >= gnuplot.plot_xmax) return; + if (zoom.y >= gnuplot.plot_ybot) return; + dx = zoom.x; dy = zoom.y; + zoom = gnuplot.zoomXY((x+w)/10.,(y+h)/10.); + if (zoom.xraw <= gnuplot.plot_xmin) return; + if (zoom.yraw <= gnuplot.plot_ytop) return; + dw = zoom.x - dx; dh = zoom.y -dy; + ctx.fillRect(dx, dy, dw, dh); + } else + ctx.fillRect(x/10.0, y/10.0, w/10.0, h/10.0); +} +gnuplot.T = function (x,y,fontsize,justify,string) { + var xx = x/10.0; var yy = y/10.0; + if (gnuplot.zoomed) { + var zoom = gnuplot.zoomXY(xx,yy); + if (zoom.clip) return; + xx = zoom.x; yy = zoom.y; + if (gnuplot.plot_xmin < xx && xx < gnuplot.plot_xmax && gnuplot.plot_ybot > yy && yy > gnuplot.plot_ytop) + if ((typeof(gnuplot.zoom_text) != "undefined") && (gnuplot.zoom_text == true)) + fontsize = Math.sqrt(gnuplot.zoomW(fontsize)*gnuplot.zoomH(fontsize)); + } + if (justify=="") ctx.drawText("sans", fontsize, xx, yy, string); + else if (justify=="Right") ctx.drawTextRight("sans", fontsize, xx, yy, string); + else if (justify=="Center") ctx.drawTextCenter("sans", fontsize, xx, yy, string); +} +gnuplot.TR = function (x,y,angle,fontsize,justify,string) { + var xx = x/10.0; var yy = y/10.0; + if (gnuplot.zoomed) { + var zoom = gnuplot.zoomXY(xx,yy); + if (zoom.clip) return; + xx = zoom.x; yy = zoom.y; + if (gnuplot.plot_xmin < xx && xx < gnuplot.plot_xmax && gnuplot.plot_ybot > yy && yy > gnuplot.plot_ytop) + if ((typeof(gnuplot.zoom_text) != "undefined") && (gnuplot.zoom_text == true)) + fontsize = Math.sqrt(gnuplot.zoomW(fontsize)*gnuplot.zoomH(fontsize)); + } + ctx.save(); + ctx.translate(xx,yy); + ctx.rotate(angle * Math.PI / 180); + if (justify=="") ctx.drawText("sans", fontsize, 0, 0, string); + else if (justify=="Right") ctx.drawTextRight("sans", fontsize, 0, 0, string); + else if (justify=="Center") ctx.drawTextCenter("sans", fontsize, 0, 0, string); + ctx.restore(); +} +gnuplot.bp = function (x,y) // begin polygon + { ctx.beginPath(); gnuplot.M(x,y); } +gnuplot.cfp = function () // close and fill polygon + { ctx.closePath(); ctx.fill(); } +gnuplot.cfsp = function () // close and fill polygon with stroke color + { ctx.closePath(); ctx.fillStyle = ctx.strokeStyle; ctx.stroke(); ctx.fill(); } +gnuplot.Dot = function (x,y) { + var xx = x; var yy = y; + if (gnuplot.zoomed) {zoom = gnuplot.zoomXY(xx,yy); xx = zoom.x; yy = zoom.y; if (zoom.clip) return;} + ctx.strokeRect(xx,yy,0.5,0.5); +} +gnuplot.Pt = function (N,x,y,w) { + var xx = x; var yy = y; + if (gnuplot.zoomed) {var zoom = gnuplot.zoomXY(xx,yy); xx = zoom.x; yy = zoom.y; if (zoom.clip) return;} + if (w==0) return; + switch (N) + { + case 0: + ctx.beginPath(); + ctx.moveTo(xx-w,yy); ctx.lineTo(xx+w,yy); + ctx.moveTo(xx,yy-w); ctx.lineTo(xx,yy+w); + ctx.stroke(); + break; + case 1: + var ww = w * 3/4; + ctx.beginPath(); + ctx.moveTo(xx-ww,yy-ww); ctx.lineTo(xx+ww,yy+ww); + ctx.moveTo(xx+ww,yy-ww); ctx.lineTo(xx-ww,yy+ww); + ctx.stroke(); + break; + case 2: + gnuplot.Pt(0,x,y,w); gnuplot.Pt(1,x,y,w); + break; + case 3: + ctx.strokeRect(xx-w/2,yy-w/2,w,w); + break; + case 4: + ctx.save(); ctx.strokeRect(xx-w/2,yy-w/2,w,w); ctx.restore(); + ctx.fillRect(xx-w/2,yy-w/2,w,w); + break; + case 5: + ctx.beginPath(); ctx.arc(xx,yy,w/2,0,Math.PI*2,true); ctx.stroke(); + break; + default: + case 6: + ctx.beginPath(); ctx.arc(xx,yy,w/2,0,Math.PI*2,true); ctx.fill(); + break; + case 7: + ctx.beginPath(); + ctx.moveTo(xx,yy-w); ctx.lineTo(xx-w,yy+w/2); ctx.lineTo(xx+w,yy+w/2); + ctx.closePath(); + ctx.stroke(); + break; + case 8: + ctx.beginPath(); + ctx.moveTo(xx,yy-w); ctx.lineTo(xx-w,yy+w/2); ctx.lineTo(xx+w,yy+w/2); + ctx.closePath(); + ctx.fill(); + break; + } +} + +// Zoomable image +gnuplot.ZI = function (image, m, n, x1, y1, x2, y2) { + if (gnuplot.zoomed) { + var sx, sy, sw, sh, dx, dy, dw, dh; + + var zoom = gnuplot.zoomXY(x1/10.0,y1/10.0); + if (zoom.x >= gnuplot.plot_xmax) return; + if (zoom.y >= gnuplot.plot_ybot) return; + x1raw = zoom.xraw; y1raw = zoom.yraw; + dx = zoom.x; dy = zoom.y; + + zoom = gnuplot.zoomXY((x2)/10.,(y2)/10.); + if (zoom.xraw <= gnuplot.plot_xmin) return; + if (zoom.yraw <= gnuplot.plot_ytop) return; + var x2raw = zoom.xraw; var y2raw = zoom.yraw; + dw = zoom.x - dx; dh = zoom.y - dy; + + // FIXME: This is sometimes flaky. Needs integer truncation? + sx = 0; sy = 0; sw = m; sh = n; + if (x1raw < dx) sx = m * (dx - x1raw) / (x2raw - x1raw); + if (y1raw < dy) sy = n * (dy - y1raw) / (y2raw - y1raw); + if (x2raw > zoom.x) + sw = m * (1. - ((x2raw - zoom.x) / (x2raw - x1raw))); + if (y2raw > zoom.y) + sh = n * (1. - ((y2raw - zoom.y) / (y2raw - y1raw))); + sw = sw - sx; sh = sh - sy; + + ctx.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh); +} else + ctx.drawImage(image, x1/10.0, y1/10.0, (x2-x1)/10.0, (y2-y1)/10.0); +} + +// These methods are place holders that are loaded by gnuplot_dashedlines.js + +gnuplot.dashtype = function (dt) {} ; +gnuplot.dashstart = function (x,y) {gnuplot.M(x,y);} ; +gnuplot.dashstep = function (x,y) {gnuplot.L(x,y);} ; +gnuplot.pattern = []; diff --git a/src/netdaemon/www/js/gnuplot_dashedlines.js b/src/netdaemon/www/js/gnuplot_dashedlines.js new file mode 100644 index 0000000..ac21681 --- /dev/null +++ b/src/netdaemon/www/js/gnuplot_dashedlines.js @@ -0,0 +1,68 @@ +/* + * $Id: gnuplot_dashedlines.js,v 1.3 2012/05/03 20:35:22 sfeam Exp $ + */ +/* + * Ethan Merritt November 2010 + * Add a dashed line method to gnuplot's HTML5 canvas toolset. + * To start a path use DS(x0,y0) instead of M(x0,y0) + * To draw line segments use DL(x,y) instead of L(x,y) + * Finish as usual with ctx.stroke(); ctx.closePath(); + */ + +gnuplot.solid = []; +gnuplot.dashpattern1 = gnuplot.solid; +gnuplot.dashpattern2 = [ 0.5, 1.0 ]; +gnuplot.dashpattern3 = [ .12, .25, .37, .50, .62, .75, .87, 1.0 ]; +gnuplot.dashpattern4 = [ 0.50, 0.70, 0.80, 1.0 ]; +gnuplot.dashpattern5 = [ 0.30, 0.50, 0.60, 0.75, 0.85, 1.0 ]; + +gnuplot.dashlength = 200.; // length of one complete pattern +gnuplot.dashfraction = 0; // how far in the pattern we got last time +gnuplot.dash_x = 0; +gnuplot.dash_y = 0; + +gnuplot.dashtype = function(dt) { + gnuplot.pattern = dt; +} + +gnuplot.dashstart = function(x,y) { + gnuplot.dash_x = x; + gnuplot.dash_y = y; + gnuplot.dashfraction = 0; + gnuplot.M(x,y); +} + +gnuplot.dashstep = function(x,y) { + var delx = x - gnuplot.dash_x; + var dely = y - gnuplot.dash_y; + var stride = Math.sqrt(delx*delx + dely*dely) / gnuplot.dashlength; + var this_step; var new_x, new_y; + + if (gnuplot.pattern.length == 0) {gnuplot.L(x,y); return;} + + while (stride > 0) { + // Find which piece of the pattern we are in + for (i=0; gnuplot.pattern[i] <= gnuplot.dashfraction; i++); + + this_step = gnuplot.pattern[i] - gnuplot.dashfraction; + if (stride > this_step) { + new_x = gnuplot.dash_x + delx*this_step/stride; + new_y = gnuplot.dash_y + dely*this_step/stride; + stride = stride - this_step; + gnuplot.dashfraction = gnuplot.pattern[i]; + delx = x - new_x; + dely = y - new_y; + } else { + new_x = x; + new_y = y; + gnuplot.dashfraction = gnuplot.dashfraction + stride; + stride = 0; + } + if (i%2==0) gnuplot.L(new_x,new_y); + else gnuplot.M(new_x,new_y); + + gnuplot.dash_x = new_x; + gnuplot.dash_y = new_y; + if (gnuplot.dashfraction >= 1.0) gnuplot.dashfraction = 0; + } +} diff --git a/src/netdaemon/www/js/gnuplot_mouse.css b/src/netdaemon/www/js/gnuplot_mouse.css new file mode 100644 index 0000000..e5ed1ba --- /dev/null +++ b/src/netdaemon/www/js/gnuplot_mouse.css @@ -0,0 +1,19 @@ +canvas { border:0; background-color: white; } +td.mousebox { vertical-align: top; } +td.mbh { height: 16pt; } +td.mb0 { text-align: left; font-size: smaller; width: 20px; padding-left: 15px; + color:#000; background-color: #EEE} +td.mb1 { text-align: left; font-size: smaller; width: 80px; padding-left: 15px; + color:#070; background-color: #EEE} +td.icon { width: 16px; height: 16px; font-size: 14px; text-align: right; + background-color: #EEE} +td.blank { height: 16px; font-size: 12pt; text-align: right; + background-color: #EEE} +.icon-image { float:right; margin: 0 0 0 0; } +.noborder { border: 0; vertical-align: text-top; } +table.mbleft { border-collapse: collapse; border: 0; margin-left: auto; margin-right: auto; } +table.mbright { border-collapse: collapse; border: 0; margin-left: 0; margin-right: auto; } +table.mbunder { border-collapse: collapse; border: 0; margin-left: auto; margin-right: auto; } +table.plot { border-collapse: collapse; border: 0; margin-left: auto; margin-right: auto; } +table.mousebox { border-collapse: collapse; margin-left: 0; margin-right: 10px; border: 0; } +div.gnuplot { background-color: white; } diff --git a/src/netdaemon/www/js/gnuplot_mouse.js b/src/netdaemon/www/js/gnuplot_mouse.js new file mode 100644 index 0000000..deb5213 --- /dev/null +++ b/src/netdaemon/www/js/gnuplot_mouse.js @@ -0,0 +1,619 @@ +/* + * $Id: gnuplot_mouse.js,v 1.28 2017/02/17 21:27:53 sfeam Exp $ + */ + gnuplot.mouse_version = " 17 February 2017"; + +// Mousing code for use with gnuplot's 'canvas' terminal driver. +// The functions defined here assume that the javascript plot produced by +// gnuplot initializes the plot boundary and scaling parameters. + + gnuplot.mousex = 0; + gnuplot.mousey = 0; + gnuplot.plotx = 0; + gnuplot.ploty = 0; + gnuplot.scaled_x = 0; + gnuplot.scaled_y = 0; + +// These will be initialized by the gnuplot canvas-drawing function + gnuplot.plot_xmin = 0; + gnuplot.plot_xmax = 0; + gnuplot.plot_ybot = 0; + gnuplot.plot_ytop = 0; + gnuplot.plot_width = 0 + gnuplot.plot_height = 0 + gnuplot.plot_term_ymax = 0; + gnuplot.plot_axis_xmin = 0; + gnuplot.plot_axis_xmax = 0; + gnuplot.plot_axis_width = 0; + gnuplot.plot_axis_height = 0; + gnuplot.plot_axis_ymin = 0; + gnuplot.plot_axis_ymax = 0; + gnuplot.plot_axis_x2min = "none"; + gnuplot.plot_axis_y2min = "none"; + gnuplot.plot_logaxis_x = 0; + gnuplot.plot_logaxis_y = 0; + gnuplot.grid_lines = true; + gnuplot.zoom_text = false; + gnuplot.display_is_uptodate = false; + +// These are the equivalent parameters while zooming + gnuplot.zoom_axis_xmin = 0; + gnuplot.zoom_axis_xmax = 0; + gnuplot.zoom_axis_ymin = 0; + gnuplot.zoom_axis_ymax = 0; + gnuplot.zoom_axis_x2min = 0; + gnuplot.zoom_axis_x2max = 0; + gnuplot.zoom_axis_y2min = 0; + gnuplot.zoom_axis_y2max = 0; + gnuplot.zoom_axis_width = 0; + gnuplot.zoom_axis_height = 0; + gnuplot.zoom_temp_xmin = 0; + gnuplot.zoom_temp_ymin = 0; + gnuplot.zoom_temp_x2min = 0; + gnuplot.zoom_temp_y2min = 0; + gnuplot.zoom_in_progress = false; + + gnuplot.full_canvas_image = null; + gnuplot.axisdate = new Date(); + +gnuplot.init = function () +{ + if (document.getElementById("gnuplot_canvas")) + document.getElementById("gnuplot_canvas").onmousemove = gnuplot.mouse_update; + if (document.getElementById("gnuplot_canvas")) + document.getElementById("gnuplot_canvas").onmouseup = gnuplot.zoom_in; + if (document.getElementById("gnuplot_canvas")) + document.getElementById("gnuplot_canvas").onmousedown = gnuplot.saveclick; + if (document.getElementById("gnuplot_canvas")) + document.getElementById("gnuplot_canvas").onkeydown = gnuplot.do_hotkey; + if (document.getElementById("gnuplot_grid_icon")) + document.getElementById("gnuplot_grid_icon").onmouseup = gnuplot.toggle_grid; + if (document.getElementById("gnuplot_textzoom_icon")) + document.getElementById("gnuplot_textzoom_icon").onmouseup = gnuplot.toggle_zoom_text; + if (document.getElementById("gnuplot_rezoom_icon")) + document.getElementById("gnuplot_rezoom_icon").onmouseup = gnuplot.rezoom; + if (document.getElementById("gnuplot_unzoom_icon")) + document.getElementById("gnuplot_unzoom_icon").onmouseup = gnuplot.unzoom; + gnuplot.mouse_update(); +} + +gnuplot.getMouseCoordsWithinTarget = function(event) +{ + var coords = { x: 0, y: 0}; + + if(!event) // then we're in a non-DOM (probably IE) browser + { + event = window.event; + if (event) { + coords.x = event.offsetX; + coords.y = event.offsetY; + } + } + else // we assume DOM modeled javascript + { + var Element = event.target ; + var CalculatedTotalOffsetLeft = 0; + var CalculatedTotalOffsetTop = 0 ; + + while (Element.offsetParent) + { + CalculatedTotalOffsetLeft += Element.offsetLeft ; + CalculatedTotalOffsetTop += Element.offsetTop ; + Element = Element.offsetParent ; + } + + coords.x = event.pageX - CalculatedTotalOffsetLeft ; + coords.y = event.pageY - CalculatedTotalOffsetTop ; + } + + gnuplot.mousex = coords.x; + gnuplot.mousey = coords.y; +} + + +gnuplot.mouse_update = function(e) +{ + gnuplot.getMouseCoordsWithinTarget(e); + + gnuplot.plotx = gnuplot.mousex - gnuplot.plot_xmin; + gnuplot.ploty = -(gnuplot.mousey - gnuplot.plot_ybot); + + // Limit tracking to the interior of the plot + if (gnuplot.plotx < 0 || gnuplot.ploty < 0){ + if (gnuplot.hypertext_list != "undefined" && gnuplot.hypertext_list.length > 0) { + gnuplot.check_hypertext(); + } + return; + } + if (gnuplot.mousex > gnuplot.plot_xmax || gnuplot.mousey < gnuplot.plot_ytop){ + if (gnuplot.hypertext_list != "undefined" && gnuplot.hypertext_list.length > 0) { + gnuplot.check_hypertext(); + } + return; + } + + var axis_xmin = (gnuplot.zoomed) ? gnuplot.zoom_axis_xmin : gnuplot.plot_axis_xmin; + var axis_xmax = (gnuplot.zoomed) ? gnuplot.zoom_axis_xmax : gnuplot.plot_axis_xmax; + var axis_ymin = (gnuplot.zoomed) ? gnuplot.zoom_axis_ymin : gnuplot.plot_axis_ymin; + var axis_ymax = (gnuplot.zoomed) ? gnuplot.zoom_axis_ymax : gnuplot.plot_axis_ymax; + + if (gnuplot.plot_logaxis_x != 0) { + x = Math.log(axis_xmax) - Math.log(axis_xmin); + x = x * (gnuplot.plotx / (gnuplot.plot_xmax-gnuplot.plot_xmin)) + Math.log(axis_xmin); + x = Math.exp(x); + } else { + x = axis_xmin + (gnuplot.plotx / (gnuplot.plot_xmax-gnuplot.plot_xmin)) * (axis_xmax - axis_xmin); + } + + if (gnuplot.plot_logaxis_y != 0) { + y = Math.log(axis_ymax) - Math.log(axis_ymin); + y = y * (-gnuplot.ploty / (gnuplot.plot_ytop-gnuplot.plot_ybot)) + Math.log(axis_ymin); + y = Math.exp(y); + } else { + y = axis_ymin - (gnuplot.ploty / (gnuplot.plot_ytop-gnuplot.plot_ybot)) * (axis_ymax - axis_ymin); + } + + if (gnuplot.plot_axis_x2min != "none") { + gnuplot.axis_x2min = (gnuplot.zoomed) ? gnuplot.zoom_axis_x2min : gnuplot.plot_axis_x2min; + gnuplot.axis_x2max = (gnuplot.zoomed) ? gnuplot.zoom_axis_x2max : gnuplot.plot_axis_x2max; + if (gnuplot.x2_mapping != undefined) + x2 = gnuplot.x2_mapping(x); + else + x2 = gnuplot.axis_x2min + (gnuplot.plotx / (gnuplot.plot_xmax-gnuplot.plot_xmin)) * (gnuplot.axis_x2max - gnuplot.axis_x2min); + if (document.getElementById(gnuplot.active_plot_name + "_x2")) + document.getElementById(gnuplot.active_plot_name + "_x2").innerHTML = x2.toPrecision(4); + } + if (gnuplot.plot_axis_y2min != "none") { + gnuplot.axis_y2min = (gnuplot.zoomed) ? gnuplot.zoom_axis_y2min : gnuplot.plot_axis_y2min; + gnuplot.axis_y2max = (gnuplot.zoomed) ? gnuplot.zoom_axis_y2max : gnuplot.plot_axis_y2max; + if (gnuplot.y2_mapping != undefined) + y2 = gnuplot.y2_mapping(y); + else + y2 = gnuplot.axis_y2min - (gnuplot.ploty / (gnuplot.plot_ytop-gnuplot.plot_ybot)) * (gnuplot.axis_y2max - gnuplot.axis_y2min); + if (document.getElementById(gnuplot.active_plot_name + "_y2")) + document.getElementById(gnuplot.active_plot_name + "_y2").innerHTML = y2.toPrecision(4); + } + + var label_x, label_y; + if (gnuplot.polar_mode) { + var polar = gnuplot.convert_to_polar(x,y); + label_x = "ang= " + polar.ang.toPrecision(4); + label_y = "R= " + polar.r.toPrecision(4); + } else { + if (typeof(gnuplot.plot_timeaxis_x) == "string" && gnuplot.plot_timeaxis_x != "") + label_x = gnuplot.datafmt(x); + else + label_x = x.toPrecision(4); + if (typeof(gnuplot.plot_timeaxis_y) == "string" && gnuplot.plot_timeaxis_y != "") + label_y = gnuplot.datafmt(y); + else + label_y = y.toPrecision(4); + } + + if (document.getElementById(gnuplot.active_plot_name + "_x")) + document.getElementById(gnuplot.active_plot_name + "_x").innerHTML = label_x; + if (document.getElementById(gnuplot.active_plot_name + "_y")) + document.getElementById(gnuplot.active_plot_name + "_y").innerHTML = label_y; + + // Echo the zoom box interactively + if (gnuplot.zoom_in_progress) { + // Clear previous box before drawing a new one + if (gnuplot.full_canvas_image == null) { + gnuplot.full_canvas_image = ctx.getImageData(0,0,canvas.width,canvas.height); + } else { + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.putImageData(gnuplot.full_canvas_image,0,0); + } + ctx.strokeStyle="rgba(128,128,128,0.60)"; + var x0 = gnuplot.plot_xmin + gnuplot.zoom_temp_plotx; + var y0 = gnuplot.plot_ybot - gnuplot.zoom_temp_ploty; + var w = gnuplot.plotx - gnuplot.zoom_temp_plotx; + var h = -(gnuplot.ploty - gnuplot.zoom_temp_ploty); + if (w<0) {x0 = x0 + w; w = -w;} + if (h<0) {y0 = y0 + h; h = -h;} + ctx.strokeRect(x0,y0,w,h); + } + + // See if we are over a hypertext anchor point + if (gnuplot.hypertext_list != "undefined" && gnuplot.hypertext_list.length > 0) { + gnuplot.check_hypertext(); + } +} + +gnuplot.check_hypertext = function() +{ + var nitems = gnuplot.hypertext_list.length; + for (var i=0; i gnuplot.plot_term_xmax-(len+8) ){ + // position text left of cursor if we are to far in the right hemisphere + // else we might print over the border + linkx = linkx - len; + shiftx = -shiftx; + } + if (linky > 14*lines.length ){ + // position text top of cursor if we are low enough in the plot + // else we might print over the border + linky = linky - 14*lines.length; + } + + ctx.fillStyle = "rgba(238,238,238,0.8)" + ctx.fillRect(linkx+shiftx, linky+4, len+8, 14*lines.length); + for (var l=0; l= 0) { + gnuplot.on_hypertext = -1; + ctx.clearRect(0,0,gnuplot.plot_term_xmax,gnuplot.plot_term_ymax); + gnuplot.display_is_uptodate = false; + gnuplot_canvas(); + } +} + +gnuplot.datafmt = function (x) +{ + if (gnuplot.plot_timeaxis_x == "DMS") { + return gnuplot.convert_to_DMS(x); + } + + // gnuplot 4.6 used (x + 946684800) + gnuplot.axisdate.setTime(1000. * x); + + if (gnuplot.plot_timeaxis_x == "DateTime") { + return gnuplot.axisdate.toUTCString(); + } + if (gnuplot.plot_timeaxis_x == "Date") { + var year = gnuplot.axisdate.getUTCFullYear(); + var month = gnuplot.axisdate.getUTCMonth(); + var date = gnuplot.axisdate.getUTCDate(); + return (" " + date).slice (-2) + "/" + + ("0" + (month+1)).slice (-2) + "/" + + year; + } + if (gnuplot.plot_timeaxis_x == "Time") { + var hour = gnuplot.axisdate.getUTCHours(); + var minute = gnuplot.axisdate.getUTCMinutes(); + var second = gnuplot.axisdate.getUTCSeconds(); + return ("0" + hour).slice (-2) + ":" + + ("0" + minute).slice (-2) + ":" + + ("0" + second).slice (-2); + } +} + +gnuplot.convert_to_DMS = function (x) +{ + var dms = {d:0, m:0, s:0}; + var deg = Math.abs(x); + dms.d = Math.floor(deg); + dms.m = Math.floor((deg - dms.d) * 60.); + dms.s = Math.floor((deg - dms.d) * 3600. - dms.m * 60.); + fmt = ((x<0)?"-":" ") + + dms.d.toFixed(0) + "°" + + dms.m.toFixed(0) + "\"" + + dms.s.toFixed(0) + "'"; + return fmt; +} + +gnuplot.convert_to_polar = function (x,y) +{ + var polar = new Object; + var phi, r; + phi = Math.atan2(y,x); + if (gnuplot.plot_logaxis_r) + r = Math.exp( (x/Math.cos(phi) + Math.log(gnuplot.plot_axis_rmin)/Math.LN10) * Math.LN10); + else if (gnuplot.plot_axis_rmin > gnuplot.plot_axis_rmax) + r = gnuplot.plot_axis_rmin - x/Math.cos(phi); + else + r = gnuplot.plot_axis_rmin + x/Math.cos(phi); + phi = phi * (180./Math.PI); + if (gnuplot.polar_sense < 0) + phi = -phi; + if (gnuplot.polar_theta0 != undefined) + phi = phi + gnuplot.polar_theta0; + if (phi > 180.) + phi = phi - 360.; + polar.r = r; + polar.ang = phi; + return polar; +} + +gnuplot.saveclick = function (event) +{ + gnuplot.mouse_update(event); + var button, label_x, label_y; + + // Limit tracking to the interior of the plot + if (gnuplot.plotx < 0 || gnuplot.ploty < 0) return; + if (gnuplot.mousex > gnuplot.plot_xmax || gnuplot.mousey < gnuplot.plot_ytop) return; + + if (event.which == null) /* IE case */ + button= (event.button < 2) ? "LEFT" : ((event.button == 4) ? "MIDDLE" : "RIGHT"); + else /* All others */ + button= (event.which < 2) ? "LEFT" : ((event.which == 2) ? "MIDDLE" : "RIGHT"); + + if (button == "LEFT") { + ctx.strokeStyle="black"; + ctx.strokeRect(gnuplot.mousex, gnuplot.mousey, 1, 1); + if (typeof(gnuplot.plot_timeaxis_x) == "string" && gnuplot.plot_timeaxis_x != "") + label_x = gnuplot.datafmt(x); + else + label_x = x.toPrecision(4); + if (typeof(gnuplot.plot_timeaxis_y) == "string" && gnuplot.plot_timeaxis_y != "") + label_y = gnuplot.datafmt(y); + else + label_y = y.toPrecision(4); + click = " " + label_x + ", " + label_y; + + var len = ctx.measureText("sans", 9, click); + if (gnuplot.mousex > gnuplot.plot_term_xmax-(len) ){ + // draw left of cursor when we are near the right side of the plot + len += ctx.measureText("sans", 9, " "); + ctx.drawText("sans", 9, gnuplot.mousex-len, gnuplot.mousey, click); + } else { + ctx.drawText("sans", 9, gnuplot.mousex, gnuplot.mousey, click); + } + } + + // Save starting corner of zoom box + else { + gnuplot.zoom_temp_xmin = x; + gnuplot.zoom_temp_ymin = y; + if (gnuplot.plot_axis_x2min != "none") gnuplot.zoom_temp_x2min = x2; + if (gnuplot.plot_axis_y2min != "none") gnuplot.zoom_temp_y2min = y2; + // Only used to echo the zoom box interactively + gnuplot.zoom_temp_plotx = gnuplot.plotx; + gnuplot.zoom_temp_ploty = gnuplot.ploty; + gnuplot.zoom_in_progress = true; + gnuplot.full_canvas_image = null; + } + return false; // Nobody else should respond to this event +} + +gnuplot.zoom_in = function (event) +{ + if (!gnuplot.zoom_in_progress) + return false; + + gnuplot.mouse_update(event); + + var button; + + if (event.which == null) /* IE case */ + button= (event.button < 2) ? "LEFT" : ((event.button == 4) ? "MIDDLE" : "RIGHT"); + else /* All others */ + button= (event.which < 2) ? "LEFT" : ((event.which == 2) ? "MIDDLE" : "RIGHT"); + + // Save ending corner of zoom box + if (button != "LEFT") { + if (x > gnuplot.zoom_temp_xmin) { + gnuplot.zoom_axis_xmin = gnuplot.zoom_temp_xmin; + gnuplot.zoom_axis_xmax = x; + if (gnuplot.plot_axis_x2min != "none") { + gnuplot.zoom_axis_x2min = gnuplot.zoom_temp_x2min; + gnuplot.zoom_axis_x2max = x2; + } + } else { + gnuplot.zoom_axis_xmin = x; + gnuplot.zoom_axis_xmax = gnuplot.zoom_temp_xmin; + if (gnuplot.plot_axis_x2min != "none") { + gnuplot.zoom_axis_x2min = x2; + gnuplot.zoom_axis_x2max = gnuplot.zoom_temp_x2min; + } + } + if (y > gnuplot.zoom_temp_ymin) { + gnuplot.zoom_axis_ymin = gnuplot.zoom_temp_ymin; + gnuplot.zoom_axis_ymax = y; + if (gnuplot.plot_axis_y2min != "none") { + gnuplot.zoom_axis_y2min = gnuplot.zoom_temp_y2min; + gnuplot.zoom_axis_y2max = y2; + } + } else { + gnuplot.zoom_axis_ymin = y; + gnuplot.zoom_axis_ymax = gnuplot.zoom_temp_ymin; + if (gnuplot.plot_axis_y2min != "none") { + gnuplot.zoom_axis_y2min = y2; + gnuplot.zoom_axis_y2max = gnuplot.zoom_temp_y2min; + } + } + gnuplot.zoom_axis_width = gnuplot.zoom_axis_xmax - gnuplot.zoom_axis_xmin; + gnuplot.zoom_axis_height = gnuplot.zoom_axis_ymax - gnuplot.zoom_axis_ymin; + gnuplot.zoom_in_progress = false; + gnuplot.rezoom(event); + } + return false; // Nobody else should respond to this event +} + +gnuplot.toggle_grid = function (e) +{ + if (!gnuplot.grid_lines) gnuplot.grid_lines = true; + else gnuplot.grid_lines = false; + ctx.clearRect(0,0,gnuplot.plot_term_xmax,gnuplot.plot_term_ymax); + gnuplot.display_is_uptodate = false; + gnuplot_canvas(); +} + +gnuplot.toggle_zoom_text = function (e) +{ + if (!gnuplot.zoom_text) gnuplot.zoom_text = true; + else gnuplot.zoom_text = false; + ctx.clearRect(0,0,gnuplot.plot_term_xmax,gnuplot.plot_term_ymax); + gnuplot.display_is_uptodate = false; + gnuplot_canvas(); +} + +gnuplot.rezoom = function (e) +{ + if (gnuplot.zoom_axis_width > 0) + gnuplot.zoomed = true; + ctx.clearRect(0,0,gnuplot.plot_term_xmax,gnuplot.plot_term_ymax); + gnuplot.display_is_uptodate = false; + gnuplot_canvas(); +} + +gnuplot.unzoom = function (e) +{ + gnuplot.zoomed = false; + ctx.clearRect(0,0,gnuplot.plot_term_xmax,gnuplot.plot_term_ymax); + gnuplot.display_is_uptodate = false; + gnuplot_canvas(); +} + +gnuplot.zoomXY = function(x,y) +{ + var zoom = new Object; + var xreal, yreal; + + zoom.x = x; zoom.y = y; zoom.clip = false; + + if (gnuplot.plot_logaxis_x != 0) { + xreal = Math.log(gnuplot.plot_axis_xmax) - Math.log(gnuplot.plot_axis_xmin); + xreal = Math.log(gnuplot.plot_axis_xmin) + (x - gnuplot.plot_xmin) * xreal/gnuplot.plot_width; + zoom.x = Math.log(gnuplot.zoom_axis_xmax) - Math.log(gnuplot.zoom_axis_xmin); + zoom.x = gnuplot.plot_xmin + (xreal - Math.log(gnuplot.zoom_axis_xmin)) * gnuplot.plot_width/zoom.x; + } else { + xreal = gnuplot.plot_axis_xmin + (x - gnuplot.plot_xmin) * (gnuplot.plot_axis_width/gnuplot.plot_width); + zoom.x = gnuplot.plot_xmin + (xreal - gnuplot.zoom_axis_xmin) * (gnuplot.plot_width/gnuplot.zoom_axis_width); + } + if (gnuplot.plot_logaxis_y != 0) { + yreal = Math.log(gnuplot.plot_axis_ymax) - Math.log(gnuplot.plot_axis_ymin); + yreal = Math.log(gnuplot.plot_axis_ymin) + (gnuplot.plot_ybot - y) * yreal/gnuplot.plot_height; + zoom.y = Math.log(gnuplot.zoom_axis_ymax) - Math.log(gnuplot.zoom_axis_ymin); + zoom.y = gnuplot.plot_ybot - (yreal - Math.log(gnuplot.zoom_axis_ymin)) * gnuplot.plot_height/zoom.y; + } else { + yreal = gnuplot.plot_axis_ymin + (gnuplot.plot_ybot - y) * (gnuplot.plot_axis_height/gnuplot.plot_height); + zoom.y = gnuplot.plot_ybot - (yreal - gnuplot.zoom_axis_ymin) * (gnuplot.plot_height/gnuplot.zoom_axis_height); + } + + // Report unclipped coords also + zoom.xraw = zoom.x; zoom.yraw = zoom.y; + + // Limit the zoomed plot to the original plot area + if (x > gnuplot.plot_xmax) { + zoom.x = x; + if (gnuplot.plot_axis_y2min == "none") { + zoom.y = y; + return zoom; + } + if (gnuplot.plot_ybot <= y && y <= gnuplot.plot_ybot + 15) + zoom.clip = true; + } + + else if (x < gnuplot.plot_xmin) + zoom.x = x; + else if (zoom.x < gnuplot.plot_xmin) + { zoom.x = gnuplot.plot_xmin; zoom.clip = true; } + else if (zoom.x > gnuplot.plot_xmax) + { zoom.x = gnuplot.plot_xmax; zoom.clip = true; } + + if (y < gnuplot.plot_ytop) { + zoom.y = y; + if (gnuplot.plot_axis_x2min == "none") { + zoom.x = x; zoom.clip = false; + return zoom; + } + } + + else if (y > gnuplot.plot_ybot) + zoom.y = y; + else if (zoom.y > gnuplot.plot_ybot) + { zoom.y = gnuplot.plot_ybot; zoom.clip = true; } + else if (zoom.y < gnuplot.plot_ytop) + { zoom.y = gnuplot.plot_ytop; zoom.clip = true; } + + return zoom; +} + +gnuplot.zoomW = function (w) { return (w*gnuplot.plot_axis_width/gnuplot.zoom_axis_width); } +gnuplot.zoomH = function (h) { return (h*gnuplot.plot_axis_height/gnuplot.zoom_axis_height); } + +gnuplot.popup_help = function(URL) { + if (typeof(URL) != "string") { + if (typeof(gnuplot.help_URL) == "string") + URL = gnuplot.help_URL; + else + return; + } + // FIXME: Placeholder for useful action + if (URL != "") + window.open (URL,"gnuplot help"); +} + +gnuplot.toggle_plot = function(plotid) { + if (typeof(gnuplot["hide_"+plotid]) == "unknown") + gnuplot["hide_"+plotid] = false; + gnuplot["hide_"+plotid] = !gnuplot["hide_"+plotid]; + ctx.clearRect(0,0,gnuplot.plot_term_xmax,gnuplot.plot_term_ymax); + gnuplot.display_is_uptodate = false; + gnuplot_canvas(); +} + +gnuplot.do_hotkey = function(event) { + keychar = String.fromCharCode(event.charCode ? event.charCode : event.keyCode); + switch (keychar) { + case 'e': ctx.clearRect(0,0,gnuplot.plot_term_xmax,gnuplot.plot_term_ymax); + gnuplot.display_is_uptodate = false; + gnuplot_canvas(); + break; + case 'g': gnuplot.toggle_grid(); + break; + case 'n': gnuplot.rezoom(); + break; + case 'r': + ctx.lineWidth = 0.5; + ctx.strokeStyle="rgba(128,128,128,0.50)"; + ctx.moveTo(gnuplot.plot_xmin, gnuplot.mousey); ctx.lineTo(gnuplot.plot_xmax, gnuplot.mousey); + ctx.moveTo(gnuplot.mousex, gnuplot.plot_ybot); ctx.lineTo(gnuplot.mousex, gnuplot.plot_ytop); + ctx.stroke(); + break; + case 'p': + case 'u': gnuplot.unzoom(); + break; + case '': zoom_in_progress = false; + break; + +// Arrow keys + case '%': // ctx.drawText("sans", 10, gnuplot.mousex, gnuplot.mousey, "<"); + break; + case '\'': // ctx.drawText("sans", 10, gnuplot.mousex, gnuplot.mousey, ">"); + break; + case '&': // ctx.drawText("sans", 10, gnuplot.mousex, gnuplot.mousey, "^"); + break; + case '(': // ctx.drawText("sans", 10, gnuplot.mousex, gnuplot.mousey, "v"); + break; + + default: ctx.drawText("sans", 10, gnuplot.mousex, gnuplot.mousey, keychar); + return true; // Let someone else handle it + break; + } + return false; // Nobody else should respond to this keypress +} diff --git a/src/netdaemon/www/js/gnuplot_svg.js b/src/netdaemon/www/js/gnuplot_svg.js new file mode 100644 index 0000000..e17c496 --- /dev/null +++ b/src/netdaemon/www/js/gnuplot_svg.js @@ -0,0 +1,376 @@ +// Javascript routines for interaction with SVG documents produced by +// gnuplot's SVG terminal driver. + +// Find your root SVG element +var svg = document.querySelector('svg'); + +// Create an SVGPoint for future math +var pt = svg.createSVGPoint(); + +// Get point in global SVG space +function cursorPoint(evt){ + pt.x = evt.clientX; pt.y = evt.clientY; + return pt.matrixTransform(svg.getScreenCTM().inverse()); +} + +var gnuplot_svg = { }; + +gnuplot_svg.version = "17 February 2017"; + +gnuplot_svg.SVGDoc = null; +gnuplot_svg.SVGRoot = null; + +gnuplot_svg.Init = function(e) +{ + gnuplot_svg.SVGDoc = e.target.ownerDocument; + gnuplot_svg.SVGRoot = gnuplot_svg.SVGDoc.documentElement; + gnuplot_svg.axisdate = new Date(); +} + +gnuplot_svg.toggleVisibility = function(evt, targetId) +{ + var newTarget = evt.target; + if (targetId) + newTarget = gnuplot_svg.SVGDoc.getElementById(targetId); + + var newValue = newTarget.getAttributeNS(null, 'visibility') + + if ('hidden' != newValue) + newValue = 'hidden'; + else + newValue = 'visible'; + + newTarget.setAttributeNS(null, 'visibility', newValue); + + if (targetId) { + newTarget = gnuplot_svg.SVGDoc.getElementById(targetId.concat("_keyentry")); + if (newTarget) + newTarget.setAttributeNS(null, 'style', + newValue == 'hidden' ? 'filter:url(#greybox)' : 'none'); + } + + evt.preventDefault(); + evt.stopPropagation(); +} + +// Mouse tracking echos coordinates to a floating text box + +gnuplot_svg.getText = function() { + return(document.getElementById("coord_text")); +} + +gnuplot_svg.updateCoordBox = function(t, evt) { + /* + * Apply screen CTM transformation to the evt screenX and screenY to get + * coordinates in SVG coordinate space. Use scaling parameters stored in + * the plot document by gnuplot to convert further into plot coordinates. + * Then position the floating text box using the SVG coordinates. + */ + var m = document.documentElement.getScreenCTM(); + var p = document.documentElement.createSVGPoint(); + var loc = cursorPoint(evt); + p.x = loc.x; + p.y = loc.y; + var label_x, label_y; + + // Allow for scrollbar position (Firefox, others?) + if (typeof evt.pageX != 'undefined') { + p.x = evt.pageX; p.y = evt.pageY; + } + t.setAttribute("x", p.x); + t.setAttribute("y", p.y); + + var plotcoord = gnuplot_svg.mouse2plot(p.x,p.y); + + if (gnuplot_svg.plot_timeaxis_x == "DMS" || gnuplot_svg.plot_timeaxis_y == "DMS") { + if (gnuplot_svg.plot_timeaxis_x == "DMS") + label_x = gnuplot_svg.convert_to_DMS(x); + else + label_x = plotcoord.x.toFixed(2); + if (gnuplot_svg.plot_timeaxis_y == "DMS") + label_y = gnuplot_svg.convert_to_DMS(y); + else + label_y = plotcoord.y.toFixed(2); + + } else if (gnuplot_svg.polar_mode) { + polar = gnuplot_svg.convert_to_polar(plotcoord.x,plotcoord.y); + label_x = "ang= " + polar.ang.toPrecision(4); + label_y = "R= " + polar.r.toPrecision(4); + + } else if (gnuplot_svg.plot_timeaxis_x == "Date") { + gnuplot_svg.axisdate.setTime(1000. * plotcoord.x); + var year = gnuplot_svg.axisdate.getUTCFullYear(); + var month = gnuplot_svg.axisdate.getUTCMonth(); + var date = gnuplot_svg.axisdate.getUTCDate(); + label_x = (" " + date).slice (-2) + "/" + + ("0" + (month+1)).slice (-2) + "/" + + year; + label_y = plotcoord.y.toFixed(2); + } else if (gnuplot_svg.plot_timeaxis_x == "Time") { + gnuplot_svg.axisdate.setTime(1000. * plotcoord.x); + var hour = gnuplot_svg.axisdate.getUTCHours(); + var minute = gnuplot_svg.axisdate.getUTCMinutes(); + var second = gnuplot_svg.axisdate.getUTCSeconds(); + label_x = ("0" + hour).slice (-2) + ":" + + ("0" + minute).slice (-2) + ":" + + ("0" + second).slice (-2); + label_y = plotcoord.y.toFixed(2); + } else if (gnuplot_svg.plot_timeaxis_x == "DateTime") { + gnuplot_svg.axisdate.setTime(1000. * plotcoord.x); + label_x = gnuplot_svg.axisdate.toUTCString(); + label_y = plotcoord.y.toFixed(2); + } else { + label_x = plotcoord.x.toFixed(2); + label_y = plotcoord.y.toFixed(2); + } + + while (null != t.firstChild) { + t.removeChild(t.firstChild); + } + var textNode = document.createTextNode(". "+label_x+" "+label_y); + t.appendChild(textNode); +} + +gnuplot_svg.showCoordBox = function(evt) { + var t = gnuplot_svg.getText(); + if (null != t) { + t.setAttribute("visibility", "visible"); + gnuplot_svg.updateCoordBox(t, evt); + } +} + +gnuplot_svg.moveCoordBox = function(evt) { + var t = gnuplot_svg.getText(); + if (null != t) + gnuplot_svg.updateCoordBox(t, evt); +} + +gnuplot_svg.hideCoordBox = function(evt) { + var t = gnuplot_svg.getText(); + if (null != t) + t.setAttribute("visibility", "hidden"); +} + +gnuplot_svg.toggleCoordBox = function(evt) { + var t = gnuplot_svg.getText(); + if (null != t) { + var state = t.getAttribute('visibility'); + if ('hidden' != state) + state = 'hidden'; + else + state = 'visible'; + t.setAttribute('visibility', state); + } +} + +gnuplot_svg.toggleGrid = function() { + if (!gnuplot_svg.SVGDoc.getElementsByClassName) // Old browsers + return; + var grid = gnuplot_svg.SVGDoc.getElementsByClassName('gridline'); + for (var i=0; i 0) { + gnuplot_svg.showHyperimage(evt, lines[0]); + lines[0] = lines[0].slice(nameindex+1); + } + } + + var loc = cursorPoint(evt); + var anchor_x = loc.x; + var anchor_y = loc.y; + + var hypertextbox = document.getElementById("hypertextbox") + hypertextbox.setAttributeNS(null,"x",anchor_x+10); + hypertextbox.setAttributeNS(null,"y",anchor_y+4); + hypertextbox.setAttributeNS(null,"visibility","visible"); + + var hypertext = document.getElementById("hypertext") + hypertext.setAttributeNS(null,"x",anchor_x+14); + hypertext.setAttributeNS(null,"y",anchor_y+18); + hypertext.setAttributeNS(null,"visibility","visible"); + + var height = 2+16*lines.length; + hypertextbox.setAttributeNS(null,"height",height); + var length = hypertext.getComputedTextLength(); + hypertextbox.setAttributeNS(null,"width",length+8); + + // bounce off frame bottom + if (anchor_y > gnuplot_svg.plot_ybot + 16 - height) { + anchor_y -= height; + hypertextbox.setAttributeNS(null,"y",anchor_y+4); + hypertext.setAttributeNS(null,"y",anchor_y+18); + } + + while (null != hypertext.firstChild) { + hypertext.removeChild(hypertext.firstChild); + } + + var textNode = document.createTextNode(lines[0]); + + if (lines.length <= 1) { + hypertext.appendChild(textNode); + } else { + xmlns="http://www.w3.org/2000/svg"; + var tspan_element = document.createElementNS(xmlns, "tspan"); + tspan_element.appendChild(textNode); + hypertext.appendChild(tspan_element); + length = tspan_element.getComputedTextLength(); + var ll = length; + + for (var l=1; l gnuplot_svg.plot_xmax + 14 - length) { + anchor_x -= length; + hypertextbox.setAttributeNS(null,"x",anchor_x+10); + hypertext.setAttributeNS(null,"x",anchor_x+14); + } + + // left-justify multiline text + var tspan_element = hypertext.firstChild; + while (tspan_element) { + tspan_element.setAttributeNS(null,"x",anchor_x+14); + tspan_element = tspan_element.nextElementSibling; + } + +} + +gnuplot_svg.hideHypertext = function () +{ + var hypertextbox = document.getElementById("hypertextbox") + var hypertext = document.getElementById("hypertext") + var hyperimage = document.getElementById("hyperimage") + hypertextbox.setAttributeNS(null,"visibility","hidden"); + hypertext.setAttributeNS(null,"visibility","hidden"); + hyperimage.setAttributeNS(null,"visibility","hidden"); +} + +gnuplot_svg.showHyperimage = function(evt, linktext) +{ + var loc = cursorPoint(evt); + var anchor_x = loc.x; + var anchor_y = loc.y; + // Allow for scrollbar position (Firefox, others?) + if (typeof evt.pageX != 'undefined') { + anchor_x = evt.pageX; anchor_y = evt.pageY; + } + + var hyperimage = document.getElementById("hyperimage") + hyperimage.setAttributeNS(null,"x",anchor_x); + hyperimage.setAttributeNS(null,"y",anchor_y); + hyperimage.setAttributeNS(null,"visibility","visible"); + + // Pick up height and width from "image(width,height):name" + var width = hyperimage.getAttributeNS(null,"width"); + var height = hyperimage.getAttributeNS(null,"height"); + if (linktext.charAt(5) == "(") { + width = parseInt(linktext.slice(6)); + height = parseInt(linktext.slice(linktext.indexOf(",") + 1)); + hyperimage.setAttributeNS(null,"width",width); + hyperimage.setAttributeNS(null,"height",height); + hyperimage.setAttributeNS(null,"preserveAspectRatio","none"); + } + + // bounce off frame bottom and right + if (anchor_y > gnuplot_svg.plot_ybot + 50 - height) + hyperimage.setAttributeNS(null,"y",20 + anchor_y-height); + if (anchor_x > gnuplot_svg.plot_xmax + 150 - width) + hyperimage.setAttributeNS(null,"x",10 + anchor_x-width); + + // attach image URL as a link + linktext = linktext.slice(linktext.indexOf(":") + 1); + var xlinkns = "http://www.w3.org/1999/xlink"; + hyperimage.setAttributeNS(xlinkns,"xlink:href",linktext); +} + +// Convert from svg panel mouse coordinates to the coordinate +// system of the gnuplot figure +gnuplot_svg.mouse2plot = function(mousex,mousey) { + var plotcoord = new Object; + var plotx = mousex - gnuplot_svg.plot_xmin; + var ploty = mousey - gnuplot_svg.plot_ybot; + var x,y; + + if (gnuplot_svg.plot_logaxis_x != 0) { + x = Math.log(gnuplot_svg.plot_axis_xmax) + - Math.log(gnuplot_svg.plot_axis_xmin); + x = x * (plotx / (gnuplot_svg.plot_xmax - gnuplot_svg.plot_xmin)) + + Math.log(gnuplot_svg.plot_axis_xmin); + x = Math.exp(x); + } else { + x = gnuplot_svg.plot_axis_xmin + (plotx / (gnuplot_svg.plot_xmax-gnuplot_svg.plot_xmin)) * (gnuplot_svg.plot_axis_xmax - gnuplot_svg.plot_axis_xmin); + } + + if (gnuplot_svg.plot_logaxis_y != 0) { + y = Math.log(gnuplot_svg.plot_axis_ymax) + - Math.log(gnuplot_svg.plot_axis_ymin); + y = y * (ploty / (gnuplot_svg.plot_ytop - gnuplot_svg.plot_ybot)) + + Math.log(gnuplot_svg.plot_axis_ymin); + y = Math.exp(y); + } else { + y = gnuplot_svg.plot_axis_ymin + (ploty / (gnuplot_svg.plot_ytop-gnuplot_svg.plot_ybot)) * (gnuplot_svg.plot_axis_ymax - gnuplot_svg.plot_axis_ymin); + } + + plotcoord.x = x; + plotcoord.y = y; + return plotcoord; +} + +gnuplot_svg.convert_to_polar = function (x,y) +{ + polar = new Object; + var phi, r; + phi = Math.atan2(y,x); + if (gnuplot_svg.plot_logaxis_r) + r = Math.exp( (x/Math.cos(phi) + Math.log(gnuplot_svg.plot_axis_rmin)/Math.LN10) * Math.LN10); + else if (gnuplot_svg.plot_axis_rmin > gnuplot_svg.plot_axis_rmax) + r = gnuplot_svg.plot_axis_rmin - x/Math.cos(phi); + else + r = gnuplot_svg.plot_axis_rmin + x/Math.cos(phi); + phi = phi * (180./Math.PI); + if (gnuplot_svg.polar_sense < 0) + phi = -phi; + if (gnuplot_svg.polar_theta0 != undefined) + phi = phi + gnuplot_svg.polar_theta0; + if (phi > 180.) + phi = phi - 360.; + polar.r = r; + polar.ang = phi; + return polar; +} + +gnuplot_svg.convert_to_DMS = function (x) +{ + var dms = {d:0, m:0, s:0}; + var deg = Math.abs(x); + dms.d = Math.floor(deg); + dms.m = Math.floor((deg - dms.d) * 60.); + dms.s = Math.floor((deg - dms.d) * 3600. - dms.m * 60.); + fmt = ((x<0)?"-":" ") + + dms.d.toFixed(0) + "°" + + dms.m.toFixed(0) + "\"" + + dms.s.toFixed(0) + "'"; + return fmt; +} diff --git a/src/netdaemon/www/js/grid.png b/src/netdaemon/www/js/grid.png new file mode 100644 index 0000000..67dc559 Binary files /dev/null and b/src/netdaemon/www/js/grid.png differ diff --git a/src/netdaemon/www/js/help.png b/src/netdaemon/www/js/help.png new file mode 100644 index 0000000..8a9146c Binary files /dev/null and b/src/netdaemon/www/js/help.png differ diff --git a/src/netdaemon/www/js/nextzoom.png b/src/netdaemon/www/js/nextzoom.png new file mode 100644 index 0000000..afa463a Binary files /dev/null and b/src/netdaemon/www/js/nextzoom.png differ diff --git a/src/netdaemon/www/js/previouszoom.png b/src/netdaemon/www/js/previouszoom.png new file mode 100644 index 0000000..1536c4b Binary files /dev/null and b/src/netdaemon/www/js/previouszoom.png differ diff --git a/src/netdaemon/www/js/return.png b/src/netdaemon/www/js/return.png new file mode 100644 index 0000000..c69a77a Binary files /dev/null and b/src/netdaemon/www/js/return.png differ diff --git a/src/netdaemon/www/js/textzoom.png b/src/netdaemon/www/js/textzoom.png new file mode 100644 index 0000000..186869c Binary files /dev/null and b/src/netdaemon/www/js/textzoom.png differ diff --git a/src/netdaemon/www/plot b/src/netdaemon/www/plot new file mode 100755 index 0000000..002ab64 --- /dev/null +++ b/src/netdaemon/www/plot @@ -0,0 +1,48 @@ +#!/bin/bash +OUT=tmpfile.txt +cat $1 > $OUT +VAL=$(head -n1 $1 | awk '{print $3}') +NM=$(basename $1) +if [ $NM = "T0" ]; then Tname="TOP side" +else Tname="BOTTOM side" +fi +echo -e "30\t30\t$VAL\n-30\t-30\t$VAL" >> $OUT +cat << EOF > gnutplt +#!/usr/bin/gnuplot +set contour +unset surface +set cntrparam order 4 +set cntrparam bspline +#set cntrparam levels auto 6 +#set cntrparam levels incremental -30,0.1,30 +set view map +set size square +set xrange [-40:40] +set yrange [-40:40] +set dgrid3d 100,100,4 +set table "contour.txt" +splot '$OUT' u 1:2:3 +unset table +unset contour +set surface +set table "dgrid.txt" +splot '$OUT' u 1:2:3 +unset table +reset +set terminal canvas enhanced mousing size 1024,768 jsdir 'js' +set output "$1.html" +set size square +set xrange [-30:30] +set yrange [-30:30] +set xlabel "X, dm" +set ylabel "Y, dm" +set title "Mirror temperature $NM for $2 ($Tname)" +set pm3d map +unset key +circle(x,y,z) = x**2+y**2 > 900 ? NaN : z +splot 'dgrid.txt' u 1:2:(circle(\$1,\$2,\$3)) w pm3d, 'contour.txt' u 1:2:(circle(\$1,\$2,\$3)) w l lc rgb "black", '$OUT' u 1:2:(circle(\$1,\$2,\$3)):3 with labels font ",8" +EOF + +chmod 755 gnutplt +./gnutplt +rm gnutplt dgrid.txt contour.txt tmpfile.txt 2>&1 > /dev/null