This commit is contained in:
Eddy 2014-03-19 00:00:06 +04:00
parent 8ed4972ce3
commit 99bc79d44e
11 changed files with 2680 additions and 0 deletions

140
daemon.c Normal file
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

61
jsonbta/client.c Normal file
View 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
View 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
View 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

Binary file not shown.

268
jsonbta/manual.tex Normal file
View 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}