a6b3cb934e1c8e799584f54a633866fda647f1d5
[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 // === IMPORTS ===
22
23 // === PROTOTYPES ===
24  int    Coke_InitHandler();
25  int    Coke_CanDispense(int User, int Item);
26  int    Coke_DoDispense(int User, int Item);
27  int    WaitForColon();
28  int    ReadLine(int len, char *output);
29
30 // === GLOBALS ===
31 tHandler        gCoke_Handler = {
32         "coke",
33         Coke_InitHandler,
34         Coke_CanDispense,
35         Coke_DoDispense
36 };
37 char    *gsCoke_SerialPort = "/dev/ttyS0";
38  int    giCoke_SerialFD;
39 regex_t gCoke_StatusRegex;
40
41 // == CODE ===
42 int Coke_InitHandler()
43 {
44         printf("connecting to coke machine...\n");
45         
46         giCoke_SerialFD = InitSerial(gsCoke_SerialPort, 9600);
47         if( giCoke_SerialFD == -1 ) {
48                 fprintf(stderr, "ERROR: Unable to open coke serial port ('%s')\n", gsCoke_SerialPort);
49         }
50         
51         CompileRegex(&gCoke_StatusRegex, "^slot\\s+([0-9]+)\\s+([^:]+):([a-zA-Z]+)\\s*", REG_EXTENDED);
52         return 0;
53 }
54
55 int Coke_CanDispense(int UNUSED(User), int Item)
56 {
57         char    tmp[40], *status;
58         regmatch_t      matches[4];
59          int    ret;
60
61         // Sanity please
62         if( Item < 0 || Item > 6 )      return -1;      // -EYOURBAD
63         
64         // Can't dispense if the machine is not connected
65         if( giCoke_SerialFD == -1 )
66                 return -2;
67         
68         // Flush the input buffer
69         {
70                 char    tmpbuf[512];
71                 read(giCoke_SerialFD, tmpbuf, sizeof(tmpbuf));
72         }
73         
74         // Wait for a prompt
75         ret = 0;
76         do {
77                 write(giCoke_SerialFD, "d7\r\n", 4);
78         } while( WaitForColon() && ret++ < 3 );
79
80         if( ret == 3 ) {
81                 fprintf(stderr, "Coke machine timed out\n");
82                 return -2;      // -EMYBAD
83         }
84
85         // TODO: Handle "not ok" response to D7
86         
87         // Ask the coke machine
88         sprintf(tmp, "s%i\r\n", Item);
89         write(giCoke_SerialFD, tmp, 4);
90
91         // Read from the machine (ignoring empty lines)
92         while( (ret = ReadLine(sizeof(tmp)-1, tmp)) == 0 );
93         printf("ret = %i, tmp = '%s'\n", ret, tmp);
94         if( tmp[0] == ':' ) {
95                 ret = ReadLine(sizeof(tmp)-1, tmp);
96                 printf("ret = %i, tmp = '%s'\n", ret, tmp);
97         }
98
99         // Catch an error       
100         if( ret <= 0 ) {
101                 fprintf(stderr, "Coke machine is not being chatty (read = %i)\n", ret);
102                 if( ret == -1 ) {
103                         perror("Coke Machine");
104                 }
105                 return -1;
106         }
107
108         // Eat rest of response
109         WaitForColon();
110
111         // Parse status response
112         ret = RunRegex(&gCoke_StatusRegex, tmp, sizeof(matches)/sizeof(matches[0]), matches, "Bad Response");
113         if( ret ) {
114                 return -1;
115         }
116
117         // Get slot status
118         tmp[ matches[3].rm_eo ] = '\0';
119         status = &tmp[ matches[3].rm_so ];
120
121         printf("Machine responded slot status '%s'\n", status);
122
123         if( strcmp(status, "full") == 0 )
124                 return 0;
125
126         return 1;
127 }
128
129 /**
130  * \brief Actually do a dispense from the coke machine
131  */
132 int Coke_DoDispense(int UNUSED(User), int Item)
133 {
134         char    tmp[32];
135          int    i, ret;
136
137         // Sanity please
138         if( Item < 0 || Item > 6 )      return -1;
139
140         // Can't dispense if the machine is not connected
141         if( giCoke_SerialFD == -1 )
142                 return -2;
143         
144         // Flush the input buffer
145         {
146                 char    tmpbuf[512];
147                 read(giCoke_SerialFD, tmpbuf, sizeof(tmpbuf));
148         }
149
150         // Wait for prompt
151         i = 0;
152         do {
153                 write(Item, "d7\r\n", 4);
154         } while( WaitForColon() && i++ < 3 );
155
156         // Dispense
157         sprintf(tmp, "d%i\r\n", Item);
158         write(giCoke_SerialFD, tmp, 4);
159         
160         // Read empty lines
161         while( (ret = ReadLine(sizeof(tmp)-1, tmp)) == -1 );
162         if( ret == -1 ) return -1;
163         // Read d%i
164         while( tmp[0] == ':' ) {
165                 ret = ReadLine(sizeof(tmp)-1, tmp);
166                 if( ret == -1 ) return -1;
167         }
168         // Get status
169         ret = ReadLine(sizeof(tmp)-1, tmp);
170         if( ret == -1 ) return -1;
171
172         WaitForColon(); // Eat up rest of response
173
174         // TODO: Regex
175         if( strcmp(tmp, "ok") == 0 ) {
176                 // We think dispense worked
177                 // - The machine returns 'ok' if an empty slot is dispensed, even if
178                 //   it doesn't actually try to dispense (no sound)
179                 return 0;
180         }
181
182         printf("Machine returned unknown value '%s'\n", tmp);   
183
184         return -1;
185 }
186
187 char ReadChar()
188 {
189         fd_set  readfs;
190         char    ch = 0;
191          int    ret;
192         struct timeval  timeout;
193         
194         timeout.tv_sec = 5;     // 5 second timeout
195         timeout.tv_usec = 0;
196         
197         FD_ZERO(&readfs);
198         FD_SET(giCoke_SerialFD, &readfs);
199         
200         ret = select(giCoke_SerialFD+1, &readfs, NULL, NULL, &timeout);
201         if( ret == 0 )  return 0;       // Timeout
202         if( ret != 1 ) {
203                 printf("readchar return %i\n", ret);
204                 return 0;
205         }
206         
207         ret = read(giCoke_SerialFD, &ch, 1);
208         if( ret != 1 ) {
209                 printf("ret = %i\n", ret);
210                 return 0;
211         }
212         
213         return ch;
214 }
215
216 int WaitForColon()
217 {
218         fd_set  readfs;
219         char    ch = 0;
220         
221         FD_SET(giCoke_SerialFD, &readfs);
222         
223         while( (ch = ReadChar()) != ':' && ch != 0);
224         
225         if( ch == 0 )   return -1;      // Timeout
226         
227         return 0;
228 }
229
230 int ReadLine(int len, char *output)
231 {
232         char    ch;
233          int    i = 0;
234         
235         for(;;)
236         {
237                 ch = ReadChar();
238                         
239                 if( i < len )
240                         output[i++] = ch;
241                 
242                 if( ch == '\0' ) {
243                         break;
244                 }
245                 if( ch == '\n' || ch == '\r' ) {
246                         if( i < len )
247                                 output[--i] = '\0';
248                         break;
249                 }
250         }
251
252         //printf("ReadLine: output=%s\n", output);
253
254         if( !ch )       return -1;
255         return i;
256 }
257
258

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