mirror of
https://github.com/eddyem/BTA_utils.git
synced 2025-12-06 02:35:13 +03:00
copy
This commit is contained in:
parent
8ed4972ce3
commit
99bc79d44e
140
daemon.c
Normal file
140
daemon.c
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* daemon.c - functions for running in background like a daemon
|
||||
*
|
||||
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define PROC_BASE "/proc"
|
||||
|
||||
#include <stdio.h> // printf, fopen, ...
|
||||
#include <unistd.h> // getpid
|
||||
#include <stdio.h> // perror
|
||||
#include <sys/types.h> // opendir
|
||||
#include <dirent.h> // opendir
|
||||
#include <sys/stat.h> // stat
|
||||
#include <fcntl.h> // fcntl
|
||||
#include <stdlib.h> // exit
|
||||
#include <string.h> // memset
|
||||
|
||||
/**
|
||||
* 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 *readname(pid_t pid){
|
||||
static char name[256];
|
||||
char *pp = name, byte, path[256];
|
||||
FILE *file;
|
||||
int cntr = 0;
|
||||
size_t sz;
|
||||
snprintf (path, 255, 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++ < 255);
|
||||
name[cntr] = 0;
|
||||
fclose(file);
|
||||
return name;
|
||||
}
|
||||
|
||||
void iffound_default(pid_t pid){
|
||||
fprintf(stderr, "\nFound running process (pid=%d), exit.\n", pid);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 argv - argument of main() or NULL for non-locking, call this function before getopt()
|
||||
* @param pidfilename - name of pidfile or NULL if none
|
||||
* @param iffound - action to run if file found or NULL for exit(0)
|
||||
*/
|
||||
void check4running(char **argv, char *pidfilename, void (*iffound)(pid_t pid)){
|
||||
DIR *dir;
|
||||
FILE *pidfile, *fself;
|
||||
struct dirent *de;
|
||||
struct stat s_buf;
|
||||
pid_t pid = 0, self;
|
||||
struct flock fl;
|
||||
char *name, *myname;
|
||||
if(!iffound) iffound = iffound_default;
|
||||
if(argv){ // block self
|
||||
fself = fopen(argv[0], "r"); // open self binary to lock
|
||||
memset(&fl, 0, sizeof(struct flock));
|
||||
fl.l_type = F_WRLCK;
|
||||
if(fcntl(fileno(fself), F_GETLK, &fl) == -1){ // check locking
|
||||
perror("fcntl");
|
||||
exit(1);
|
||||
}
|
||||
if(fl.l_type != F_UNLCK){ // file is locking - exit
|
||||
printf("Found locker, PID = %d!\n", fl.l_pid);
|
||||
exit(1);
|
||||
}
|
||||
fl.l_type = F_RDLCK;
|
||||
if(fcntl(fileno(fself), F_SETLKW, &fl) == -1){
|
||||
perror("fcntl");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
self = getpid(); // get self PID
|
||||
if(!(dir = opendir(PROC_BASE))){ // open /proc directory
|
||||
perror(PROC_BASE);
|
||||
exit(1);
|
||||
}
|
||||
if(!(name = readname(self))){ // error reading self name
|
||||
perror("Can't read self name");
|
||||
exit(1);
|
||||
}
|
||||
myname = strdup(name);
|
||||
if(pidfilename && stat(pidfilename, &s_buf) == 0){ // pidfile exists
|
||||
pidfile = fopen(pidfilename, "r");
|
||||
if(pidfile){
|
||||
fscanf(pidfile, "%d", &pid); // read PID of (possibly) running process
|
||||
fclose(pidfile);
|
||||
if((name = readname(pid)) && strncmp(name, myname, 255) == 0)
|
||||
iffound(pid);
|
||||
}
|
||||
}
|
||||
// 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 = readname(pid)) && strncmp(name, myname, 255) == 0)
|
||||
iffound(pid);
|
||||
}
|
||||
closedir(dir);
|
||||
if(pidfilename){
|
||||
pidfile = fopen(pidfilename, "w");
|
||||
fprintf(pidfile, "%d\n", self); // write self PID to pidfile
|
||||
fclose(pidfile);
|
||||
}
|
||||
free(myname);
|
||||
}
|
||||
16
jsonbta/Makefile
Normal file
16
jsonbta/Makefile
Normal file
@ -0,0 +1,16 @@
|
||||
LOADLIBES = -lm -lcrypt
|
||||
SRCS = bta_json.c bta_print.c ../daemon.c
|
||||
CC = gcc
|
||||
DEFINES =
|
||||
CXX = gcc
|
||||
CPPFLAGS = -Wall -Werror $(DEFINES)
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
all : bta_json client_streaming
|
||||
$(OBJS): bta_json.h bta_shdata.h
|
||||
bta_json : $(OBJS)
|
||||
$(CC) $(CPPFLAGS) $(OBJS) $(LOADLIBES) -o bta_json
|
||||
client_streaming: client_streaming.o
|
||||
$(CC) $(CPPFLAGS) -lm -ljson client_streaming.o -o client_streaming
|
||||
clean:
|
||||
/bin/rm -f *.o *~
|
||||
|
||||
211
jsonbta/bta_json.c
Normal file
211
jsonbta/bta_json.c
Normal file
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* bta_json.c - create socket and reply bta data
|
||||
*
|
||||
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||
*
|
||||
* 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 <strings.h>
|
||||
#include "bta_json.h"
|
||||
|
||||
// wait for child to avoid zombies
|
||||
static void wait_for_child(int sig){
|
||||
pid_t child;
|
||||
while((child = waitpid(-1, NULL, WNOHANG)) > 0);
|
||||
}
|
||||
|
||||
// search a first word after needle without spaces
|
||||
char* stringscan(char *str, char *needle){
|
||||
char *a, *e;
|
||||
char *end = str + strlen(str);
|
||||
a = strstr(str, needle);
|
||||
if(!a) return NULL;
|
||||
a += strlen(needle);
|
||||
while (a < end && (*a == ' ' || *a == '\r' || *a == '\t' || *a == '\r')) a++;
|
||||
if(a >= end) return NULL;
|
||||
e = strchr(a, ' ');
|
||||
if(e) *e = 0;
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parce data received from client
|
||||
* In case of http request we send all avaiable data
|
||||
* In case of request from client socket, parce it
|
||||
* Socket's request have structure like "par1<del>par2<del>..."
|
||||
* where "pars" are names of bta_pars fields
|
||||
* <del> is delimeter: one of symbols " &\t\n"
|
||||
* @param buf - incoming data
|
||||
* @param L - length of buf
|
||||
* @param par - returned parameters structure
|
||||
*/
|
||||
void parce_incoming_buf(char *buf, size_t L, bta_pars *par){
|
||||
char *tok, *got;
|
||||
memset(par, 0, sizeof(bta_pars));
|
||||
DBG("got data: %s", buf);
|
||||
// http request - send all if get == "bta_par"
|
||||
// if get == "bta_par?a&b&c...", change buf to pars
|
||||
if((got = stringscan(buf, "GET"))){
|
||||
size_t LR = strlen(RESOURCE);
|
||||
if(strcmp(got, RESOURCE) == 0){
|
||||
par->ALL = true;
|
||||
return;
|
||||
}else if(strncmp(got, RESOURCE, LR) == 0) buf = &got[LR+1];
|
||||
else exit(-1); // wrong request
|
||||
}
|
||||
// request from socket -> check all parameters
|
||||
tok = strtok(buf, " &\t\n");
|
||||
if(!tok) return;
|
||||
do{
|
||||
#define checkpar(val) if(strcasecmp(tok, val) == 0){par->val = true; continue;}
|
||||
checkpar(vel);
|
||||
checkpar(diff);
|
||||
checkpar(corr);
|
||||
checkpar(mtime);
|
||||
checkpar(meteo);
|
||||
checkpar(target);
|
||||
checkpar(p2mode);
|
||||
checkpar(eqcoor);
|
||||
checkpar(telmode);
|
||||
checkpar(sidtime);
|
||||
checkpar(horcoor);
|
||||
checkpar(valsens);
|
||||
checkpar(telfocus);
|
||||
#undef checkpar
|
||||
}while((tok = strtok(NULL, " &\t\n")));
|
||||
}
|
||||
|
||||
void handle(int newsock){
|
||||
size_t buflen = 4095;
|
||||
char buffer[buflen + 1];
|
||||
bta_pars par;
|
||||
do{
|
||||
ssize_t readed = recv(newsock, buffer, buflen, 0);
|
||||
if(readed < 1) break; // client closed or error
|
||||
parce_incoming_buf(buffer, readed, &par);
|
||||
#ifdef EBUG
|
||||
#define checkpar(val) if(par.val){ fprintf(stderr, "par: %s\n", val); }
|
||||
if(par.ALL){ fprintf(stderr, "par: ALL\n"); }
|
||||
checkpar(vel);
|
||||
checkpar(diff);
|
||||
checkpar(corr);
|
||||
checkpar(mtime);
|
||||
checkpar(sidtime);
|
||||
checkpar(meteo);
|
||||
checkpar(target);
|
||||
checkpar(p2mode);
|
||||
checkpar(eqcoor);
|
||||
checkpar(telmode);
|
||||
checkpar(horcoor);
|
||||
checkpar(valsens);
|
||||
checkpar(telfocus);
|
||||
#undef checkpar
|
||||
#endif // EBUG
|
||||
make_JSON(newsock, &par);
|
||||
}while(!par.ALL);
|
||||
close(newsock);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv){
|
||||
int sock;
|
||||
struct sigaction sa;
|
||||
struct addrinfo hints, *res, *p;
|
||||
int reuseaddr = 1;
|
||||
check4running(argv, PIDFILE, NULL);
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
if(getaddrinfo(NULL, PORT, &hints, &res) != 0){
|
||||
perror("getaddrinfo");
|
||||
return 1;
|
||||
}
|
||||
struct sockaddr_in *ia = (struct sockaddr_in*)res->ai_addr;
|
||||
char str[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, &(ia->sin_addr), str, INET_ADDRSTRLEN);
|
||||
printf("port: %u, addr: %s\n", ntohs(ia->sin_port), str);
|
||||
// loop through all the results and bind to the first we can
|
||||
for(p = res; p != NULL; p = p->ai_next){
|
||||
if((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1){
|
||||
perror("socket");
|
||||
continue;
|
||||
}
|
||||
if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1){
|
||||
perror("setsockopt");
|
||||
return -1;
|
||||
}
|
||||
if(bind(sock, p->ai_addr, p->ai_addrlen) == -1){
|
||||
close(sock);
|
||||
perror("bind");
|
||||
continue;
|
||||
}
|
||||
break; // if we get here, we must have connected successfully
|
||||
}
|
||||
if(p == NULL){
|
||||
// looped off the end of the list with no successful bind
|
||||
fprintf(stderr, "failed to bind socket\n");
|
||||
exit(2);
|
||||
}
|
||||
// Listen
|
||||
if(listen(sock, BACKLOG) == -1) {
|
||||
perror("listen");
|
||||
return 1;
|
||||
}
|
||||
freeaddrinfo(res);
|
||||
// Set up the signal handler
|
||||
sa.sa_handler = wait_for_child;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART;
|
||||
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
|
||||
perror("sigaction");
|
||||
return 1;
|
||||
}
|
||||
// OK, all done, now we can daemonize
|
||||
#ifndef EBUG // daemonize only in release mode
|
||||
if(daemon(1, 0)){
|
||||
perror("daemon()");
|
||||
exit(1);
|
||||
}
|
||||
#endif // EBUG
|
||||
// Main loop
|
||||
while(1){
|
||||
struct sockaddr_in their_addr;
|
||||
socklen_t size = sizeof(struct sockaddr_in);
|
||||
int newsock = accept(sock, (struct sockaddr*)&their_addr, &size);
|
||||
int pid;
|
||||
if(newsock == -1) return 0;
|
||||
pid = fork();
|
||||
if(pid == 0){
|
||||
// Child process
|
||||
close(sock);
|
||||
handle(newsock);
|
||||
return 0;
|
||||
}else{
|
||||
// Parent process
|
||||
if(pid == -1) return 1;
|
||||
else{
|
||||
close(newsock);
|
||||
DBG("Create child: %d", pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
close(sock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// ËÏÎÅÃ ÆÁÊÌÁ
|
||||
92
jsonbta/bta_json.h
Normal file
92
jsonbta/bta_json.h
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* bta_json.h
|
||||
*
|
||||
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||
*
|
||||
* 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 __BTA_JSON_H__
|
||||
#define __BTA_JSON_H__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h> // exit
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <netdb.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define RESOURCE "/bta_par" // resource to request in http
|
||||
#define PORT "12345" // Port to listen on
|
||||
#define BACKLOG 10 // Passed to listen()
|
||||
#define PIDFILE "/tmp/btajson.pid" // PID file
|
||||
|
||||
#ifdef EBUG // debug mode
|
||||
#define DBG(...) do{fprintf(stderr, __VA_ARGS__); fprintf(stderr,"\n");}while(0)
|
||||
#else
|
||||
#define DBG(...)
|
||||
#endif
|
||||
|
||||
typedef struct{
|
||||
bool ALL;
|
||||
bool corr;
|
||||
bool diff;
|
||||
bool eqcoor;
|
||||
bool horcoor;
|
||||
bool meteo;
|
||||
bool mtime;
|
||||
bool p2mode;
|
||||
bool sidtime;
|
||||
bool target;
|
||||
bool telfocus;
|
||||
bool telmode;
|
||||
bool valsens;
|
||||
bool vel;
|
||||
} bta_pars;
|
||||
|
||||
// named parameters
|
||||
#ifndef BTA_PRINT_C
|
||||
#define defpar(val) const char* val = #val
|
||||
#else
|
||||
#define defpar(val) extern const char* val
|
||||
#endif
|
||||
defpar(mtime);
|
||||
defpar(sidtime);
|
||||
defpar(telmode);
|
||||
defpar(telfocus);
|
||||
defpar(target);
|
||||
defpar(p2mode);
|
||||
defpar(eqcoor);
|
||||
defpar(horcoor);
|
||||
defpar(valsens);
|
||||
defpar(diff);
|
||||
defpar(vel);
|
||||
defpar(corr);
|
||||
defpar(meteo);
|
||||
#undef defpar
|
||||
|
||||
void make_JSON(int sock, bta_pars *par); // bta_print.c
|
||||
void check4running(char **argv, char *pidfilename, void (*iffound)(pid_t pid)); // daemon.h
|
||||
|
||||
#endif // __BTA_JSON_H__
|
||||
|
||||
// ËÏÎÅÃ ÆÁÊÌÁ
|
||||
380
jsonbta/bta_print.c
Normal file
380
jsonbta/bta_print.c
Normal file
@ -0,0 +1,380 @@
|
||||
/*
|
||||
* bta_print.c
|
||||
*
|
||||
* Copyright Vladimir S. Shergin <vsher@sao.ru>
|
||||
*
|
||||
* some changes (2013) Edward V. Emelianoff <eddy@sao.ru>
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/times.h>
|
||||
#include <crypt.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "bta_shdata.h"
|
||||
#define BTA_PRINT_C
|
||||
#include "bta_json.h"
|
||||
|
||||
#define BUFSZ 255
|
||||
static char buf[BUFSZ+1];
|
||||
|
||||
char *double_asc(double d, char *fmt){
|
||||
if(!fmt) snprintf(buf, BUFSZ, "%.6f", d);
|
||||
else snprintf(buf, BUFSZ, fmt, d);
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *time_asc(double t){
|
||||
int h, min;
|
||||
double sec;
|
||||
h = (int)(t/3600.);
|
||||
min = (int)((t - (double)h*3600.)/60.);
|
||||
sec = t - (double)h*3600. - (double)min*60.;
|
||||
h %= 24;
|
||||
if(sec>59.99) sec=59.99;
|
||||
snprintf(buf, BUFSZ, "\"%02d:%02d:%05.2f\"", h,min,sec);
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *angle_asc(double a){
|
||||
char s;
|
||||
int d, min;
|
||||
double sec;
|
||||
if (a >= 0.)
|
||||
s = '+';
|
||||
else {
|
||||
s = '-'; a = -a;
|
||||
}
|
||||
d = (int)(a/3600.);
|
||||
min = (int)((a - (double)d*3600.)/60.);
|
||||
sec = a - (double)d*3600. - (double)min*60.;
|
||||
d %= 360;
|
||||
if(sec>59.9) sec=59.9;
|
||||
snprintf (buf, BUFSZ, "\"%c%02d:%02d:%04.1f\"", s,d,min,sec);
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *angle_fmt(double a, char *format){
|
||||
char s, *p;
|
||||
int d, min, n;
|
||||
double sec, msec;
|
||||
char *newformat = calloc(strlen(format) + 3, 1);
|
||||
assert(newformat);
|
||||
sprintf(newformat, "\"%s\"", format);
|
||||
if (a >= 0.)
|
||||
s = '+';
|
||||
else {
|
||||
s = '-'; a = -a;
|
||||
}
|
||||
d = (int)(a/3600.);
|
||||
min = (int)((a - (double)d*3600.)/60.);
|
||||
sec = a - (double)d*3600. - (double)min*60.;
|
||||
d %= 360;
|
||||
if ((p = strchr(format,'.')) == NULL)
|
||||
msec=59.;
|
||||
else if (*(p+2) == 'f' ) {
|
||||
n = *(p+1) - '0';
|
||||
msec = 60. - pow(10.,(double)(-n));
|
||||
} else
|
||||
msec=60.;
|
||||
if(sec>msec) sec=msec;
|
||||
if (strstr(format,"%c"))
|
||||
snprintf(buf, BUFSZ, newformat, s,d,min,sec);
|
||||
else
|
||||
snprintf(buf, BUFSZ, newformat, d,min,sec);
|
||||
free(newformat);
|
||||
return buf;
|
||||
}
|
||||
|
||||
#ifndef PI
|
||||
#define PI 3.14159265358979323846 /* pi */
|
||||
#endif
|
||||
|
||||
#define R2D 180./PI /* rad. to degr. */
|
||||
#define D2R PI/180. /* degr. to rad. */
|
||||
#define R2S 648000./PI /* rad. to sec */
|
||||
#define S2R PI/648000. /* sec. to rad. */
|
||||
#define S360 1296000. /* sec in 360degr */
|
||||
|
||||
const double longitude=149189.175; /* SAO longitude 41 26 29.175 (-2:45:45.945)*/
|
||||
const double Fi=157152.7; /* SAO latitude 43 39 12.7 */
|
||||
const double cos_fi=0.7235272793; /* Cos of SAO latitude */
|
||||
const double sin_fi=0.6902957888; /* Sin --- "" ----- */
|
||||
|
||||
static void calc_AZ(double alpha, double delta, double stime, double *az, double *zd){
|
||||
double sin_t,cos_t, sin_d,cos_d, cos_z;
|
||||
double t, d, z, a, x, y;
|
||||
|
||||
t = (stime - alpha) * 15.;
|
||||
if (t < 0.)
|
||||
t += S360; // +360degr
|
||||
t *= S2R; // -> rad
|
||||
d = delta * S2R;
|
||||
sin_t = sin(t);
|
||||
cos_t = cos(t);
|
||||
sin_d = sin(d);
|
||||
cos_d = cos(d);
|
||||
|
||||
cos_z = cos_fi * cos_d * cos_t + sin_fi * sin_d;
|
||||
z = acos(cos_z);
|
||||
|
||||
y = cos_d * sin_t;
|
||||
x = cos_d * sin_fi * cos_t - cos_fi * sin_d;
|
||||
a = atan2(y, x);
|
||||
|
||||
*zd = z * R2S;
|
||||
*az = a * R2S;
|
||||
}
|
||||
|
||||
static double calc_PA(double alpha, double delta, double stime){
|
||||
double sin_t,cos_t, sin_d,cos_d;
|
||||
double t, d, p, sp, cp;
|
||||
|
||||
t = (stime - alpha) * 15.;
|
||||
if (t < 0.)
|
||||
t += S360; // +360degr
|
||||
t *= S2R; // -> rad
|
||||
d = delta * S2R;
|
||||
sin_t = sin(t);
|
||||
cos_t = cos(t);
|
||||
sin_d = sin(d);
|
||||
cos_d = cos(d);
|
||||
|
||||
sp = sin_t * cos_fi;
|
||||
cp = sin_fi * cos_d - sin_d * cos_fi * cos_t;
|
||||
p = atan2(sp, cp);
|
||||
if (p < 0.0)
|
||||
p += 2.0*PI;
|
||||
|
||||
return(p * R2S);
|
||||
}
|
||||
|
||||
/*
|
||||
static void calc_AD(double az, double zd, double stime, double *alpha, double *delta){
|
||||
double sin_d, sin_a,cos_a, sin_z,cos_z;
|
||||
double t, d, z, a, p , x, y, s;
|
||||
a = az * S2R;
|
||||
z = zd * S2R;
|
||||
sin_a = sin(a);
|
||||
cos_a = cos(a);
|
||||
sin_z = sin(z);
|
||||
cos_z = cos(z);
|
||||
|
||||
y = sin_z * sin_a;
|
||||
x = cos_a * sin_fi * sin_z + cos_fi * cos_z;
|
||||
t = atan2(y, x);
|
||||
if (t < 0.0)
|
||||
t += 2.0*PI;
|
||||
|
||||
sin_d = sin_fi * cos_z - cos_fi * cos_a * sin_z;
|
||||
d = asin(sin_d);
|
||||
|
||||
*delta = d * R2S;
|
||||
*alpha = (stime - t * R2S / 15.);
|
||||
if (*alpha < 0.0)
|
||||
*alpha += S360/15.; // +24h
|
||||
}*/
|
||||
|
||||
void make_JSON(int sock, bta_pars *par){
|
||||
bool ALL = par->ALL;
|
||||
// print next JSON pair; par, val - strings
|
||||
void sendstr(char *str){
|
||||
size_t L = strlen(str);
|
||||
if(send(sock, str, L, 0) != L) exit(-1);
|
||||
}
|
||||
int json_send(char *par, char *val){
|
||||
char buf[256];
|
||||
int L = snprintf(buf, 255, ",\n\"%s\": %s", par, val);
|
||||
if(send(sock, buf, L, 0) != L) return -1;
|
||||
return 0;
|
||||
}
|
||||
int json_send_s(char *par, char *val){
|
||||
char buf[256];
|
||||
int L = snprintf(buf, 255, ",\n\"%s\": \"%s\"", par, val);
|
||||
if(send(sock, buf, L, 0) != L) return -1;
|
||||
return 0;
|
||||
}
|
||||
#define JSON(p, val) do{if(json_send(p, val)) exit(-1);} while(0)
|
||||
#define JSONSTR(p, val) do{if(json_send_s(p, val)) exit(-1);} while(0)
|
||||
get_shm_block( &sdat, ClientSide);
|
||||
char *str;
|
||||
if(!check_shm_block(&sdat)) exit(-1);
|
||||
// beginning of json object
|
||||
sendstr("{\n\"ACS_BTA\": true");
|
||||
|
||||
// mean local time
|
||||
if(ALL || par->mtime)
|
||||
JSON("M_time", time_asc(M_time+DUT1));
|
||||
// Mean Sidereal Time
|
||||
if(ALL || par->sidtime){
|
||||
#ifdef EE_time
|
||||
JSON("JDate", double_asc(JDate, NULL));
|
||||
str = time_asc(S_time-EE_time);
|
||||
#else
|
||||
str = time_asc(S_time);
|
||||
#endif
|
||||
JSON("S_time", str);
|
||||
}
|
||||
// Telecope mode
|
||||
if(ALL || par->telmode){
|
||||
if(Tel_Hardware == Hard_Off) str = "Off";
|
||||
else if(Tel_Mode != Automatic) str = "Manual";
|
||||
else{
|
||||
switch (Sys_Mode){
|
||||
default:
|
||||
case SysStop : str = "Stopping"; break;
|
||||
case SysWait : str = "Waiting"; break;
|
||||
case SysPointAZ :
|
||||
case SysPointAD : str = "Pointing"; break;
|
||||
case SysTrkStop :
|
||||
case SysTrkStart:
|
||||
case SysTrkMove :
|
||||
case SysTrkSeek : str = "Seeking"; break;
|
||||
case SysTrkOk : str = "Tracking"; break;
|
||||
case SysTrkCorr : str = "Correction";break;
|
||||
case SysTest : str = "Testing"; break;
|
||||
}
|
||||
}
|
||||
JSONSTR("Tel_Mode", str);
|
||||
}
|
||||
// Telescope focus
|
||||
if(ALL || par->telfocus){
|
||||
switch (Tel_Focus){
|
||||
default:
|
||||
case Prime : str = "Prime"; break;
|
||||
case Nasmyth1 : str = "Nasmyth1"; break;
|
||||
case Nasmyth2 : str = "Nasmyth2"; break;
|
||||
}
|
||||
JSONSTR("Tel_Focus", str);
|
||||
JSON("ValFoc", double_asc(val_F, "%0.2f"));
|
||||
}
|
||||
// Telescope target
|
||||
if(ALL || par->target){
|
||||
switch (Sys_Target) {
|
||||
default:
|
||||
case TagObject : str = "Object"; break;
|
||||
case TagPosition : str = "A/Z-Pos."; break;
|
||||
case TagNest : str = "Nest"; break;
|
||||
case TagZenith : str = "Zenith"; break;
|
||||
case TagHorizon : str = "Horizon"; break;
|
||||
}
|
||||
JSONSTR("Tel_Taget", str);
|
||||
}
|
||||
// Mode of P2
|
||||
if(ALL || par->p2mode){
|
||||
if(Tel_Hardware == Hard_On){
|
||||
switch (P2_State) {
|
||||
default:
|
||||
case P2_Off : str = "Stop"; break;
|
||||
case P2_On : str = "Track"; break;
|
||||
case P2_Plus : str = "Move+"; break;
|
||||
case P2_Minus : str = "Move-"; break;
|
||||
}
|
||||
} else str = "Off";
|
||||
JSONSTR("P2_Mode", str);
|
||||
}
|
||||
// Equatorial coordinates
|
||||
if(ALL || par->eqcoor){
|
||||
JSON("CurAlpha", time_asc(CurAlpha));
|
||||
JSON("CurDelta", angle_asc(CurDelta));
|
||||
JSON("SrcAlpha", time_asc(SrcAlpha));
|
||||
JSON("SrcDelta", angle_asc(SrcDelta));
|
||||
JSON("InpAlpha", time_asc(InpAlpha));
|
||||
JSON("InpDelta", angle_asc(InpDelta));
|
||||
JSON("TelAlpha", time_asc(val_Alp));
|
||||
JSON("TelDelta", angle_asc(val_Del));
|
||||
}
|
||||
// Horizontal coordinates
|
||||
if(ALL || par->horcoor){
|
||||
JSON("InpAzim", angle_fmt(InpAzim,"%c%03d:%02d:%04.1f"));
|
||||
JSON("InpZenD", angle_fmt(InpZdist,"%02d:%02d:%04.1f"));
|
||||
JSON("CurAzim", angle_fmt(tag_A,"%c%03d:%02d:%04.1f"));
|
||||
JSON("CurZenD", angle_fmt(tag_Z,"%02d:%02d:%04.1f"));
|
||||
JSON("CurPA", angle_fmt(tag_P,"%03d:%02d:%04.1f"));
|
||||
JSON("SrcPA", angle_fmt(calc_PA(SrcAlpha,SrcDelta,S_time),"%03d:%02d:%04.1f"));
|
||||
JSON("InpPA", angle_fmt(calc_PA(InpAlpha,InpDelta,S_time),"%03d:%02d:%04.1f"));
|
||||
JSON("TelPA", angle_fmt(calc_PA(val_Alp, val_Del, S_time),"%03d:%02d:%04.1f"));
|
||||
}
|
||||
// Values from sensors
|
||||
if(ALL || par->valsens){
|
||||
JSON("ValAzim", angle_fmt(val_A,"%c%03d:%02d:%04.1f"));
|
||||
JSON("ValZenD", angle_fmt(val_Z,"%02d:%02d:%04.1f"));
|
||||
JSON("ValP2", angle_fmt(val_P,"%03d:%02d:%04.1f"));
|
||||
JSON("ValDome", angle_fmt(val_D,"%c%03d:%02d:%04.1f"));
|
||||
}
|
||||
// Differences
|
||||
if(ALL || par->diff){
|
||||
JSON("DiffAzim", angle_fmt(Diff_A,"%c%03d:%02d:%04.1f"));
|
||||
JSON("DiffZenD", angle_fmt(Diff_Z,"%c%02d:%02d:%04.1f"));
|
||||
JSON("DiffP2", angle_fmt(Diff_P,"%c%03d:%02d:%04.1f"));
|
||||
JSON("DiffDome", angle_fmt(val_A-val_D,"%c%03d:%02d:%04.1f"));
|
||||
}
|
||||
// Velocities
|
||||
if(ALL || par->vel){
|
||||
JSON("VelAzim", angle_fmt(vel_A,"%c%02d:%02d:%04.1f"));
|
||||
JSON("VelZenD", angle_fmt(vel_Z,"%c%02d:%02d:%04.1f"));
|
||||
JSON("VelP2", angle_fmt(vel_P,"%c%02d:%02d:%04.1f"));
|
||||
JSON("VelPA", angle_fmt(vel_objP,"%c%02d:%02d:%04.1f"));
|
||||
JSON("VelDome", angle_fmt(vel_D,"%c%02d:%02d:%04.1f"));
|
||||
}
|
||||
// Correction
|
||||
if(ALL || par->corr){
|
||||
double curA,curZ,srcA,srcZ;
|
||||
double corAlp,corDel,corA,corZ;
|
||||
if(Sys_Mode==SysTrkSeek||Sys_Mode==SysTrkOk||Sys_Mode==SysTrkCorr){
|
||||
corAlp = CurAlpha-SrcAlpha;
|
||||
corDel = CurDelta-SrcDelta;
|
||||
if(corAlp > 23*3600.) corAlp -= 24*3600.;
|
||||
if(corAlp < -23*3600.) corAlp += 24*3600.;
|
||||
calc_AZ(SrcAlpha, SrcDelta, S_time, &srcA, &srcZ);
|
||||
calc_AZ(CurAlpha, CurDelta, S_time, &curA, &curZ);
|
||||
corA=curA-srcA;
|
||||
corZ=curZ-srcZ;
|
||||
}else{
|
||||
corAlp = corDel = corA = corZ = 0.;
|
||||
}
|
||||
JSON("CorrAlpha", angle_fmt(corAlp,"%c%01d:%02d:%05.2f"));
|
||||
JSON("CorrDelta", angle_fmt(corDel,"%c%01d:%02d:%04.1f"));
|
||||
JSON("CorrAzim", angle_fmt(corA,"%c%01d:%02d:%04.1f"));
|
||||
JSON("CorrZenD", angle_fmt(corZ,"%c%01d:%02d:%04.1f"));
|
||||
}
|
||||
// meteo
|
||||
if(ALL || par->meteo){
|
||||
JSON("ValTind", double_asc(val_T2, "%05.1f"));
|
||||
JSON("ValTmir", double_asc(val_T3, "%05.1f"));
|
||||
JSON("ValPres", double_asc(val_B, "%05.1f"));
|
||||
JSON("ValWind", double_asc(val_Wnd, "%04.1f"));
|
||||
if(Wnd10_time>0.1 && Wnd10_time<=M_time) {
|
||||
JSON("Blast10", double_asc((M_time-Wnd10_time)/60, "%.1f"));
|
||||
JSON("Blast15", double_asc((M_time-Wnd15_time)/60, "%.1f"));
|
||||
}
|
||||
JSON("ValHumd", double_asc(val_Hmd, "%04.1f"));
|
||||
if(Precip_time>0.1 && Precip_time<=M_time)
|
||||
JSON("Precipt", double_asc((M_time-Precip_time)/60, "%.1f"));
|
||||
}
|
||||
// end of json onject
|
||||
sendstr("\n}\n");
|
||||
}
|
||||
|
||||
|
||||
// ËÏÎÅÃ ÆÁÊÌÁ
|
||||
1159
jsonbta/bta_shdata.h
Normal file
1159
jsonbta/bta_shdata.h
Normal file
File diff suppressed because it is too large
Load Diff
61
jsonbta/client.c
Normal file
61
jsonbta/client.c
Normal file
@ -0,0 +1,61 @@
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "bta_json.h"
|
||||
|
||||
int main(int argc, char *argv[]){
|
||||
int sockfd = 0, n = 0;
|
||||
char recvBuff[1024];
|
||||
memset(recvBuff, '0',sizeof(recvBuff));
|
||||
struct addrinfo h, *r, *p;
|
||||
memset(&h, 0, sizeof(h));
|
||||
h.ai_family = AF_INET;
|
||||
h.ai_socktype = SOCK_STREAM;
|
||||
h.ai_flags = AI_CANONNAME;
|
||||
char *host = "localhost";
|
||||
if(argc > 1) host = argv[1];
|
||||
char *port = PORT;
|
||||
if(argc > 2) port = argv[2];
|
||||
if(getaddrinfo(host, port, &h, &r)){perror("getaddrinfo"); return -1;}
|
||||
struct sockaddr_in *ia = (struct sockaddr_in*)r->ai_addr;
|
||||
char str[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, &(ia->sin_addr), str, INET_ADDRSTRLEN);
|
||||
printf("canonname: %s, port: %u, addr: %s\n", r->ai_canonname, ntohs(ia->sin_port), str);
|
||||
for(p = r; p; p = p->ai_next) {
|
||||
if ((sockfd = socket(p->ai_family, p->ai_socktype,
|
||||
p->ai_protocol)) == -1) {
|
||||
perror("socket");
|
||||
continue;
|
||||
}
|
||||
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
|
||||
close(sockfd);
|
||||
perror("connect");
|
||||
continue;
|
||||
}
|
||||
break; // if we get here, we must have connected successfully
|
||||
}
|
||||
if (p == NULL) {
|
||||
// looped off the end of the list with no connection
|
||||
fprintf(stderr, "failed to connect\n");
|
||||
return -1;
|
||||
}
|
||||
freeaddrinfo(r);
|
||||
char *msg = malloc(2048);
|
||||
char *res = RESOURCE;
|
||||
if(argc == 4) res = argv[3];
|
||||
snprintf(msg, 2047, "GET %s HTTP/1.1\r\n", res);
|
||||
if(send(sockfd, msg, strlen(msg), 0) != strlen(msg)){perror("send");}
|
||||
while((n = read(sockfd, recvBuff, sizeof(recvBuff)-1)) > 0){
|
||||
recvBuff[n] = 0;
|
||||
if(fputs(recvBuff, stdout) == EOF) printf("\n Error : Fputs error\n");
|
||||
}
|
||||
if(n < 0) printf("\n Read error \n");
|
||||
return 0;
|
||||
}
|
||||
337
jsonbta/client_streaming.c
Normal file
337
jsonbta/client_streaming.c
Normal file
@ -0,0 +1,337 @@
|
||||
/*
|
||||
* client_streaming.c - example of streaming client
|
||||
*
|
||||
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||
*
|
||||
* 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 <netdb.h> // addrinfo
|
||||
#include <stdio.h> // printf etc
|
||||
#include <string.h> // memset, strdup, strlen
|
||||
#include <stdlib.h> // exit
|
||||
#include <unistd.h> // close, read, sleep
|
||||
#include <fcntl.h> // fcntl
|
||||
#include <arpa/inet.h> // inet_ntop
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <json/json.h>
|
||||
|
||||
#define PORT "12345"
|
||||
#define RESOURSE "GET /bta_par HTTP/1.1\r\n"
|
||||
|
||||
typedef enum{
|
||||
T_STRING, // string
|
||||
T_DMS, // degrees:minutes:seconds
|
||||
T_HMS, // hours:minutes:seconds
|
||||
T_DOUBLE // double
|
||||
} otype;
|
||||
|
||||
/*
|
||||
#define defpar(val) const char* val = #val
|
||||
defpar(mtime);
|
||||
defpar(sidtime);
|
||||
defpar(telmode);
|
||||
defpar(telfocus);
|
||||
defpar(target);
|
||||
defpar(p2mode);
|
||||
defpar(eqcoor);
|
||||
defpar(horcoor);
|
||||
defpar(valsens);
|
||||
defpar(diff);
|
||||
defpar(vel);
|
||||
defpar(corr);
|
||||
defpar(meteo);
|
||||
#undef defpar
|
||||
*/
|
||||
/*
|
||||
{
|
||||
"M_time": "22:49:13.61",
|
||||
"JDate": 2456367.284184,
|
||||
"S_time": "09:09:07.34",
|
||||
"Tel_Mode": "Stopping",
|
||||
"Tel_Focus": "Prime",
|
||||
"ValFoc": 43.54,
|
||||
"Tel_Taget": "Nest",
|
||||
"P2_Mode": "Stop",
|
||||
"CurAlpha": "00:26:06.78",
|
||||
"CurDelta": "+25:34:34.0",
|
||||
"SrcAlpha": "06:03:08.53",
|
||||
"SrcDelta": "+30:00:00.0",
|
||||
"InpAlpha": "06:03:08.53",
|
||||
"InpDelta": "+30:00:00.0",
|
||||
"TelAlpha": "04:23:09.30",
|
||||
"TelDelta": "+25:34:41.3",
|
||||
"InpAzim": "+085:27:36.4",
|
||||
"InpZenD": "39:01:06.5",
|
||||
"CurAzim": "+136:27:11.0",
|
||||
"CurZenD": "97:21:25.3",
|
||||
"CurPA": "033:32:49.7",
|
||||
"SrcPA": "056:23:33.0",
|
||||
"InpPA": "056:23:33.0",
|
||||
"TelPA": "052:39:24.3",
|
||||
"ValAzim": "+097:37:52.4",
|
||||
"ValZenD": "59:37:00.2",
|
||||
"ValP2": "310:24:30.2",
|
||||
"ValDome": "+180:16:47.0",
|
||||
"DiffAzim": "+000:00:00.0",
|
||||
"DiffZenD": "+00:00:00.0",
|
||||
"DiffP2": "+000:00:00.6",
|
||||
"DiffDome": "-082:38:54.6",
|
||||
"VelAzim": "+00:00:00.0",
|
||||
"VelZenD": "-00:00:00.0",
|
||||
"VelP2": "+00:00:00.0",
|
||||
"VelPA": "+00:00:00.0",
|
||||
"VelDome": "+00:00:00.0",
|
||||
"CorrAlpha": "+0:00:00.00",
|
||||
"CorrDelta": "+0:00:00.0",
|
||||
"CorrAzim": "+0:00:00.0",
|
||||
"CorrZenD": "+0:00:00.0",
|
||||
"ValTind": +02.5,
|
||||
"ValTmir": +01.7,
|
||||
"ValPres": 590.3,
|
||||
"ValWind": 06.6,
|
||||
"Blast10": 1.4,
|
||||
"Blast15": 3.4,
|
||||
"ValHumd": 50.0
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get double parameter
|
||||
* @param jobj - json record with double parameter
|
||||
* @return NULL in case of error or allocated double value (MUST BE FREE LATER)
|
||||
*/
|
||||
double* get_jdouble(json_object *jobj){
|
||||
enum json_type type = json_object_get_type(jobj);
|
||||
double val, *ret;
|
||||
switch(type){
|
||||
case json_type_double:
|
||||
val = json_object_get_double(jobj);
|
||||
break;
|
||||
case json_type_int:
|
||||
val = json_object_get_int(jobj);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Wrong value! Get non-number!\n");
|
||||
return NULL;
|
||||
}
|
||||
ret = malloc(sizeof(double));
|
||||
assert(ret);
|
||||
memcpy(ret, &val, sizeof(double));
|
||||
return ret;
|
||||
}
|
||||
double strtod(const char *nptr, char **endptr);
|
||||
|
||||
char *atodbl(char *str, double *d){
|
||||
char *eptr;
|
||||
*d = strtod(str, &eptr);
|
||||
if(eptr == str) return NULL;
|
||||
return eptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* get string parameter
|
||||
* @param jobj - json record
|
||||
* @return string or NULL in case of error
|
||||
*/
|
||||
|
||||
char *get_jstr(json_object *jobj){
|
||||
enum json_type type = json_object_get_type(jobj);
|
||||
if(type != json_type_string) return NULL;
|
||||
return (char*)json_object_get_string(jobj);
|
||||
}
|
||||
|
||||
double *get_jdhms(json_object *jobj){
|
||||
char *jptr = get_jstr(jobj);
|
||||
char *str = jptr, *endln = str + strlen(str);
|
||||
double h,m,s, sgn;
|
||||
#define GetVal(x) do{if(str >= endln) return NULL; str = atodbl(str, &x); if(!str)return NULL; str++;}while(0)
|
||||
GetVal(h);
|
||||
GetVal(m);
|
||||
GetVal(s);
|
||||
#undef GetVal
|
||||
sgn = (h < 0.) ? -1. : 1.;
|
||||
h = fabs(h);
|
||||
double *ret = malloc(sizeof(double));
|
||||
assert(ret);
|
||||
*ret = sgn * (h + m / 60. + s / 3600.);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* get parameter from json object
|
||||
* @param jobj - json records
|
||||
* @param par - parameter to find
|
||||
* @param type - type of par
|
||||
* @return dinamycally allocated pointer to char (if T_STRING) or double (for other types);
|
||||
* if type is angle or time, its value converts from DMS/HMS to double
|
||||
* in case of parameter absense or error function returns NULL
|
||||
*/
|
||||
void *get_json_par(json_object *jobj, char *par, otype type){
|
||||
json_object *o = json_object_object_get(jobj, par);
|
||||
void *ret;
|
||||
if(!o) return NULL;
|
||||
switch(type){
|
||||
case T_DOUBLE:
|
||||
ret = (void*)get_jdouble(o);
|
||||
break;
|
||||
case T_DMS:
|
||||
case T_HMS:
|
||||
ret = (void*)get_jdhms(o);
|
||||
break;
|
||||
case T_STRING:
|
||||
default:
|
||||
ret = (void*)strdup(get_jstr(o));
|
||||
}
|
||||
json_object_put(o); // free object's memory
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* set non-blocking flag to socket
|
||||
* @param sock - socket fd
|
||||
*
|
||||
void setnonblocking(int sock){
|
||||
int opts = fcntl(sock, F_GETFL);
|
||||
if(opts < 0){
|
||||
perror("fcntl(F_GETFL)");
|
||||
exit(-1);
|
||||
}
|
||||
opts = (opts | O_NONBLOCK);
|
||||
if (fcntl(sock,F_SETFL,opts) < 0) {
|
||||
perror("fcntl(F_SETFL)");
|
||||
exit(-1);
|
||||
}
|
||||
return;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* wait for answer from server
|
||||
* @param sock - socket fd
|
||||
* @return 0 in case of error or timeout, 1 in case of socket ready
|
||||
*/
|
||||
int waittoread(int sock){
|
||||
fd_set fds;
|
||||
struct timeval timeout;
|
||||
int rc;
|
||||
timeout.tv_sec = 1; // wait not more than 1 second
|
||||
timeout.tv_usec = 0;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(sock, &fds);
|
||||
rc = select(sock+1, &fds, NULL, NULL, &timeout);
|
||||
if(rc < 0){
|
||||
perror("select failed");
|
||||
return 0;
|
||||
}
|
||||
if(rc > 0 && FD_ISSET(sock, &fds)) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]){
|
||||
size_t BUFSZ = 1024;
|
||||
int sockfd = 0;
|
||||
char *recvBuff = calloc(BUFSZ, 1);
|
||||
assert(recvBuff);
|
||||
struct addrinfo h, *r, *p;
|
||||
memset(&h, 0, sizeof(h));
|
||||
h.ai_family = AF_INET;
|
||||
h.ai_socktype = SOCK_STREAM;
|
||||
h.ai_flags = AI_CANONNAME;
|
||||
char *host = "localhost";
|
||||
if(argc > 1) host = argv[1];
|
||||
char *port = PORT;
|
||||
if(argc > 2) port = argv[2];
|
||||
if(getaddrinfo(host, port, &h, &r)){perror("getaddrinfo"); return -1;}
|
||||
struct sockaddr_in *ia = (struct sockaddr_in*)r->ai_addr;
|
||||
char str[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, &(ia->sin_addr), str, INET_ADDRSTRLEN);
|
||||
printf("canonname: %s, port: %u, addr: %s\n", r->ai_canonname, ntohs(ia->sin_port), str);
|
||||
for(p = r; p; p = p->ai_next) {
|
||||
if ((sockfd = socket(p->ai_family, p->ai_socktype,
|
||||
p->ai_protocol)) == -1) {
|
||||
perror("socket");
|
||||
continue;
|
||||
}
|
||||
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
|
||||
close(sockfd);
|
||||
perror("connect");
|
||||
continue;
|
||||
}
|
||||
break; // if we get here, we must have connected successfully
|
||||
}
|
||||
if(p == NULL){
|
||||
// looped off the end of the list with no connection
|
||||
fprintf(stderr, "failed to connect\n");
|
||||
return -1;
|
||||
}
|
||||
freeaddrinfo(r);
|
||||
//setnonblocking(sockfd);
|
||||
char *msg;
|
||||
char *res = RESOURSE;
|
||||
if(argc == 4) res = argv[3];
|
||||
msg = strdup(res);
|
||||
do{
|
||||
if(send(sockfd, msg, strlen(msg), 0) != strlen(msg)){perror("send"); return -1;}
|
||||
if(!waittoread(sockfd)) return -1;
|
||||
int offset = 0, n = 0;
|
||||
do{
|
||||
if(offset >= BUFSZ){
|
||||
BUFSZ += 1024;
|
||||
recvBuff = realloc(recvBuff, BUFSZ);
|
||||
assert(recvBuff);
|
||||
fprintf(stderr, "Buffer reallocated, new size: %zd\n", BUFSZ);
|
||||
}
|
||||
n = read(sockfd, &recvBuff[offset], BUFSZ - offset);
|
||||
if(!n) break;
|
||||
if(n < 0){
|
||||
perror("read");
|
||||
return -1;
|
||||
}
|
||||
offset += n;
|
||||
}while(waittoread(sockfd));
|
||||
if(!offset){
|
||||
fprintf(stderr, "Socket closed\n");
|
||||
return 0;
|
||||
}
|
||||
printf("read %d bytes\n", offset);
|
||||
recvBuff[offset] = 0;
|
||||
printf("Received data:\n-->%s<--\n", recvBuff);
|
||||
fflush(stdout);
|
||||
// here we do something with values we got
|
||||
// for example - parce them & print
|
||||
json_object *jobj = json_tokener_parse(recvBuff);
|
||||
if(!jobj){
|
||||
fprintf(stderr, "Can't parse json!\n");
|
||||
return -1;
|
||||
}
|
||||
double *focval = (double*)get_json_par(jobj, "ValFoc", T_DOUBLE);
|
||||
double *valaz = (double*)get_json_par(jobj, "ValAzim", T_DMS);
|
||||
double *valzd = (double*)get_json_par(jobj, "ValZenD", T_DMS);
|
||||
double *valdaz = (double*)get_json_par(jobj, "ValDome", T_DMS);
|
||||
double *valP2 = (double*)get_json_par(jobj, "ValP2", T_DMS);
|
||||
#define prntdbl(name, val) do{if(val) printf("%s = %g\n", name, *val); free(val);}while(0)
|
||||
prntdbl("Focus value", focval);
|
||||
prntdbl("Telescope azimuth", valaz);
|
||||
prntdbl("Telescope zenith distance", valzd);
|
||||
prntdbl("Dome azimuth", valdaz);
|
||||
prntdbl("P2 angle", valP2);
|
||||
#undef prntdbl
|
||||
json_object_put(jobj); // destroy jobj
|
||||
sleep(1);
|
||||
}while(1);
|
||||
return 0;
|
||||
}
|
||||
16
jsonbta/makefile_from_tb
Normal file
16
jsonbta/makefile_from_tb
Normal file
@ -0,0 +1,16 @@
|
||||
LOADLIBES = -lm -lcrypt
|
||||
SRCS = bta_json.c bta_print.c ../daemon.c
|
||||
CC = gcc
|
||||
DEFINES =
|
||||
CXX = gcc
|
||||
CPPFLAGS = -Wall $(DEFINES) -I/Users/eddy/include
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
all : bta_json client_streaming
|
||||
$(OBJS): bta_json.h bta_shdata.h
|
||||
bta_json : $(OBJS)
|
||||
$(CC) $(CPPFLAGS) $(OBJS) $(LOADLIBES) -o bta_json
|
||||
client_streaming: client_streaming.o
|
||||
$(CC) $(CPPFLAGS) client_streaming.o /Users/eddy/lib/libjson.a -lm -o client_streaming
|
||||
clean:
|
||||
/bin/rm -f *.o *~
|
||||
|
||||
BIN
jsonbta/manual.pdf
Normal file
BIN
jsonbta/manual.pdf
Normal file
Binary file not shown.
268
jsonbta/manual.tex
Normal file
268
jsonbta/manual.tex
Normal file
@ -0,0 +1,268 @@
|
||||
\documentclass[a4paper,12pt]{extarticle}
|
||||
\usepackage{listings}
|
||||
\usepackage{hyperref}
|
||||
\usepackage{/home/eddy/ed}
|
||||
\colorlet{punct}{red}
|
||||
\definecolor{delim}{RGB}{20,105,176}
|
||||
\colorlet{numb}{red!60!black}
|
||||
\lstdefinelanguage{JSON}{
|
||||
basicstyle=\small\ttfamily,
|
||||
numbers=left,
|
||||
numberstyle=\scriptsize,
|
||||
stepnumber=2,
|
||||
numbersep=8pt,
|
||||
showstringspaces=false,
|
||||
breaklines=true,
|
||||
frame=tbrL,
|
||||
literate=
|
||||
*{0}{{{\color{numb}0}}}{1}
|
||||
{1}{{{\color{numb}1}}}{1}
|
||||
{2}{{{\color{numb}2}}}{1}
|
||||
{3}{{{\color{numb}3}}}{1}
|
||||
{4}{{{\color{numb}4}}}{1}
|
||||
{5}{{{\color{numb}5}}}{1}
|
||||
{6}{{{\color{numb}6}}}{1}
|
||||
{7}{{{\color{numb}7}}}{1}
|
||||
{8}{{{\color{numb}8}}}{1}
|
||||
{9}{{{\color{numb}9}}}{1}
|
||||
{.}{{{\color{numb}.}}}{1}
|
||||
{:}{{{\color{punct}{:}}}}{1}
|
||||
{,}{{{\color{punct}{,}}}}{1}
|
||||
{\{}{{{\color{delim}{\{}}}}{1}
|
||||
{\}}{{{\color{delim}{\}}}}}{1}
|
||||
{[}{{{\color{delim}{[}}}}{1}
|
||||
{]}{{{\color{delim}{]}}}}{1},
|
||||
morestring=[b]",
|
||||
morestring=[b]',
|
||||
extendedchars=true,
|
||||
escapechar=`,
|
||||
stringstyle=\bfseries\color[rgb]{0.6,0,1},
|
||||
keywords={true,false},
|
||||
keywordstyle=\bfseries\color[rgb]{0,0.1,0.5},
|
||||
frameround=tttt
|
||||
}
|
||||
\lstset{basicstyle=\small,breaklines=true,
|
||||
extendedchars=true,aboveskip=1em,belowcaptionskip=5pt,
|
||||
prebreak = \hbox{%
|
||||
\normalfont\small\hfill\green{\ensuremath{\hookleftarrow}}},
|
||||
postbreak = \hbox to 0pt{%
|
||||
\hss\normalfont\small\green{\ensuremath{\hookrightarrow}}\hspace{1ex}},
|
||||
commentstyle=\color{blue},showspaces=false,
|
||||
showstringspaces=false,stringstyle=\bfseries\color[rgb]{0.6,0,1},
|
||||
numbers=left,numberstyle=\tiny,stepnumber=2,
|
||||
keywordstyle=\bfseries\color[rgb]{0,0.1,0.5},
|
||||
frameround=tttt,frame=trBL,tabsize=4,backgroundcolor=\color[rgb]{.9,.9,1}}
|
||||
\lstloadlanguages{JSON,C}
|
||||
\def\lstlistingname{Листинг}
|
||||
\def\lstref#1{(см.~листинг~\ref{#1})}
|
||||
\LTcapwidth=\textwidth
|
||||
|
||||
\title{Вспомогательный сервис BTA-JSON}
|
||||
|
||||
\begin{document}
|
||||
\nocolon
|
||||
\maketitle
|
||||
\section{Введение}
|
||||
Для осуществления возможности удаленного мониторинга состояния телескопа БТА (в
|
||||
т.ч. для заполнения стандартного заголовка FITS-файлов) на основе разработанной
|
||||
В.С.~Шергиным программы \verb'bta_print' был создан простой сетевой сервис,
|
||||
выдающий по запросу пользователя всю необходимую информацию в формате JSON.
|
||||
|
||||
Необходимость создания данного сервиса была обоснована тем, что прохождение
|
||||
multicast-пакетов из локальной сети БТА в локальную сеть ННП зачастую
|
||||
блокируется многочисленым промежуточным сетевым оборудованием.
|
||||
|
||||
Данный сервис в режиме демона работает на компьютере \verb'tb.sao.ru', отвечая
|
||||
на запросы, адресованные ресурсу \verb'/bta_par' на порту \verb'12345'.
|
||||
Демон принимает запросы как от веб-клиентов, так и от непосредственно
|
||||
подключенных клиентских сокетов. Полный адрес запроса для веб-клиента выглядит
|
||||
так:
|
||||
$$
|
||||
\verb'http://tb.sao.ru:12345/bta_par'
|
||||
$$
|
||||
|
||||
По полному запросу сервер возвращает объект JSON с переменными, аналогичными
|
||||
выводу программы \verb'bta_print'.
|
||||
\begin{lstlisting}[language=JSON]
|
||||
{
|
||||
"ACS_BTA": true,
|
||||
"M_time": "13:58:57.74",
|
||||
"JDate": 2456432.915943,
|
||||
"S_time": "04:37:37.01",
|
||||
"Tel_Mode": "Stopping",
|
||||
"Tel_Focus": "Prime",
|
||||
"ValFoc": 98.77,
|
||||
"Tel_Taget": "Zenith",
|
||||
"P2_Mode": "Stop",
|
||||
"CurAlpha": "04:12:28.25",
|
||||
"CurDelta": "+39:56:53.4",
|
||||
"SrcAlpha": "06:25:53.48",
|
||||
"SrcDelta": "+10:00:00.0",
|
||||
"InpAlpha": "11:41:52.03",
|
||||
"InpDelta": "+24:40:36.4",
|
||||
"TelAlpha": "04:57:06.82",
|
||||
"TelDelta": "+39:57:14.6",
|
||||
"InpAzim": "-118:27:18.0",
|
||||
"InpZenD": "83:52:29.3",
|
||||
"CurAzim": "+053:55:21.3",
|
||||
"CurZenD": "05:57:00.7",
|
||||
"CurPA": "049:36:52.7",
|
||||
"SrcPA": "329:54:01.0",
|
||||
"InpPA": "315:37:58.0",
|
||||
"TelPA": "317:08:11.1",
|
||||
"ValAzim": "-045:59:40.4",
|
||||
"ValZenD": "05:09:31.2",
|
||||
"ValP2": "219:33:16.7",
|
||||
"ValDome": "+134:55:41.6",
|
||||
"DiffAzim": "+000:00:00.0",
|
||||
"DiffZenD": "+00:00:00.0",
|
||||
"DiffP2": "+000:00:00.0",
|
||||
"DiffDome": "-180:55:22.0",
|
||||
"VelAzim": "+00:00:00.0",
|
||||
"VelZenD": "-00:00:00.0",
|
||||
"VelP2": "+00:00:00.0",
|
||||
"VelPA": "+00:00:00.0",
|
||||
"VelDome": "+00:00:00.0",
|
||||
"CorrAlpha": "+0:00:00.00",
|
||||
"CorrDelta": "+0:00:00.0",
|
||||
"CorrAzim": "+0:00:00.0",
|
||||
"CorrZenD": "+0:00:00.0",
|
||||
"ValTind": 010.4,
|
||||
"ValTmir": 010.3,
|
||||
"ValPres": 595.8,
|
||||
"ValWind": 01.7,
|
||||
"Blast10": 18633.4,
|
||||
"Blast15": 59794.7,
|
||||
"ValHumd": 86.3,
|
||||
"Precipt": 1087.4
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
||||
Момимо запуска утилиты из веб-браузеров можно использовать клиент командной
|
||||
строки. Образцы клиентов находятся в директории \verb'/Users/eddy/BTA_utils'
|
||||
компьютера \verb'tb.sao.ru', а также "--- в репозитории \verb'btautils' по
|
||||
адресу \url{https://sourceforge.net/projects/btautils/}. Помимо однократных
|
||||
запросов клиент может создать постоянное подключение к сокету демона для
|
||||
регулярных запросов интересующих его данных (см.~п.~\ref{coding}).
|
||||
|
||||
\section{Определение полей объекта JSON}
|
||||
В таблице~\ref{json_head} приведено описание полей объекта JSON, возвращаемого
|
||||
сервером клиенту. Тип данных поля имеет одно из следующих значений:
|
||||
\begin{description}
|
||||
\item[текст --] строковая величина (например, \verb'"Zenith"');
|
||||
\item[время --] строковая величина, характеризующая время, вида
|
||||
\verb'HH:MM:SS.SS' (для времени и прямых восхождений);
|
||||
\item[угол --] строковая величина вида \verb'[+]D:MM:SS.S' (для угловых
|
||||
величин);
|
||||
\item[число --] число с плавающей точкой.
|
||||
\end{description}
|
||||
|
||||
|
||||
\def\CH#1{\multicolumn{1}{|c|}{#1}}
|
||||
\begin{longtable}{|>{\tt}r|p{0.46\textwidth}<{\hfil}|c|>{\tt}c|}
|
||||
\caption{Поля объектов}\label{json_head}\\
|
||||
\hline\hline
|
||||
\multicolumn{1}{|m{2cm}|}{\bf Название\par\hfil поля\hfil}& \CH{\bf
|
||||
Описание} & \bf Тип данных &\bf Блок\\
|
||||
\hline
|
||||
\CH{\bf 1}&\CH{\bf 2}& \bf 3 & \bf 4\\
|
||||
\hline\endfirsthead
|
||||
\hline
|
||||
\multicolumn{4}{r}{\small(продолжение см.~на следующей странице)}\\
|
||||
\endfoot
|
||||
\hline\hline\endlastfoot
|
||||
\caption{Продолжение}\\
|
||||
\hline
|
||||
\CH{\bf 1}&\CH{\bf 2}& \bf 3& \bf 4\\
|
||||
\hline\endhead
|
||||
ACS\_BTA& Унаследованное от \texttt{bta\_print} поле, всегда \texttt{true}&
|
||||
--- & \bf---\\
|
||||
M\_time& Текущее московское время& время & mtime\\
|
||||
JDate& Текущая юлианская дата (сут.)& число & sidtime\\
|
||||
S\_time& Текущее звездное время& время & sidtime\\
|
||||
Tel\_Mode& Режим работы телескопа& строка & telmode\\
|
||||
Tel\_Focus& Активный фокус телескопа & строка & telfocus\\
|
||||
ValFoc& Отсчет датчика фокуса телескопа, мм& число & telfocus\\
|
||||
Tel\_Taget& Текущая цель телескопа& строка & target\\
|
||||
P2\_Mode& Режим работы поворотного стола& строка & p2mode\\
|
||||
CurAlpha& Текущие координаты предыдущей цели ($\alpha$~-- прямое восхождение) &
|
||||
время & eqcoor\\
|
||||
CurDelta& Текущие координаты предыдущей цели ($\delta$~-- склонение)& угол &
|
||||
eqcoor\\
|
||||
SrcAlpha& Текущие координаты цели ($\alpha$)& время & eqcoor\\
|
||||
SrcDelta& Текущие координаты цели ($\delta$)& угол & eqcoor\\
|
||||
InpAlpha& Введенные пользователем координаты ($\alpha$)& время & eqcoor\\
|
||||
InpDelta& Введенные пользователем координаты ($\delta$)& угол & eqcoor\\
|
||||
TelAlpha& Текущие координаты телескопа ($\alpha$)& время & eqcoor\\
|
||||
TelDelta& Текущие координаты телескопа ($\delta$)& угол & eqcoor\\
|
||||
InpAzim& Введенные горизонтальные координаты ($A$~-- азимут)& угол & horcoor\\
|
||||
InpZenD& Введенные горизонтальные координаты ($Z$~-- зенитное расстояние)&
|
||||
угол & horcoor\\
|
||||
CurAzim& Текущие горизонтальные координаты предыдущей цели ($A$)& угол &
|
||||
horcoor\\
|
||||
CurZenD& Текущие горизонтальные координаты предыдущей цели ($Z$)& угол &
|
||||
horcoor\\
|
||||
CurPA& Текущий позиционный угол предыдущей цели& угол & horcoor\\
|
||||
SrcPA& Текущий позиционный угол цели& угол & horcoor\\
|
||||
InpPA& Введенный пользователем позиционный угол& угол & horcoor\\
|
||||
TelPA& Текущий позиционный угол телескопа& угол & horcoor\\
|
||||
ValAzim& Отсчет датчика азимута& угол & valsens\\
|
||||
ValZenD& Отсчет датчика зенитного расстояния& угол & valsens\\
|
||||
ValP2& Отсчет датчика положения поворотного стола& угол & valsens\\
|
||||
ValDome& Отсчет датчика азимута купола& угол & valsens\\
|
||||
DiffAzim& Рассогласование по азимуту& угол & diff\\
|
||||
DiffZenD& Рассогласование по зенитному расстоянию& угол & diff\\
|
||||
DiffP2& Рассогласование по углу вращения поворотного стола& угол & diff\\
|
||||
DiffDome& Рассогласование по азимуту купола& угол & diff\\
|
||||
VelAzim& Текущая скорость движения телескопа по азимуту& угол & vel\\
|
||||
VelZenD& Текущая скорость движения телескопа по зенитному расстоянию& угол &
|
||||
vel\\
|
||||
VelP2& Текущая скорость движения поворотного стола & угол & vel\\
|
||||
VelPA& Текущая скорость <<вращения неба>>& угол & vel\\
|
||||
VelDome& Текущая скорость движения купола& угол & vel\\
|
||||
CorrAlpha& Введенная поправка по прямому восхождению& угол & corr\\
|
||||
CorrDelta& Введенная поправка по склонению& угол & corr\\
|
||||
CorrAzim& Введенная поправка по азимуту& угол & corr\\
|
||||
CorrZenD& Введенная поправка по зенитному расстоянию& угол & corr\\
|
||||
ValTind& Температура в подкупольном, $\degr$C& число & meteo\\
|
||||
ValTmir& Температура зеркала, $\degr$C& число & meteo\\
|
||||
ValPres& Атмосферное давление, мм.рт.ст.& число & meteo\\
|
||||
ValWind& Скорость ветра, м/с& число & meteo\\
|
||||
Blast10& Время от последнего порыва ветра, превышающего 10\,м/с, секунд& число &
|
||||
meteo\\
|
||||
Blast15& Время от последнего порыва ветра, превышающего 15\,м/с, секунд& число &
|
||||
meteo\\
|
||||
ValHumd& Влажность воздуха, \%& число & meteo\\
|
||||
Precipt& Время, прошедшее с момента последнего выпадения осадков, секунд&
|
||||
число & meteo\\
|
||||
\end{longtable}
|
||||
|
||||
\section{Осуществление постоянного подключения}
|
||||
\label{coding}
|
||||
Для осуществления постоянного удаленного подключения к демону для
|
||||
периодического получения необходимых данных, необходимо создать сокет,
|
||||
подключенный к хосту \verb'tb.sao.ru' по порту \verb'12345'.
|
||||
|
||||
Для запроса конкретного блока данных во время постоянного подключения, в
|
||||
качестве запроса необходимо отправлять текст, указанный в строке <<Блок>>
|
||||
таблицы~\ref{json_head}.
|
||||
|
||||
Формирование строки запроса можно сделать одним из следующих способов:
|
||||
\begin{itemize}
|
||||
\item посредством перечисления названий нужных блоков через разделитель
|
||||
(разделителем является пробел, амперсанд, символ табуляции или символ новой
|
||||
строки);
|
||||
\item посредством формирования запроса вида \verb'GET /bta_par&blocks', где
|
||||
\verb'blocks'~-- запрос аналогичный предыдущему пункту;
|
||||
\item посредством формирования запроса вида \verb'GET /bta_par', в этом случае
|
||||
демон формирует ответ с перечислением всех блоков, а затем отключается.
|
||||
\end{itemize}
|
||||
|
||||
Для обработки JSON--объекта можно использовать библиотеку \verb'libjson', либо
|
||||
же обрабатывать его вручную (т.к. структура объекта элементарная и
|
||||
однообразная).
|
||||
|
||||
Учитывая то, что мультикаст-пакеты с данными по БТА распространяются не чаще
|
||||
15~раз в~секунду, не стоит делать запросы чаще 10~раз в~секунду.
|
||||
|
||||
\end{document}
|
||||
Loading…
x
Reference in New Issue
Block a user