summaryrefslogtreecommitdiffabout
path: root/lib
Unidiff
Diffstat (limited to 'lib') (more/less context) (ignore whitespace changes)
-rw-r--r--lib/.gitignore11
-rw-r--r--lib/Makefile.am11
-rw-r--r--lib/hypnodata.cc92
-rw-r--r--lib/st-decode.cc121
-rw-r--r--lib/st-download.cc70
-rw-r--r--lib/util.cc13
6 files changed, 318 insertions, 0 deletions
diff --git a/lib/.gitignore b/lib/.gitignore
new file mode 100644
index 0000000..b7b1941
--- a/dev/null
+++ b/lib/.gitignore
@@ -0,0 +1,11 @@
1/.deps
2/.libs
3/st-decode.lo
4/st-decode.o
5/libnapkin.la
6/st-download.lo
7/st-download.o
8/hypnodata.lo
9/hypnodata.o
10/util.lo
11/util.o
diff --git a/lib/Makefile.am b/lib/Makefile.am
new file mode 100644
index 0000000..bf55849
--- a/dev/null
+++ b/lib/Makefile.am
@@ -0,0 +1,11 @@
1lib_LTLIBRARIES = libnapkin.la
2
3INCLUDES = -I${top_builddir}/include/ -I${top_srcdir}/include/ \
4 ${MODULES_CFLAGS}
5LIBS = ${MODULES_CFLAGS}
6
7libnapkin_la_SOURCES = \
8 st-decode.cc st-download.cc \
9 hypnodata.cc \
10 util.cc
11libnapkin_la_LDFLAGS = -version-info 0:0:0
diff --git a/lib/hypnodata.cc b/lib/hypnodata.cc
new file mode 100644
index 0000000..977fb76
--- a/dev/null
+++ b/lib/hypnodata.cc
@@ -0,0 +1,92 @@
1#include <napkin/exception.h>
2#include <napkin/util.h>
3#include <napkin/types.h>
4
5namespace napkin {
6
7 void hypnodata_t::clear() {
8 to_bed = alarm = 0;
9 data_a = window = 0;
10 almost_awakes.clear();
11 }
12
13 static time_t from_minute_w3c(const string& w3c) {
14 struct tm t; memset(&t,0,sizeof(t)); t.tm_isdst=-1;
15 if(sscanf(w3c.c_str(),"%04d-%02d-%02dT%02d:%02d",
16 &t.tm_year,&t.tm_mon,&t.tm_mday,&t.tm_hour,&t.tm_min)!=5)
17 throw exception("failed to parse w3c time");
18 --t.tm_mon;t.tm_year-=1900;
19 time_t rv = mktime(&t);
20 if(rv==(time_t)-1)
21 throw exception("failed to mktime()");
22 return rv;
23 }
24
25 void hypnodata_t::set_to_bed(const string& w3c) {
26 to_bed = from_minute_w3c(w3c); }
27 void hypnodata_t::set_alarm(const string& w3c) {
28 alarm = from_minute_w3c(w3c); }
29 void hypnodata_t::set_window(const string& str) {
30 window = strtol(str.c_str(),0,10); /* TODO: check for error */
31 }
32 void hypnodata_t::set_data_a(const string& str) {
33 data_a = strtol(str.c_str(),0,10); /* TODO: check for error */
34 }
35 void hypnodata_t::set_almost_awakes(const string& str) {
36 almost_awakes.clear();
37 static const char *significants = "0123456789-T:Z";
38 string::size_type p = str.find_first_of(significants);
39 struct tm t; memset(&t,0,sizeof(t)); t.tm_isdst=-1;
40 while(p!=string::npos) {
41 string::size_type ns = str.find_first_not_of(significants,p);
42 string w3c;
43 if(ns==string::npos) {
44 w3c = str.substr(p);
45 p = string::npos;
46 }else{
47 w3c = str.substr(p,ns-p);
48 p = str.find_first_of(significants,ns);
49 }
50 if(w3c.empty()) continue;
51 if(sscanf(w3c.c_str(),"%04d-%02d-%02dT%02d:%02d:%02d",
52 &t.tm_year,&t.tm_mon,&t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec)!=6)
53 throw exception("failed to parse w3c time");
54 --t.tm_mon;t.tm_year-=1900;
55 time_t aa = mktime(&t);
56 if(aa==(time_t)-1)
57 throw exception("failed to mktime()");
58 almost_awakes.push_back(aa);
59 }
60 }
61
62 const string hypnodata_t::w3c_to_bed() const {
63 return strftime("%Y-%m-%dT%H:%M",to_bed); }
64 const string hypnodata_t::w3c_alarm() const {
65 return strftime("%Y-%m-%dT%H:%M",alarm); }
66 const string hypnodata_t::w3c_almostawakes() const {
67 string rv;
68 for(vector<time_t>::const_iterator i=almost_awakes.begin();i!=almost_awakes.end();++i) {
69 if(!rv.empty())
70 rv += ',';
71 rv += strftime("%Y-%m-%dT%H:%M:%S",*i);
72 }
73 return rv;
74 }
75
76 const string hypnodata_t::str_to_bed() const {
77 return strftime("%H:%M",to_bed); }
78 const string hypnodata_t::str_alarm() const {
79 return strftime("%H:%M",alarm); }
80 const string hypnodata_t::str_date() const {
81 return strftime("%Y-%m-%d, %a",alarm); }
82 const string hypnodata_t::str_data_a() const {
83 char tmp[16];
84 snprintf(tmp,sizeof(tmp),"%d:%02d:%02d",
85 data_a/3600, (data_a%3600)/60,
86 data_a % 60 );
87 return tmp; }
88
89 time_t hypnodata_t::aligned_start() const {
90 return alarm - (alarm % (24*60*60)) - 24*60*60; }
91
92}
diff --git a/lib/st-decode.cc b/lib/st-decode.cc
new file mode 100644
index 0000000..f8459ac
--- a/dev/null
+++ b/lib/st-decode.cc
@@ -0,0 +1,121 @@
1#include <stdexcept>
2#include <numeric>
3#include <napkin/exception.h>
4#include <napkin/st/decode.h>
5
6namespace napkin {
7 namespace sleeptracker {
8 using std::invalid_argument;
9 using std::runtime_error;
10
11 struct st_time_t {
12 uint8_t hour;
13 uint8_t min;
14 };
15 struct st_date_t {
16 uint8_t month;
17 uint8_t day;
18 };
19 struct st_fulltime_t {
20 uint8_t hour;
21 uint8_t min;
22 uint8_t sec;
23 };
24 struct st_data_header_t {
25 char magic;
26 st_date_t today;
27 uint8_t unknown;
28 uint8_t window;
29 st_time_t to_bed;
30 st_time_t alarm;
31 uint8_t nawakes;
32 };
33 struct st_data_footer_t {
34 uint16_t data_a;
35 uint8_t checksum;
36 uint8_t eof_mark;
37 };
38
39 static void back_a_day(struct tm& t) {
40 time_t ts = mktime(&t);
41 if(ts==(time_t)-1)
42 throw exception_st_data("failed to make up time to step back a day");
43 ts -= 60*60*24;
44 if(!localtime_r(&ts,&t))
45 throw exception_st_data("failed to localtime_r() while stepping back a day");
46 }
47
48 hypnodata_t& decode(hypnodata_t& rv,const void *data,size_t data_length) {
49 if(data_length < (sizeof(st_data_header_t)+sizeof(st_data_footer_t)))
50 throw exception_st_data_envelope("not enough sleeptracker data to decode");
51 st_data_header_t *h = (st_data_header_t*)data;
52 if(h->magic != 'V')
53 throw exception_st_data_envelope("invalid magic in the data");
54 st_data_footer_t *f = (st_data_footer_t*)(static_cast<const char *>(data)+data_length-sizeof(st_data_footer_t));
55 if( (std::accumulate((uint8_t*)&h->today,(uint8_t*)&f->checksum,0)&0xFF) != f->checksum )
56 throw exception_st_data_integrity("checksum mismatch");
57 st_fulltime_t *aawake = (st_fulltime_t*)&h[1];
58 if((void*)&aawake[h->nawakes] != (void*)f)
59 throw exception_st_data_envelope("unbelievably screwed up data");
60 rv.clear();
61 time_t now = time(0);
62 struct tm t;
63 if(!localtime_r(&now,&t))
64 throw exception_st_data("failed to localtime_r()");
65 t.tm_mon = h->today.month-1;
66 t.tm_mday = h->today.day;
67 time_t mkt = mktime(&t);
68 if(mkt == (time_t)-1)
69 throw exception_st_data("failed to mktime() for a timestamp");
70 if(mkt > now) {
71 --t.tm_year;
72 }
73 struct tm ta;
74 memmove(&ta,&t,sizeof(ta));
75 ta.tm_sec = 0;
76 ta.tm_hour = h->alarm.hour; ta.tm_min = h->alarm.min;
77 rv.alarm = mktime(&ta);
78 if(rv.alarm == (time_t)-1)
79 throw exception_st_data("failed to mktime() for alarm");
80 struct tm tb;
81 memmove(&tb,&ta,sizeof(tb));
82 tb.tm_hour = h->to_bed.hour; tb.tm_min = h->to_bed.min;
83 rv.to_bed = mktime(&tb);
84 if(rv.to_bed == (time_t)-1)
85 throw exception_st_data("failed to mktime() for 'to bed'");
86 if(rv.to_bed > rv.alarm) {
87 back_a_day(tb);
88 rv.to_bed -= 24*60*60;
89 }
90 struct tm taaw;
91 memmove(&taaw,&tb,sizeof(taaw));
92 for(int rest=h->nawakes;rest;--rest,++aawake) {
93 if(
94 taaw.tm_mday!=ta.tm_mday
95 && (
96 aawake->hour < tb.tm_hour
97 || (
98 aawake->hour==tb.tm_hour
99 && aawake->min < tb.tm_min )
100 ) )
101 memmove(&taaw,&ta,sizeof(taaw));
102 taaw.tm_hour = aawake->hour;
103 taaw.tm_min = aawake->min;
104 taaw.tm_sec = aawake->sec;
105 rv.almost_awakes.push_back( mktime(&taaw) );
106 if(rv.almost_awakes.back() == (time_t)-1)
107 throw exception_st_data("failed to mktime() for almost awake moment");
108 }
109 rv.window = h->window;
110 rv.data_a = f->data_a;
111 return rv;
112 }
113
114 hypnodata_ptr_t decode(const void *data,size_t data_length) {
115 hypnodata_ptr_t rv( new hypnodata_t );
116 decode(*rv,data,data_length);
117 return rv;
118 }
119
120 }
121}
diff --git a/lib/st-download.cc b/lib/st-download.cc
new file mode 100644
index 0000000..b56e52d
--- a/dev/null
+++ b/lib/st-download.cc
@@ -0,0 +1,70 @@
1#include <sys/types.h>
2#include <sys/stat.h>
3#include <fcntl.h>
4#include <unistd.h>
5#include <termios.h>
6#include <stdexcept>
7#include <napkin/exception.h>
8#include <napkin/st/download.h>
9#include <napkin/st/decode.h>
10
11namespace napkin {
12 namespace sleeptracker {
13 using std::runtime_error;
14
15 intdownload_initiate(const char *port) {
16 int fd = open(port?port:"/dev/sleeptracker",
17 O_RDWR|O_NOCTTY|O_NONBLOCK);
18 if(fd<0)
19 throw exception_st_port("failed to open() sleeptracker port");
20
21 if(tcflush(fd,TCIOFLUSH)) {
22 close(fd);
23 throw exception_st_port("failed to tcflush()");
24 }
25 struct termios ts;
26 ts.c_cflag = CS8|CREAD;
27 cfsetispeed(&ts,B2400); cfsetospeed(&ts,B2400);
28 ts.c_iflag = IGNPAR;
29 ts.c_oflag = ts.c_lflag = 0;
30 ts.c_cc[VMIN]=1; ts.c_cc[VTIME]=0;
31 if(tcsetattr(fd,TCSANOW,&ts)) {
32 close(fd);
33 throw exception_st_port("failed to tcsetattr()");
34 }
35
36 if(write(fd,"V",1)!=1) {
37 close(fd);
38 throw exception_st_port("failed to write() to sleeptracker");
39 }
40 return fd;
41 }
42 size_t download_finish(int fd,void *buffer,size_t buffer_size) {
43 size_t rv = read(fd,buffer,buffer_size);
44 close(fd);
45
46 if(rv==(size_t)-1)
47 throw exception_st_port("failed to read() from sleeptracker");
48 return rv;
49 }
50
51 size_t download(
52 void *buffer,size_t buffer_size,
53 const char *port) {
54 int fd = download_initiate(port);
55 /* this is not the best way to wait for data, but
56 * after all it's a sleeptracker! */
57 sleep(1);
58 return download_finish(fd,buffer,buffer_size);
59 }
60
61 hypnodata_ptr_t download(const char *port) {
62 char buffer[2048];
63 size_t rb = download(buffer,sizeof(buffer),port);
64 hypnodata_ptr_t rv( new hypnodata_t );
65 decode(*rv,buffer,rb);
66 return rv;
67 }
68
69 }
70}
diff --git a/lib/util.cc b/lib/util.cc
new file mode 100644
index 0000000..350cbac
--- a/dev/null
+++ b/lib/util.cc
@@ -0,0 +1,13 @@
1#include <napkin/util.h>
2
3namespace napkin {
4
5 string strftime(const char *fmt,time_t t) {
6 struct tm tt;
7 localtime_r(&t,&tt);// TODO: check res
8 char rv[1024];
9 strftime(rv,sizeof(rv),fmt,&tt); // TODO: check res
10 return rv;
11 }
12
13}