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
|
package org.reprap.comms.snap;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.Properties;
import org.reprap.Device;
import org.reprap.comms.Address;
import org.reprap.comms.Communicator;
import org.reprap.comms.IncomingContext;
import org.reprap.comms.IncomingMessage;
import org.reprap.comms.OutgoingMessage;
import org.reprap.comms.port.Port;
public class SNAPCommunicator implements Communicator {
private Address localAddress;
private Port port;
private OutputStream writeStream;
private InputStream readStream;
private boolean debugMode;
private CommsLock lock = new CommsLock();
public SNAPCommunicator(Port port, Address localAddress)
throws Exception {
this.localAddress = localAddress;
this.port = port;
writeStream = port.getOutputStream();
readStream = port.getInputStream();
try {
// Try to load debug setting from properties file
Properties props = new Properties();
URL url = ClassLoader.getSystemResource("reprap.properties");
props.load(url.openStream());
debugMode = Boolean.valueOf(props.getProperty("CommsDebug")).booleanValue();
} catch (Exception ex) {
// Fall back to non-debug mode if no setting is available
debugMode = false;
}
}
public void close()
{
if (port != null)
port.close();
port = null;
}
public IncomingContext sendMessage(Device device,
OutgoingMessage messageToSend) throws IOException {
byte [] binaryMessage = messageToSend.getBinary();
SNAPPacket packet = new SNAPPacket((SNAPAddress)localAddress,
(SNAPAddress)device.getAddress(),
binaryMessage);
for(;;) {
if (debugMode) {
System.out.print("TX ");
System.out.print(localAddress.toString());
System.out.print("->");
System.out.print(device.getAddress().toString());
System.out.print(": ");
for(int i = 0; i < binaryMessage.length; i++)
System.out.print(Integer.toHexString(binaryMessage[i]) + " ");
System.out.println("");
}
sendRawMessage(packet);
SNAPPacket ackPacket = receivePacket();
if (ackPacket.isAck())
break;
if (!ackPacket.isNak())
throw new IOException("Received data packet when expecting ACK");
}
IncomingContext replyContext = messageToSend.getReplyContext(this,
device);
return replyContext;
}
private synchronized void sendRawMessage(SNAPPacket packet) throws IOException {
writeStream.write(packet.getRawData());
writeStream.flush();
}
protected synchronized SNAPPacket receivePacket() throws IOException {
SNAPPacket packet = null;
if (debugMode) System.out.print("RX ");
for(;;) {
int c = readStream.read();
if (debugMode) System.out.print(Integer.toHexString(c) + " ");
if (c == -1) throw new IOException();
if (packet == null) {
if (c != 0x54) // Always wait for a sync byte before doing anything
continue;
packet = new SNAPPacket();
}
if (packet.receiveByte((byte)c)) {
// Packet is complete
if (packet.validate()) {
if (debugMode) System.out.println("");
return packet;
} else {
System.out.println("CRC error, NAKing");
/// TODO send NAK
//sendRawMessage(packet.generateNAK());
}
packet = null;
}
}
}
public void receiveMessage(IncomingMessage message) throws IOException {
// Here we collect one packet and notify the message
// of its contents. The message will respond
// to indicate if it wants the message. If not,
// it will be discarded and we will wait for another
// message.
// Since this is a SNAP ring, we have to pass on
// any packets that are not destined for us.
// We will also only pass packets to the message if they are for
// the local address.
for(;;) {
SNAPPacket packet = receivePacket();
if (processPacket(message, packet))
return;
}
}
private boolean processPacket(IncomingMessage message, SNAPPacket packet) throws IOException {
// First ACK the message
if (packet.isAck()) {
System.out.println("Unexpected ACK received instead of message, not supported yet");
return false;
}
/// TODO send ACKs
//sendRawMessage(packet.generateACK());
if (!packet.getDestinationAddress().equals(localAddress)) {
// Not for us, so forward it on
sendRawMessage(packet);
return false;
} else if (message.receiveData(packet.getPayload())) {
// All received as expected
return true;
} else {
// Not interested, wait for more
System.out.println("Ignored and dropped packet");
return false;
}
}
public Address getAddress() {
return localAddress;
}
public void dispose() {
close();
}
public void lock() {
lock.lock();
}
public void unlock() {
lock.unlock();
}
// TODO make a background receiver thread. It can keep a pool of async receive contexts and
// fire them off if anything matching arrives.
// TODO Make a generic message receiver. Use reflection to get correct class.
}
|