33 t = t + n |
36 t = t + n |
34 } |
37 } |
35 return t, nil |
38 return t, nil |
36 } |
39 } |
37 |
40 |
38 func main() { |
41 func fetchData(dev string) (items []measurement, err error) { |
39 c := &serial.Config{Name: "/dev/ttyUSB0", Baud: 4800} |
42 c := &serial.Config{Name: dev, Baud: 4800} |
40 s, err := serial.OpenPort(c) |
43 |
41 if err != nil { |
44 var s *serial.Port |
42 log.Fatal(err) |
45 s, err = serial.OpenPort(c) |
43 return |
46 if err != nil { |
44 } |
47 return items, err |
45 |
48 } |
|
49 |
|
50 // =================== Handshake ===================== |
46 q := []byte("\xaa") |
51 q := []byte("\xaa") |
47 //log.Printf("Query: %q\n", q) |
52 //log.Printf("Query: %q\n", q) |
48 log.Println("Starting handshake...") |
53 log.Println("Starting handshake...") |
49 n, err := s.Write(q) |
54 n, err := s.Write(q) |
50 if err != nil { |
55 if err != nil { |
51 log.Fatal(err) |
56 return items, err |
52 return |
|
53 } |
57 } |
54 |
58 |
55 buf := make([]byte, 128) |
59 buf := make([]byte, 128) |
56 n, err = getData(s, buf, 1) |
60 n, err = getData(s, buf, 1) |
57 if err != nil { |
61 if err != nil { |
58 log.Fatal(err) |
62 return items, err |
59 return |
|
60 } |
63 } |
61 if n == 1 && buf[0] == '\x55' { |
64 if n == 1 && buf[0] == '\x55' { |
62 log.Println("Handshake successful.") |
65 log.Println("Handshake successful.") |
63 } else { |
66 } else { |
64 log.Printf("(%d bytes) %q\n", n, buf[:n]) |
67 log.Printf("(%d bytes) %q\n", n, buf[:n]) |
65 s.Close() |
68 s.Close() |
66 return |
69 return items, fmt.Errorf("handshake failed") |
67 } |
70 } |
68 |
71 |
69 // =================== Desc ===================== |
72 // =================== Desc ===================== |
70 q = []byte("\xa4") |
73 q = []byte("\xa4") |
71 //log.Printf("Query: %q\n", q) |
74 //log.Printf("Query: %q\n", q) |
72 log.Println("Requesting device description...") |
75 log.Println("Requesting device description...") |
73 n, err = s.Write(q) |
76 n, err = s.Write(q) |
74 if err != nil { |
77 if err != nil { |
75 log.Fatal(err) |
78 return items, err |
76 return |
|
77 } |
79 } |
78 |
80 |
79 n, err = getData(s, buf, 32) |
81 n, err = getData(s, buf, 32) |
80 log.Printf("DESC> %q\n", buf[:n]) |
82 log.Printf("DESC> %q\n", buf[:n]) |
81 |
83 |
83 q = []byte("\xa2") |
85 q = []byte("\xa2") |
84 //log.Printf("Query: %q\n", q) |
86 //log.Printf("Query: %q\n", q) |
85 log.Println("Requesting data counter...") |
87 log.Println("Requesting data counter...") |
86 n, err = s.Write(q) |
88 n, err = s.Write(q) |
87 if err != nil { |
89 if err != nil { |
88 log.Fatal(err) |
90 return items, err |
89 return |
|
90 } |
91 } |
91 |
92 |
92 n, err = getData(s, buf, 1) |
93 n, err = getData(s, buf, 1) |
93 if err != nil { |
94 if err != nil { |
94 log.Fatal(err) |
95 return items, err |
95 return |
|
96 } |
96 } |
97 var nRecords int |
97 var nRecords int |
98 if n == 1 { |
98 if n == 1 { |
99 log.Printf("%d item(s) available.", buf[0]) |
99 log.Printf("%d item(s) available.", buf[0]) |
100 nRecords = int(buf[0]) |
100 nRecords = int(buf[0]) |
101 } else { |
101 } else { |
102 log.Printf("(%d bytes) %q\n", n, buf[:n]) |
102 log.Printf("(%d bytes) %q\n", n, buf[:n]) |
103 return |
103 return items, fmt.Errorf("no measurement found") |
104 } |
104 } |
105 |
105 |
|
106 // =================== Records ===================== |
106 for i := 0; i < nRecords; i++ { |
107 for i := 0; i < nRecords; i++ { |
107 q = []byte{'\xa3', uint8(i + 1)} |
108 q = []byte{'\xa3', uint8(i + 1)} |
108 //log.Printf("Query: %q\n", q) |
109 //log.Printf("Query: %q\n", q) |
109 //log.Printf("Requesting measurement %d...", i+1) |
110 //log.Printf("Requesting measurement %d...", i+1) |
110 n, err = s.Write(q) |
111 n, err = s.Write(q) |
111 if err != nil { |
112 if err != nil { |
112 log.Fatal(err) |
113 return items, err |
113 return |
|
114 } |
114 } |
115 |
115 |
116 n, err = getData(s, buf, 9) |
116 n, err = getData(s, buf, 9) |
117 //log.Printf("DESC> %q\n", buf[:n]) |
117 //log.Printf("DESC> %q\n", buf[:n]) |
118 |
118 |
119 var data measurement |
119 var data measurement |
120 data.header = int(buf[0]) |
120 data.Header = int(buf[0]) |
121 data.systolic = int(buf[1]) + 25 |
121 data.Systolic = int(buf[1]) + 25 |
122 data.diastolic = int(buf[2]) + 25 |
122 data.Diastolic = int(buf[2]) + 25 |
123 data.pulse = int(buf[3]) |
123 data.Pulse = int(buf[3]) |
124 data.month = int(buf[4]) |
124 data.Month = int(buf[4]) |
125 data.day = int(buf[5]) |
125 data.Day = int(buf[5]) |
126 data.hour = int(buf[6]) |
126 data.Hour = int(buf[6]) |
127 data.minute = int(buf[7]) |
127 data.Minute = int(buf[7]) |
128 data.year = int(buf[8]) + 2000 |
128 data.Year = int(buf[8]) + 2000 |
129 fmt.Printf("%d;%x;%d-%02d-%02d %02d:%02d;%d;%d;%d\n", |
129 items = append(items, data) |
130 i+1, data.header, |
|
131 data.year, data.month, data.day, |
|
132 data.hour, data.minute, |
|
133 data.systolic, data.diastolic, data.pulse) |
|
134 } |
130 } |
135 |
131 |
136 s.Close() |
132 s.Close() |
137 } |
133 return items, nil |
|
134 } |
|
135 |
|
136 func loadFromJSONFile(filename string) (items []measurement, err error) { |
|
137 data, err := ioutil.ReadFile(filename) |
|
138 if err != nil { |
|
139 return items, err |
|
140 } |
|
141 |
|
142 err = json.Unmarshal(data, &items) |
|
143 return items, err |
|
144 } |
|
145 |
|
146 func mergeItems(newItems, oldItems []measurement) []measurement { |
|
147 var result []measurement |
|
148 var j int |
|
149 // TODO: Would be better to compare dates and merge chronologically... |
|
150 for _, nItem := range newItems { |
|
151 result = append(result, nItem) |
|
152 if j+1 <= len(oldItems) && nItem == oldItems[j] { |
|
153 j++ |
|
154 } |
|
155 } |
|
156 if j+1 <= len(oldItems) { |
|
157 result = append(result, oldItems[j:]...) |
|
158 } |
|
159 return result |
|
160 } |
|
161 |
|
162 func main() { |
|
163 inFile := flag.String([]string{"-input-file", "i"}, "", "Input JSON file") |
|
164 outFile := flag.String([]string{"-output-file", "o"}, "", "Output JSON file") |
|
165 limit := flag.Uint([]string{"-limit", "l"}, 0, "Limit number of items") |
|
166 format := flag.String([]string{"-format", "f"}, "", "Output format (csv, json)") |
|
167 avg := flag.Bool([]string{"-average", "a"}, false, "Compute average") |
|
168 merge := flag.Bool([]string{"-merge", "m"}, false, |
|
169 "Try to merge input JSON file with fetched data") |
|
170 device := flag.String([]string{"-device", "d"}, "/dev/ttyUSB0", "Serial device") |
|
171 |
|
172 flag.Parse() |
|
173 |
|
174 switch *format { |
|
175 case "": |
|
176 if *outFile == "" { |
|
177 *format = "csv" |
|
178 } |
|
179 break |
|
180 case "json", "csv": |
|
181 break |
|
182 default: |
|
183 log.Fatal("Unknown output format. Possible choices are csv, json.") |
|
184 } |
|
185 |
|
186 var err error |
|
187 var items []measurement |
|
188 |
|
189 if *inFile == "" { |
|
190 // Read from device |
|
191 if items, err = fetchData(*device); err != nil { |
|
192 log.Fatal(err) |
|
193 } |
|
194 } else { |
|
195 // Read from file |
|
196 var fileItems []measurement |
|
197 if fileItems, err = loadFromJSONFile(*inFile); err != nil { |
|
198 log.Fatal(err) |
|
199 } |
|
200 if *merge { |
|
201 if items, err = fetchData(*device); err != nil { |
|
202 log.Fatal(err) |
|
203 } |
|
204 items = mergeItems(items, fileItems) |
|
205 } else { |
|
206 items = fileItems |
|
207 } |
|
208 } |
|
209 |
|
210 if *limit > 0 && len(items) > int(*limit) { |
|
211 items = items[0:*limit] |
|
212 } |
|
213 |
|
214 var avgMeasure measurement |
|
215 var avgCount int |
|
216 |
|
217 for i, data := range items { |
|
218 if *format == "csv" { |
|
219 fmt.Printf("%d;%x;%d-%02d-%02d %02d:%02d;%d;%d;%d\n", |
|
220 i+1, data.Header, |
|
221 data.Year, data.Month, data.Day, |
|
222 data.Hour, data.Minute, |
|
223 data.Systolic, data.Diastolic, data.Pulse) |
|
224 } |
|
225 |
|
226 avgMeasure.Systolic += data.Systolic |
|
227 avgMeasure.Diastolic += data.Diastolic |
|
228 avgMeasure.Pulse += data.Pulse |
|
229 avgCount++ |
|
230 } |
|
231 |
|
232 if *avg && avgCount > 0 { |
|
233 avgMeasure.Systolic /= avgCount |
|
234 avgMeasure.Diastolic /= avgCount |
|
235 avgMeasure.Pulse /= avgCount |
|
236 |
|
237 fmt.Printf("Average: %d;%d;%d\n", avgMeasure.Systolic, |
|
238 avgMeasure.Diastolic, avgMeasure.Pulse) |
|
239 } |
|
240 |
|
241 if *format == "json" || *outFile != "" { |
|
242 rawJSON, err := json.MarshalIndent(items, "", " ") |
|
243 if err != nil { |
|
244 log.Fatal("Error:", err) |
|
245 } |
|
246 |
|
247 if *format == "json" { |
|
248 fmt.Println(string(rawJSON)) |
|
249 } |
|
250 if *outFile != "" { |
|
251 err = ioutil.WriteFile(*outFile, rawJSON, 0600) |
|
252 if err != nil { |
|
253 log.Println("Could not write output file:", err) |
|
254 } |
|
255 } |
|
256 } |
|
257 } |