dbc2a81483eab315afb6d714e37e1392639b32ce
[tpg/opendispense2.git] / src / server / handler_coke.c
1 /*
2  * OpenDispense 2 
3  * UCC (University [of WA] Computer Club) Electronic Accounting System
4  *
5  * handler_coke.c - Coke controller code
6  *
7  * This file is licenced under the 3-clause BSD Licence. See the file
8  * COPYING for full details.
9  *
10  * NOTES:
11  * - Remember, the coke machine echoes your text back to you!
12  */
13 #include "common.h"
14 #include <stdio.h>
15 #include <string.h>
16 #include <unistd.h>
17 #include <sys/stat.h>
18 #include <fcntl.h>
19 #include <regex.h>
20
21 #define READ_TIMEOUT    2       // 2 seconds for ReadChar
22 #define TRACE_COKE      1
23
24 // === IMPORTS ===
25
26 // === PROTOTYPES ===
27  int    Coke_InitHandler();
28  int    Coke_CanDispense(int User, int Item);
29  int    Coke_DoDispense(int User, int Item);
30  int    WaitForColon();
31  int    ReadLine(int len, char *output);
32
33 // === GLOBALS ===
34 tHandler        gCoke_Handler = {
35         "coke",
36         Coke_InitHandler,
37         Coke_CanDispense,
38         Coke_DoDispense
39 };
40 char    *gsCoke_SerialPort = "/dev/ttyS0";
41  int    giCoke_SerialFD;
42 regex_t gCoke_StatusRegex;
43
44 // == CODE ===
45 int Coke_InitHandler()
46 {
47         printf("connecting to coke machine...\n");
48         
49         giCoke_SerialFD = InitSerial(gsCoke_SerialPort, 9600);
50         if( giCoke_SerialFD == -1 ) {
51                 fprintf(stderr, "ERROR: Unable to open coke serial port ('%s')\n", gsCoke_SerialPort);
52         }
53         
54         CompileRegex(&gCoke_StatusRegex, "^slot\\s+([0-9]+)\\s+([^:]+):([a-zA-Z]+)\\s*", REG_EXTENDED);
55         return 0;
56 }
57
58 int Coke_CanDispense(int UNUSED(User), int Item)
59 {
60         char    tmp[40], *status;
61         regmatch_t      matches[4];
62          int    ret;
63
64         // Sanity please
65         if( Item < 0 || Item > 6 )      return -1;      // -EYOURBAD
66         
67         // Can't dispense if the machine is not connected
68         if( giCoke_SerialFD == -1 )
69                 return -2;
70         
71         #if TRACE_COKE
72         printf("Coke_CanDispense: Flushing\n");
73         #endif
74         
75         
76         // Wait for a prompt
77         ret = 0;
78         while( WaitForColon() && ret < 3 )
79         {
80                 // Flush the input buffer
81                 char    tmpbuf[512];
82                 read(giCoke_SerialFD, tmpbuf, sizeof(tmpbuf));
83                 #if TRACE_COKE
84                 printf("Coke_CanDispense: sending 'd7'\n");
85                 #endif
86                 write(giCoke_SerialFD, "d7\r\n", 4);
87                 ret ++;
88         }
89
90         if( !(ret < 3) ) {
91                 fprintf(stderr, "Coke machine timed out\n");
92                 return -2;      // -EMYBAD
93         }
94
95         // TODO: Handle "not ok" response to D7
96         
97         #if TRACE_COKE
98         printf("Coke_CanDispense: sending 's%i'\n", Item);
99         #endif
100         
101         // Ask the coke machine
102         sprintf(tmp, "s%i\r\n", Item);
103         write(giCoke_SerialFD, tmp, 4);
104
105         #if TRACE_COKE
106         printf("Coke_CanDispense: reading response\n");
107         #endif
108         // Read from the machine (ignoring empty lines)
109         while( (ret = ReadLine(sizeof(tmp)-1, tmp)) == 0 );
110         printf("ret = %i, tmp = '%s'\n", ret, tmp);
111         if( tmp[0] == ':' ) {
112                 ret = ReadLine(sizeof(tmp)-1, tmp);
113                 printf("ret = %i, tmp = '%s'\n", ret, tmp);
114         }
115
116         // Catch an error       
117         if( ret <= 0 ) {
118                 fprintf(stderr, "Coke machine is not being chatty (read = %i)\n", ret);
119                 if( ret == -1 ) {
120                         perror("Coke Machine");
121                 }
122                 return -1;
123         }
124         
125         #if TRACE_COKE
126         printf("Coke_CanDispense: wait for the prompt again\n");
127         #endif
128
129         // Eat rest of response
130         WaitForColon();
131
132         // Parse status response
133         ret = RunRegex(&gCoke_StatusRegex, tmp, sizeof(matches)/sizeof(matches[0]), matches, "Bad Response");
134         if( ret ) {
135                 return -1;
136         }
137
138         // Get slot status
139         tmp[ matches[3].rm_eo ] = '\0';
140         status = &tmp[ matches[3].rm_so ];
141
142         printf("Machine responded slot status '%s'\n", status);
143         
144         #if TRACE_COKE
145         printf("Coke_CanDispense: done");
146         #endif
147
148         if( strcmp(status, "full") == 0 )
149                 return 0;
150
151         return 1;
152 }
153
154 /**
155  * \brief Actually do a dispense from the coke machine
156  */
157 int Coke_DoDispense(int UNUSED(User), int Item)
158 {
159         char    tmp[32];
160          int    i, ret;
161
162         // Sanity please
163         if( Item < 0 || Item > 6 )      return -1;
164
165         // Can't dispense if the machine is not connected
166         if( giCoke_SerialFD == -1 )
167                 return -2;
168         
169         #if TRACE_COKE
170         printf("Coke_DoDispense: flushing input\n");
171         #endif
172         
173         // Wait for prompt
174         ret = 0;
175         while( WaitForColon() && ret < 3 )
176         {
177                 // Flush the input buffer
178                 char    tmpbuf[512];
179                 read(giCoke_SerialFD, tmpbuf, sizeof(tmpbuf));
180                 #if TRACE_COKE
181                 printf("Coke_DoDispense: sending 'd7'\n");
182                 #endif
183                 write(Item, "d7\r\n", 4);
184         }
185
186         #if TRACE_COKE
187         printf("Coke_DoDispense: sending 'd%i'\n", Item);
188         #endif
189         // Dispense
190         sprintf(tmp, "d%i\r\n", Item);
191         write(giCoke_SerialFD, tmp, 4);
192         
193         // Read empty lines
194         while( (ret = ReadLine(sizeof(tmp)-1, tmp)) == -1 );
195         if( ret == -1 ) return -1;
196         // Read d%i
197         while( tmp[0] == ':' ) {
198                 ret = ReadLine(sizeof(tmp)-1, tmp);
199                 if( ret == -1 ) return -1;
200         }
201         // Get status
202         ret = ReadLine(sizeof(tmp)-1, tmp);
203         if( ret == -1 ) return -1;
204
205         WaitForColon(); // Eat up rest of response
206         
207         #if TRACE_COKE
208         printf("Coke_DoDispense: done\n");
209         #endif
210
211         // TODO: Regex
212         if( strcmp(tmp, "ok") == 0 ) {
213                 // We think dispense worked
214                 // - The machine returns 'ok' if an empty slot is dispensed, even if
215                 //   it doesn't actually try to dispense (no sound)
216                 return 0;
217         }
218
219         printf("Machine returned unknown value '%s'\n", tmp);   
220
221         return -1;
222 }
223
224 char ReadChar()
225 {
226         fd_set  readfs;
227         char    ch = 0;
228          int    ret;
229         struct timeval  timeout;
230         
231         timeout.tv_sec = READ_TIMEOUT;
232         timeout.tv_usec = 0;
233         
234         FD_ZERO(&readfs);
235         FD_SET(giCoke_SerialFD, &readfs);
236         
237         ret = select(giCoke_SerialFD+1, &readfs, NULL, NULL, &timeout);
238         if( ret == 0 )  return 0;       // Timeout
239         if( ret != 1 ) {
240                 printf("readchar return %i\n", ret);
241                 return 0;
242         }
243         
244         ret = read(giCoke_SerialFD, &ch, 1);
245         if( ret != 1 ) {
246                 printf("ret = %i\n", ret);
247                 return 0;
248         }
249         
250         return ch;
251 }
252
253 int WaitForColon()
254 {
255         fd_set  readfs;
256         char    ch = 0;
257         
258         FD_SET(giCoke_SerialFD, &readfs);
259         
260         while( (ch = ReadChar()) != ':' && ch != 0);
261         
262         if( ch == 0 )   return -1;      // Timeout
263         
264         return 0;
265 }
266
267 int ReadLine(int len, char *output)
268 {
269         char    ch;
270          int    i = 0;
271         
272         for(;;)
273         {
274                 ch = ReadChar();
275                         
276                 if( i < len )
277                         output[i++] = ch;
278                 
279                 if( ch == '\0' ) {
280                         break;
281                 }
282                 if( ch == '\n' || ch == '\r' ) {
283                         if( i < len )
284                                 output[--i] = '\0';
285                         break;
286                 }
287         }
288
289         //printf("ReadLine: output=%s\n", output);
290
291         if( !ch )       return -1;
292         return i;
293 }
294
295

UCC git Repository :: git.ucc.asn.au