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

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