added a command to calculate the current average wait time
[eqemu] / src / dev.cc
1 /*
2 eqemu - electronic queue system emulator
3 Copyright (C) 2014  John Tsiombikas <nuclear@member.fsf.org>,
4                     Eleni-Maria Stea <eleni@mutantstargoat.com>
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 #include <stdio.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <signal.h>
23 #include <time.h>
24 #include <limits.h>
25 #include <vector>
26 #include <string>
27 #include <unistd.h>
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30 #include <fcntl.h>
31 #include <termios.h>
32 #include "dev.h"
33 #include "timer.h"
34
35 struct CustStat {
36         int id;
37         time_t start, end;
38 };
39
40 #define TMHIST_SIZE             16
41
42 void post_redisplay();  // defined in main.cc
43
44 int customer, ticket;
45 static int report_inputs, cmd_echo;
46 static long last_ticket_msec = LONG_MIN;
47
48 static std::vector<CustStat> cstat;
49
50 time_t calc_avg_wait();
51 static void runcmd(const char *cmd);
52
53 static int fd = -1;
54 static FILE *fp;
55 static std::string cur_line;
56
57 int start_dev(const char *devpath)
58 {
59         if((fd = open(devpath, O_RDWR | O_NONBLOCK)) == -1) {
60                 fprintf(stderr, "failed to open device: %s: %s\n", devpath, strerror(errno));
61                 return -1;
62         }
63         if(isatty(fd)) {
64                 struct termios term;
65
66                 if(tcgetattr(fd, &term) == -1) {
67                         perror("failed to retrieve terminal attributes");
68                         stop_dev();
69                         return -1;
70                 }
71                 term.c_cflag = CS8 | CLOCAL;
72                 term.c_iflag &= ~(IXON | IXOFF);
73                 term.c_lflag = 0;
74
75                 cfsetispeed(&term, B38400);
76                 cfsetospeed(&term, B38400);
77
78                 if(tcsetattr(fd, TCSANOW, &term) == -1) {
79                         perror("failed to set terminal attributes");
80                         stop_dev();
81                         return -1;
82                 }
83         }
84
85         if(!(fp = fdopen(fd, "r+"))) {
86                 perror("failed to attach an I/O stream to the device file\n");
87                 stop_dev();
88                 return -1;
89         }
90         setvbuf(fp, 0, _IONBF, 0);
91
92         return fd;
93 }
94
95 void stop_dev()
96 {
97         if(fp)
98                 fclose(fp);
99         else if(fd >= 0)
100                 close(fd);
101 }
102
103
104 void proc_dev_input()
105 {
106         int rdbytes;
107         char buf[256];
108         static bool skip_line;
109
110         while((rdbytes = read(fd, buf, sizeof buf - 1)) > 0) {
111                 buf[rdbytes] = 0;
112
113                 /* ignore our own crap */
114                 if(memcmp(buf, "OK,", 3) == 0 || memcmp(buf, "ERR,", 4) == 0) {
115                         skip_line = true;
116                 }
117
118                 for(int i=0; i<rdbytes; i++) {
119                         if(buf[i] == '\n' || buf[i] == '\r') {
120                                 if(!cur_line.empty()) {
121                                         runcmd(cur_line.c_str());
122                                         cur_line.clear();
123                                 }
124                                 skip_line = false;
125                         } else {
126                                 if(!skip_line) {
127                                         cur_line.push_back(buf[i]);
128                                 }
129                         }
130                 }
131         }
132 }
133
134 void issue_ticket()
135 {
136         ticket++;
137         last_ticket_msec = get_msec();
138
139         CustStat st;
140         st.id = ticket;
141         st.start = time(0);
142         st.end = (time_t)-1;
143         cstat.push_back(st);
144
145
146         if(report_inputs) {
147                 fprintf(fp, "ticket: %d\n", ticket);
148         }
149
150         post_redisplay();
151 }
152
153 void next_customer()
154 {
155         if(customer < ticket) {
156                 customer++;
157                 last_ticket_msec = LONG_MIN;
158
159                 for(size_t i=0; i<cstat.size(); i++) {
160                         if(cstat[i].id == customer) {
161                                 cstat[i].end = time(0);
162                                 fprintf(stderr, "start/end/interval: %lu %lu %lu\n", cstat[i].start,
163                                                 cstat[i].end, cstat[i].end - cstat[i].start);
164                                 break;
165                         }
166                 }
167
168                 if(report_inputs) {
169                         fprintf(fp, "customer: %d\n", customer);
170                 }
171
172                 post_redisplay();
173         }
174 }
175
176 time_t calc_avg_wait()
177 {
178         int count = 0;
179         time_t sum = 0;
180
181         for(size_t i=0; i<cstat.size(); i++) {
182                 if(cstat[i].end != (time_t)-1) {
183                         sum += cstat[i].end - cstat[i].start;
184                         count++;
185                 }
186         }
187
188         return count ? sum / count : 0;
189 }
190
191 #define TICKET_SHOW_DUR         1000
192
193 int get_display_number()
194 {
195         if(get_msec() - last_ticket_msec < TICKET_SHOW_DUR) {
196                 return ticket;
197         }
198         return customer;
199 }
200
201 int get_led_state(int led)
202 {
203         int ledon = get_msec() - last_ticket_msec < TICKET_SHOW_DUR ? 0 : 1;
204         return led == ledon ? 1 : 0;
205 }
206
207 #define VERSTR \
208         "Queue system emulator v0.1"
209
210 static void runcmd(const char *cmd)
211 {
212         printf("DBG: runcmd(\"%s\")\n", cmd);
213
214         switch(cmd[0]) {
215         case 'e':
216                 cmd_echo = !cmd_echo;
217                 fprintf(fp, "OK,turning echo %s\n", cmd_echo ? "on" : "off");
218                 break;
219
220         case 'i':
221                 report_inputs = !report_inputs;
222                 fprintf(fp, "OK,turning input reports %s\n", report_inputs ? "on" : "off");
223                 break;
224
225         case 'v':
226                 fprintf(fp, "OK,%s\n", VERSTR);
227                 break;
228
229         case 'r':
230                 fprintf(fp, "OK,reseting queues\n");
231                 customer = 0;
232                 ticket = 0;
233                 last_ticket_msec = LONG_MIN;
234                 post_redisplay();
235                 break;
236
237         case 't':
238                 fprintf(fp, "OK,ticket: %d\r\n", ticket);
239                 break;
240
241         case 'c':
242                 fprintf(fp, "OK,customer: %d\r\n", customer);
243                 break;
244
245         case 'q':
246                 fprintf(fp, "OK,issuing queue ticket\n");
247                 issue_ticket();
248                 break;
249
250         case 'n':
251                 fprintf(fp, "OK,next customer\n");
252                 next_customer();
253                 break;
254
255         case 'a':
256                 fprintf(fp, "OK,avg wait time: %lu\r\n", (unsigned long)calc_avg_wait());
257                 break;
258
259         case 'h':
260                 fprintf(fp, "OK,commands: (e)cho, (v)ersion, (t)icket, (c)ustomer, "
261                                 "(n)ext, (q)ueue, (a)verage wait time, (r)eset, (i)nput-reports, "
262                                 "(h)elp.\n");
263                 break;
264
265         default:
266                 fprintf(fp, "ERR,unknown command: %s\n", cmd);
267         }
268 }