-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathReaderClass.java
More file actions
321 lines (264 loc) · 11.4 KB
/
ReaderClass.java
File metadata and controls
321 lines (264 loc) · 11.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
/*The readerclass handles adding items to the database (contained in expiryChecker) and any UDP required to enter fooditems in the database
addingMode is there to allow multiple items to be added into the fridge, duplicate scans are ignored
*/
//This class contains debug messages, each debug line/section contains a DEBUG comment to ease searching for them
//OFFLINE EDITS - look for CHECKFIX in comments to fix possible unknown calls
import java.io.IOException;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.LinkedList;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.net.SocketTimeoutException;
import java.io.PrintStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Date;
class ReaderClass extends Thread{
public static final int datagramLength = 100;
public static final String fifoName = "/var/run/RFID_FIFO";
public static final String remoteDatabaseInetAddressString = "127.0.0.1"; //set this appropriately
public static final int remoteDatabasePort = 4001;
public static final int TAGCODE_LENGTH = 10;
private InetAddress remoteDatabaseInetAddress;
private InetAddress androidInetAddress;
private BufferedReader fifoReader = null;
private String tagBuffer;
private boolean addingMode = false;
private int addingModeTimeout = 30000; //30 second timeout default
private long timeLastAdded;
private Database<FoodItem> db;
private DatagramSocket databaseRequestSocket;
private DatagramSocket androidCommSocket;
/*The println method is overridden because we want to write to files
this could (should) be refactored into a singleton logger class, but for now, this works.
The main starts by redirecting standard out to /var/log/fridgeServerLogs.log */
public static void println(String s){
System.out.println(new Date() + " - " + s);
}
static String getStringFromByteArray(byte[] arr, int indexOfString){
String s = new String(arr);
String[] strings = s.split(FoodItem.matchRegexOpcodeDelimiter);
return strings[indexOfString];
}
public ReaderClass(Database<FoodItem> d){
db = d;
try{
databaseRequestSocket = new DatagramSocket(); //no port specified, we are always sending to the database first, so the database can learn our port
databaseRequestSocket.setSoTimeout(200000); //the database has 20 seconds to respond to a request
androidCommSocket = new DatagramSocket();
androidCommSocket.setSoTimeout(200000); //the android has ~3.5 minutes to respond, since it's waiting for user input
} catch(SocketException e){
println("Error creating datagram socket");
}
try{
remoteDatabaseInetAddress = InetAddress.getByName(remoteDatabaseInetAddressString);
androidInetAddress = InetAddress.getByName(ExpiryChecker.androidInetAddressString);
} catch(UnknownHostException e){
println("No host " + remoteDatabaseInetAddressString);
}
}
public BufferedReader makeBufferedReader(){
BufferedReader r = null;
try{
r = new BufferedReader(new FileReader(fifoName));
} catch(IOException e){
println("Error opening BufferedReader");
}
return r;
}
public void enterAddingMode(){
addingMode = true;
}
public byte[] makeByteArray(byte opcode, byte[] arg1){
//create the byte array
byte[] byteArray = new byte[datagramLength];
byteArray[0] = opcode; //opcode 6 for Not contained in Database
byteArray[1] = FoodItem.opcodeDelimiter.getBytes()[0];
System.arraycopy(arg1, 0, byteArray, 2, arg1.length);
byteArray[arg1.length + 2] = FoodItem.opcodeDelimiter.getBytes()[0];
return byteArray;
}
// public byte[] makeByteArray(byte opcode, byte[] arg1, byte[] arg2){
// //create the byte array
// byte[] byteArray = new byte[datagramLength];
// byteArray[0] = '6'; //opcode 6 for Not contained in Database
// byteArray[1] = FoodItem.opcodeDelimiter.getBytes()[0];
// System.arraycopy(tagCodeAsBytes, 0, byteArray, 2, tagCodeAsBytes.length);
// byteArray[tagCodeAsBytes.length + 2] = FoodItem.opcodeDelimiter.getBytes()[0];
// }
public byte[] charArrayToByteArray(char[] cArr){
byte[] bytes = new byte[cArr.length];
for(int i = 0; i < cArr.length; ++i){
bytes[i] = (byte)(cArr[i]);
}
return bytes;
}
public void enterAddingMode(int newTimeout){
addingMode = true;
addingModeTimeout = newTimeout;
}
public void run(){ //run method that listens to the FIFO, manages 'adding mode' aka 'grocery mode' (see top of file for details), and adds/removes things from the local database
println("FIFO reader is alive");
timeLastAdded = System.currentTimeMillis();
BufferedReader fifoReader = makeBufferedReader();
char[] tagCodeCharArray = new char[TAGCODE_LENGTH];
String tagCode = null;
while(true){
try{
fifoReader.read(tagCodeCharArray, 0, tagCodeCharArray.length);
println("Input from RFID_FIFO " + new String(tagCodeCharArray));
if (System.currentTimeMillis() - timeLastAdded > 30000){ //it's been more than 30 since the last time something was added
addingMode = false; //no longer in adding mode
println("Leaving adding mode");
}
// fifoReader = makeBufferedReader(fifoReader);
} catch (IOException e){
println("IO exception reading from the bufferedReader");
return;
}
// tagCodeCharArray = tagCode.getBytes();
FoodItem iToAdd = null;
int index = db.indexOf(new FoodItem(tagCodeCharArray));
//either scenario creates an iToAdd
if (index == -1){ //the item isn't in the database, fetch it and add it, enter adding mode if we aren't already
println("Item " + new String(tagCodeCharArray) + " not found locally, fetching from remote database");
iToAdd = getItemFromRemoteDatabase(tagCodeCharArray); //it's not already in the fridge, we have to fetch the item from the database
if (iToAdd == null) println("Obtaining an iToAdd failed, no item is being added to the fridge");
addingMode = true;
println("Switching to adding mode");
} else { //the item is in the fridge, remove it if we're not in adding mode, add it again if we are
println("Item found in local database - figuring out what to do");
if(addingMode){
println("In grocery mode, adding a duplicate");
Object t = db.get(index);
if (t instanceof FoodItem){
iToAdd = (FoodItem) t;
}
} else{
println("Not in grocery mode, removing item from fridge");
db.remove(index);
}
}
if (iToAdd != null){ //if it's null, we didn't find it or we removed it from the database instead
iToAdd.renewExpiryDate(); //update the expiry date of the new item, this must be done on creation of a new object
db.add(iToAdd);
timeLastAdded = System.currentTimeMillis(); //we've already checked whether we should leave adding mode
println("Added foodItem to database : " + iToAdd);
}
fifoReader = makeBufferedReader();//make a new reader, this is the only way I can figure out how to clear it so it blocks on the next read
}
}
//tries really hard to return a foodItem, but returns null if the user really,really doesn't want to put something in, even though they scanned something (Users are stupid, right?)
public FoodItem getItemFromRemoteDatabase(char[] tagCode){
byte[] tagCodeAsBytes = charArrayToByteArray(tagCode);
byte opcode = '0';
byte[] byteArray = makeByteArray(opcode, tagCodeAsBytes);
//put the byte array into a packet destined for port 1077 on the database
DatagramPacket p = new DatagramPacket(byteArray, byteArray.length, remoteDatabaseInetAddress, remoteDatabasePort);
//try to send the packet
try{
databaseRequestSocket.send(p);
} catch(IOException e){
println("DatagramSocket error while attempting to send packet");
}
p = new DatagramPacket(new byte[100], 100);
//wait for the database to respond
try{
databaseRequestSocket.receive(p); //we don't need p anymore, we can reuse it
} catch(SocketTimeoutException e){
println("The database did not respond in 20 seconds");
return null;
} catch(IOException e){
println("Error receiving from database");
return null;
}
//get the byte array out of the packet
byteArray = p.getData();
if (byteArray[0] == '2'){
//The database does not have the food item, at some point, we may make a request to the android app, for now, give up and return null
println("Database does not have this tagCode, polling android");
FoodItem item = sendnotContainedToAndroid(tagCode);
if (item == null) {
return null;
} else {
return item;
}
} else if(byteArray[0] == '1') { //the database responded correctly
println("Database response received, processing"); //DEBUG/verbose/log?
println("Database responded with " + new String(byteArray) + " of length " + byteArray.length); //DEBUG
return FoodItem.getFoodItemFromByteArray(tagCode, byteArray);
} else { //the database responded incorrectly
println("The database responded incorrectly to a FoodItem request");
return null;
}
}
//
//Called when the database doesn't contain an item, gets input from the user about a new food item
public FoodItem sendnotContainedToAndroid(char[] tagCode){
byte[] tagCodeAsBytes = charArrayToByteArray(tagCode);
byte opcode = '6';
byte[] byteArray = makeByteArray(opcode, tagCodeAsBytes);
//put the byte array into a packet destined for port 1077 on the database
DatagramPacket p = new DatagramPacket(byteArray, byteArray.length, androidInetAddress, ExpiryChecker.androidPort);
//try to send the packet
try{
androidCommSocket.send(p);
} catch(IOException e){
println("DatagramSocket error while attempting to send packet");
}
try{
androidCommSocket.receive(p);
} catch (SocketTimeoutException e){
println("Android did not respond");
return null;
} catch (IOException e){
println("IO exception sending on androidCommSocket");
return null;
}
println("Packet received from android - parsing");
byteArray = p.getData();
if (byteArray[0] == '1'){
addFoodItemToDatabase(FoodItem.getFoodItemFromByteArray(tagCode, byteArray));
return FoodItem.getFoodItemFromByteArray(tagCode, byteArray);
} else{
return null;
}
}
public void addFoodItemToDatabase(FoodItem f){
byte[] buf = f.to1Packet();
int byteCrawler = 0;
for(int countDownCounter = 3; countDownCounter > 0; byteCrawler++){
if (buf[byteCrawler] == FoodItem.opcodeDelimiter.getBytes()[0]) countDownCounter--;
}
System.arraycopy(charArrayToByteArray(f.getTagCode()), 0, buf, byteCrawler, f.getTagCode().length);
try{
databaseRequestSocket.send(new DatagramPacket(buf, buf.length, remoteDatabaseInetAddress, remoteDatabasePort));
println("New food item added to the remote database records");
} catch(IOException e){
println("Error sending add packet to database");
}
}
public static void main(String[] args) {
File logFile = new File("/var/log/fridgeServerLogs.log");
PrintStream oStream = null;
try{
oStream = new PrintStream(logFile); //true for appending
} catch (FileNotFoundException e){
println("Issue connecting to /var/log/fridgeServerLogs.log - was it deleted?");
}
System.setOut(oStream);
println("Java server running");
Database<FoodItem> database = new Database<FoodItem>();
ReaderClass r = new ReaderClass(database);
Thread fridgeServerReader = new Thread(r);
fridgeServerReader.start();
Thread expiryChecker = new Thread(new ExpiryChecker(database));
expiryChecker.start();
Thread udpListener = new Thread(new UDPListener(r, database));
udpListener.start();
}
}