add detection of reconnect
[trinked.git] / main.c
1 /*-------------------------------------------------------------------------
2  * main.c
3  *
4  * Copyright (c) 2016, Teodor Sigaev <teodor@sigaev.ru>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *        notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *        notice, this list of conditions and the following disclaimer in the
13  *        documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *-------------------------------------------------------------------------
27  */
28
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <stdbool.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <hidapi/hidapi.h>
37 #include <sys/param.h>
38
39 #include <tlog.h>
40 #include <trinket.h>
41
42 static char*    dbdir = NULL;
43 static int              period = 300; /* in seconds */
44 static int              tics = 1;
45 static char*    pidfile = NULL;
46 static bool             daemonize = false;
47 static char             tfile[MAXPATHLEN],
48                                 ofile[MAXPATHLEN];
49
50 static const double coefficient = (248e-6 / 2637.0);
51
52 static void
53 usage(const char *errmsg) {
54         puts("trinketd - collecting info from AtomPro");
55         puts("Copyright (c) 2016, Teodor Sigaev <teodor@sigaev.ru>");
56         puts("trinketd [-d] [-p pidfile] [-P period] [-D datadir] [-i tics] [-l logfile");
57
58         if (errmsg) {
59                 puts("");
60                 puts(errmsg);
61         }
62
63         exit(1);
64 }
65
66 static void
67 main_loop() {
68         double  *prevDose,
69                         curDose;
70         int             i;
71         int             collected = 0;
72
73         prevDose = malloc(sizeof(*prevDose) * (tics + 1));
74         if (!prevDose) {
75                 tlog(TL_CRIT, "could not allocate array, exiting...");
76                 return;
77         }
78
79         while(42) {
80
81                 if (trinketOpen() != ERR_OK) {
82                         tlog(TL_ALARM, "trinketOpen fails");
83                         sleep(1);
84                         continue;
85                 }
86
87                 if (trinketGetCumDose(&curDose) != ERR_OK) {
88                         tlog(TL_ALARM, "trinketGetCumDose fails");
89                         trinketClose();
90                         sleep(1);
91                         continue;
92                 }
93
94                 trinketClose();
95
96                 if (collected > 0 && prevDose[collected - 1] > curDose)
97                 {
98                         tlog(TL_WARN, "Reconnect or full reset detected");
99                         collected = 0;
100                 }
101
102                 if (collected <= tics) {
103                         prevDose[collected++] = curDose;
104                 } else {
105                         for (i=1; i<=tics; i++)
106                                 prevDose[i - 1] = prevDose[i];
107                         prevDose[tics] = curDose;
108                 }
109
110                 if (collected > 1) {
111                         double  counts = curDose - prevDose[0];
112                         double  radlevel = 1e6 * coefficient * counts * 3600.0 /
113                                                                 (double)(period * (collected - 1));
114                         FILE    *fh = stdout;
115                         bool    successOpen = true;
116
117                         if (daemonize) {
118                                 if ((fh = fopen(tfile, "w")) == NULL) {
119                                         tlog(TL_ALARM, "fopen fails: %s", strerror(errno));
120                                         successOpen = false;
121                                 }
122                         }
123
124                         if (successOpen) {
125                                 fprintf(fh, "total: %lld\n", (long long)curDose);
126                                 fprintf(fh, "counts: %lld\n", (long long)counts);
127                                 fprintf(fh, "tics: %d\n", collected - 1);
128                                 fprintf(fh, "radlevel: %.02f\n", radlevel);
129                         }
130
131                         if (fh && fh != stdout) {
132                                 fflush(fh);
133                                 fclose(fh);
134                                 rename(tfile, ofile);
135                         }
136                 }
137
138                 sleep(period);
139         }
140 }
141
142
143 extern char *optarg;
144 extern int opterr, optind;
145
146 int
147 main(int argn, char* argv[]) {
148         int             i;
149         int             pidfd;
150         char    *logfile = NULL;
151
152         opterr = 0;
153         while((i=getopt(argn,argv,"dD:i:l:p:P:h")) != EOF) {
154                 switch(i) {
155                         case 'd':
156                                 daemonize = true;
157                                 break;
158                         case 'D':
159                                 dbdir = strdup(optarg);
160                                 break;
161                         case 'i':
162                                 tics = atoi(optarg);
163                                 break;
164                         case 'l':
165                                 logfile = strdup(optarg);
166                                 break;
167                         case 'p':
168                                 pidfile = strdup(optarg);
169                                 break;
170                         case 'P':
171                                 period = atoi(optarg);
172                                 break;
173                         case 'h':
174                         default:
175                                 usage(NULL);
176                 }
177         }
178
179         if (opterr || optind != argn)
180                 usage("argument err");
181
182         if (period <= 0)
183                 usage("Collecting period could not be zero nor negative");
184
185         if (tics <= 0)
186                 usage("Number of tics could not be zero nor negative");
187
188         if (daemonize && !dbdir)
189                 usage("trinketd: it is useless to use -d without -D");
190
191         if (dbdir && strlen(dbdir) > MAXPATHLEN - 32)
192                 usage("datadir is too long");
193
194         if (pidfile) {
195                 pidfd = open(pidfile, O_CREAT | O_WRONLY | O_TRUNC, 0666);
196                 if (pidfd < 0)
197                         usage("could not write pidfile");
198         }
199
200         if (daemonize) {
201                 opentlog((logfile == NULL) ? TL_OPEN_SYSLOG : TL_OPEN_FILE,
202                                  TL_INFO, logfile);
203
204                 if (daemon(0, 0) == -1)
205                         tlog(TL_CRIT | TL_EXIT, "could not daemonize");
206         } else {
207                 opentlog(TL_OPEN_STDERR | TL_OPEN_FILE, TL_INFO, logfile);
208         }
209
210
211
212         if (pidfile) {
213                 char pid[64];
214
215                 snprintf(pid, sizeof(pid), "%lld", (long long)getpid());
216                 if (write(pidfd, pid, strlen(pid)) != strlen(pid))
217                         tlog(TL_CRIT | TL_EXIT, "could not write pid");
218                 close(pidfd);
219         }
220
221         tlog(TL_CRIT, "trinketd started, pid: %lld", (long long)getpid());
222
223         if (hid_init() < 0)
224                 tlog(TL_CRIT | TL_EXIT, "hid_init fails");
225
226         if (dbdir) {
227                 snprintf(tfile, MAXPATHLEN, "%s/trinket.tmp", dbdir);
228                 snprintf(ofile, MAXPATHLEN, "%s/trinket.dat", dbdir);
229         }
230
231         main_loop();
232
233         hid_exit();
234         return 0;
235 }