Subversion Repositories Projects

Compare Revisions

Ignore whitespace Rev 1687 → Rev 1688

/dongfang_FC_rewrite_tool/src/dongfang/mkt/RequestFrameVisitor.java
14,13 → 14,15
import dongfang.mkt.frames.OSDDataRequestFrame;
import dongfang.mkt.frames.ReadExternalControlRequestFrame;
import dongfang.mkt.frames.ReadIMUConfigurationRequestFrame;
import dongfang.mkt.frames.ReadMotorMixerRequestFrame;
import dongfang.mkt.frames.ReadParamSetRequestFrame;
import dongfang.mkt.frames.ReadVariablesRequestFrame;
import dongfang.mkt.frames.ResetRequestFrame;
import dongfang.mkt.frames.SetCompassHeadingRequestFrame;
import dongfang.mkt.frames.SingleDisplayRequestFrame;
import dongfang.mkt.frames.VariablesRequestFrame;
import dongfang.mkt.frames.VersionRequestFrame;
import dongfang.mkt.frames.WriteIMUConfigurationRequestFrame;
import dongfang.mkt.frames.WriteMotorMixerRequestFrame;
import dongfang.mkt.frames.WriteParamSetRequestFrame;
 
public interface RequestFrameVisitor {
33,16 → 35,18
void visit(AttitudeDataRequestFrame f) throws IOException;
void visit(SingleDisplayRequestFrame f) throws IOException;
void visit(AllDisplaysRequestFrame f) throws IOException;
void visit(VariablesRequestFrame f) throws IOException;
void visit(ReadVariablesRequestFrame f) throws IOException;
void visit(ExternalControlRequestFrame f) throws IOException;
void visit(ResetRequestFrame f) throws IOException;
void visit(ChangeParameterSetRequestFrame f) throws IOException;
void visit(ReadParamSetRequestFrame f) throws IOException;
void visit(ReadIMUConfigurationRequestFrame f) throws IOException;
void visit(ReadMotorMixerRequestFrame f) throws IOException;
void visit(WriteParamSetRequestFrame f) throws IOException;
void visit(SetCompassHeadingRequestFrame f) throws IOException;
void visit(ReadExternalControlRequestFrame f) throws IOException;
void visit(OSDDataRequestFrame f) throws IOException;
void visit(CompassHeadingRequestFrame f) throws IOException;
void visit(ReadIMUConfigurationRequestFrame f) throws IOException;
void visit(WriteMotorMixerRequestFrame f) throws IOException;
void visit(WriteIMUConfigurationRequestFrame f) throws IOException;
}
/dongfang_FC_rewrite_tool/src/dongfang/mkt/comm/MKInputStream.java
17,12 → 17,14
import dongfang.mkt.frames.OSDDataResponseFrame;
import dongfang.mkt.frames.ReadExternalControlResponseFrame;
import dongfang.mkt.frames.ReadIMUConfigurationResponseFrame;
import dongfang.mkt.frames.ReadMotorMixerResponseFrame;
import dongfang.mkt.frames.ResponseFrame;
import dongfang.mkt.frames.SetCompassHeadingResponseFrame;
import dongfang.mkt.frames.ReadParamSetResponseFrame;
import dongfang.mkt.frames.WriteIMUConfigurationResponseFrame;
import dongfang.mkt.frames.WriteMotorMixerResponseFrame;
import dongfang.mkt.frames.WriteParamSetResponseFrame;
import dongfang.mkt.frames.VariablesResponseFrame;
import dongfang.mkt.frames.ReadVariablesResponseFrame;
import dongfang.mkt.frames.VersionResponseFrame;
 
public class MKInputStream extends InputStream {
259,6 → 261,39
result = f;
break;
}
case 'M': {
WriteMotorMixerResponseFrame f = new WriteMotorMixerResponseFrame(address);
f.setWasAccepted(base64InputStream.readByte() != 0);
}
case 'N': {
int numMotors = 12;
ReadMotorMixerResponseFrame f = new ReadMotorMixerResponseFrame();
f.setConfigurationVersion(base64InputStream.readByte());
int length = base64InputStream.readByte();
f.setDataLength(length);
int testLength = 0;
int[][] matrix = new int[numMotors][];
for(int i=0; i<numMotors; i++) {
int[] row = new int[4];
for(int j=0; j<4; j++) {
matrix[i][j] = base64InputStream.readByte();
testLength++;
}
}
if (length != testLength)
throw new IOException("Length of motor mixer data was not as expected.");
/*
int[] opposite = new int[numMotors];
for(int i=0; i<numMotors; i++) {
opposite[i] = base64InputStream.readByte();
}
*/
f.setMatrix(matrix);
 
result = f;
}
case 'O': {
OSDDataResponseFrame f = new OSDDataResponseFrame(address);
f.setVersion(base64InputStream.readByte());
366,7 → 401,7
// This is my own creation. The ID collides with the waypoint one of FC.
case 'X': {
VariablesResponseFrame f = new VariablesResponseFrame(address);
ReadVariablesResponseFrame f = new ReadVariablesResponseFrame(address);
f.setVariables(base64InputStream.readSignedWords(8));
result = f;
break;
/dongfang_FC_rewrite_tool/src/dongfang/mkt/comm/MKOutputStream.java
16,14 → 16,16
import dongfang.mkt.frames.OSDDataRequestFrame;
import dongfang.mkt.frames.ReadExternalControlRequestFrame;
import dongfang.mkt.frames.ReadIMUConfigurationRequestFrame;
import dongfang.mkt.frames.ReadMotorMixerRequestFrame;
import dongfang.mkt.frames.ReadParamSetRequestFrame;
import dongfang.mkt.frames.ReadVariablesRequestFrame;
import dongfang.mkt.frames.RequestFrame;
import dongfang.mkt.frames.ResetRequestFrame;
import dongfang.mkt.frames.SetCompassHeadingRequestFrame;
import dongfang.mkt.frames.SingleDisplayRequestFrame;
import dongfang.mkt.frames.VariablesRequestFrame;
import dongfang.mkt.frames.VersionRequestFrame;
import dongfang.mkt.frames.WriteIMUConfigurationRequestFrame;
import dongfang.mkt.frames.WriteMotorMixerRequestFrame;
import dongfang.mkt.frames.WriteParamSetRequestFrame;
 
public class MKOutputStream extends OutputStream implements RequestFrameVisitor {
162,8 → 164,29
base64OutputStream.writeByte(f.getPageOrder().getRemoteKeys());
// mdo.writeByte(f.getAutoSendInterval());
}
public void visit(ReadMotorMixerRequestFrame f) throws IOException {
writeByte('n');
}
 
public void visit(VariablesRequestFrame f) throws IOException {
public void visit(WriteMotorMixerRequestFrame f) throws IOException {
writeByte('m');
writeByte(f.getConfigurationVersion());
writeByte(f.getDataLength());
for(int i=0; i<f.getMatrix().length; i++) {
int[] row = f.getMatrix()[i];
for(int j=0; j<row.length; j++) {
base64OutputStream.writeByte(row[j]);
}
}
/*
for(int i=0; i<f.getOppositeMotors().length; i++) {
base64OutputStream.writeByte(f.getOppositeMotors()[i]);
}
*/
}
 
public void visit(ReadVariablesRequestFrame f) throws IOException {
writeByte('x');
}
 
/dongfang_FC_rewrite_tool/src/dongfang/mkt/configuration/MotorMixer.java
0,0 → 1,120
package dongfang.mkt.configuration;
 
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
 
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
 
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
 
import dongfang.mkt.frames.WriteParamSetRequestFrame;
 
public class MotorMixer {
public static final int EEPROMVERSION = 11;
public static final int MAX_MOTORS = 12;
public static final int MIX_PITCH = 0;
public static final int MIX_ROLL = 1;
public static final int MIX_THROTTLE = 2;
public static final int MIX_YAW = 3;
public static final int MIX_OPPOSITE_MOTOR = 4;
 
private int getByteCount() {
return MAX_MOTORS * 4 + MAX_MOTORS;
}
void rowToXML(int[] row, StringBuilder out) {
out.append("<motor throttlePart=\"" + row[MIX_THROTTLE] + "\" pitchPart=\"" + row[MIX_PITCH] + "\" rollPart=\"" + row[MIX_ROLL] + "\" yawPart=\"" + row[MIX_YAW] + "\" oppositeMotor=\"" + row[MIX_OPPOSITE_MOTOR] + "\"/>");
}
private int[][] matrix = new int[MAX_MOTORS][];
 
public void setMatrix(int[][] matrix) {
this.matrix = matrix;
}
 
public int[][] toBinary() {
return matrix;
}
public String toXML() {
StringBuilder result = new StringBuilder();
result.append("<motorMixer eepromVersion=\"" + EEPROMVERSION + "\" length=\"" + getByteCount() + "\">\n");
for (int i = 0; i < MAX_MOTORS; i++) {
int[] row = matrix[i];
result.append(" ");
this.rowToXML(row, result);
result.append("\n");
}
result.append("</motorMixer>\n");
return result.toString();
}
private void parseXMLParameterSet(InputStream input) throws IOException {
DocumentBuilderFactory saxfac = DocumentBuilderFactory.newInstance();
saxfac.setValidating(false);
try {
DocumentBuilder bldr = saxfac.newDocumentBuilder();
Document doc = bldr.parse(input);
 
XPath xpath = XPathFactory.newInstance().newXPath();
String s_eepromVersion = xpath.evaluate("/motorMixer/@eepromVersion", doc);
int eepromVersion;
 
try {
eepromVersion = Integer.parseInt(s_eepromVersion);
} catch (NumberFormatException ex) {
System.err
.println("The motorMixer element must have an 'eepromVersion' attribute with a numerical value (eg.<motorMixer eepromVersion='1'>)");
System.exit(-1);
}
 
NodeList rowNodes = (NodeList) xpath.evaluate(
"/motorMixer/motor", doc, XPathConstants.NODESET);
 
for (int i = 0; i < rowNodes.getLength(); i++) {
Element e = (Element) rowNodes.item(i);
int[] row = new int[5];
String s_value = e.getAttribute("throttle");
row[MIX_THROTTLE] = Integer.parseInt(s_value);
 
s_value = e.getAttribute("pitch");
row[MIX_PITCH] = Integer.parseInt(s_value);
 
s_value = e.getAttribute("roll");
row[MIX_ROLL] = Integer.parseInt(s_value);
 
s_value = e.getAttribute("yaw");
row[MIX_YAW] = Integer.parseInt(s_value);
 
s_value = e.getAttribute("oppositeMotor");
row[MIX_OPPOSITE_MOTOR] = Integer.parseInt(s_value);
 
matrix[i] = row;
}
input.close();
} catch (ParserConfigurationException ex) {
// Should never happen.
throw new RuntimeException(ex);
} catch (SAXException ex) {
System.err.println(ex);
System.err
.println("There is something screwed with your XML document. It is not well-formed and won't parse.");
throw new RuntimeException("Parse error.");
} catch (XPathExpressionException ex) {
// Should never happen.
throw new RuntimeException(ex);
}
}
}
/dongfang_FC_rewrite_tool/src/dongfang/mkt/frames/VariablesResponseFrame.java
File deleted
/dongfang_FC_rewrite_tool/src/dongfang/mkt/frames/VariablesRequestFrame.java
File deleted
/dongfang_FC_rewrite_tool/src/dongfang/mkt/frames/FrameFactory.java
15,7 → 15,7
ReadParamSetRequestFrame createReadParamSetRequestFrame(int address);
ResetRequestFrame createResetRequestFrame(int address);
SingleDisplayRequestFrame createSingleDisplayRequestFrame(int address);
VariablesRequestFrame createVariablesRequestFrame(int address);
ReadVariablesRequestFrame createVariablesRequestFrame(int address);
VersionRequestFrame createVersionRequestFrame(int address);
// WriteParamSetRequestFrame createWriteParamSetRequestFrame(ParamSet paramSet);
}
/dongfang_FC_rewrite_tool/src/dongfang/mkt/frames/ReadMotorMixerRequestFrame.java
0,0 → 1,17
package dongfang.mkt.frames;
 
import java.io.IOException;
 
import dongfang.mkt.RequestFrameVisitor;
 
 
public class ReadMotorMixerRequestFrame extends RequestFrame {
public ReadMotorMixerRequestFrame() {
super(FC_ADDRESS);
}
 
@Override
public void accept(RequestFrameVisitor o) throws IOException {
o.visit(this);
}
}
/dongfang_FC_rewrite_tool/src/dongfang/mkt/frames/ReadMotorMixerResponseFrame.java
0,0 → 1,68
package dongfang.mkt.frames;
 
public class ReadMotorMixerResponseFrame extends ResponseFrame {
private int[][] matrix;
// private int[] oppositeMotors;
private int configurationVersion;
private int dataLength; // not used really...
 
public ReadMotorMixerResponseFrame() {
super(FC_ADDRESS);
}
 
@Override
public boolean isResponseTo(RequestFrame r) {
return r instanceof ReadMotorMixerRequestFrame;
}
 
public int getConfigurationVersion() {
return configurationVersion;
}
 
public void setConfigurationVersion(int configurationVersion) {
this.configurationVersion = configurationVersion;
}
 
public int[][] getMatrix() {
return matrix;
}
 
public void setMatrix(int[][] matrix) {
this.matrix = matrix;
}
/*
public int[] getOppositeMotors() {
return oppositeMotors;
}
public void setOppositeMotors(int[] oppositeMotors) {
this.oppositeMotors = oppositeMotors;
}
*/
public int getDataLength() {
return dataLength;
}
public void setDataLength(int dataLength) {
this.dataLength = dataLength;
}
public String toString() {
StringBuilder result = new StringBuilder(getClass().getSimpleName() + ": ");
if (matrix != null) {
for (int i=0; i<matrix.length; i++) {
result.append(i + "->");
result.append("Pitch: " + matrix[i][0]);
result.append("Roll: " + matrix[i][1]);
result.append("Throttle: " + matrix[i][2]);
result.append("Yaw: " + matrix[i][3]);
result.append("Opposite motor: " + matrix[i][4]);
if (i<matrix.length-1) result.append("\n");
}
}
return result.toString();
}
}
 
/dongfang_FC_rewrite_tool/src/dongfang/mkt/frames/ReadVariablesRequestFrame.java
0,0 → 1,17
package dongfang.mkt.frames;
 
import java.io.IOException;
 
import dongfang.mkt.RequestFrameVisitor;
 
 
public class ReadVariablesRequestFrame extends RequestFrame {
public ReadVariablesRequestFrame(int address) {
super(address);
}
 
@Override
public void accept(RequestFrameVisitor o) throws IOException {
o.visit(this);
}
}
/dongfang_FC_rewrite_tool/src/dongfang/mkt/frames/ReadVariablesResponseFrame.java
0,0 → 1,34
package dongfang.mkt.frames;
 
public class ReadVariablesResponseFrame extends ResponseFrame {
private int[] variables;
 
public ReadVariablesResponseFrame(int address) {
super(address);
}
 
@Override
public boolean isResponseTo(RequestFrame r) {
return r instanceof ReadVariablesRequestFrame;
}
 
public int[] getVariables() {
return variables;
}
 
public void setVariables(int[] variables) {
this.variables = variables;
}
public String toString() {
String result = getClass().getSimpleName() + ": ";
if (variables != null) {
for (int i=0; i<variables.length; i++) {
result += i + "->" + variables[i];
if (i<variables.length-1)
result += ", ";
}
}
return result;
}
}
/dongfang_FC_rewrite_tool/src/dongfang/mkt/frames/RequestFrameVisitor.java
2,7 → 2,7
 
import java.io.IOException;
 
 
/*
public interface RequestFrameVisitor {
// void visit(RequestFrame f) throws IOException;
void visit(AnalogDebugLabelRequestFrame f) throws IOException;
13,7 → 13,7
void visit(AttitudeDataRequestFrame f) throws IOException;
void visit(SingleDisplayRequestFrame f) throws IOException;
void visit(AllDisplaysRequestFrame f) throws IOException;
void visit(VariablesRequestFrame f) throws IOException;
void visit(ReadVariablesRequestFrame f) throws IOException;
void visit(ExternalControlRequestFrame f) throws IOException;
void visit(ResetRequestFrame f) throws IOException;
void visit(ChangeParameterSetRequestFrame f) throws IOException;
23,4 → 23,5
void visit(ReadExternalControlRequestFrame f) throws IOException;
void visit(OSDDataRequestFrame f) throws IOException;
void visit(CompassHeadingRequestFrame f) throws IOException;
}
}
*/
/dongfang_FC_rewrite_tool/src/dongfang/mkt/frames/WriteMotorMixerRequestFrame.java
0,0 → 1,51
package dongfang.mkt.frames;
 
import java.io.IOException;
 
import dongfang.mkt.RequestFrameVisitor;
 
 
public class WriteMotorMixerRequestFrame extends RequestFrame {
private int[][] matrix;
// private int[] oppositeMotors;
private int motorMixerVersionNumber;
private int configurationVersion;
public WriteMotorMixerRequestFrame(int motorMixerVersionNumber, int[][] matrix /*, int[] oppositeMotors*/) {
super(FC_ADDRESS);
this.motorMixerVersionNumber = motorMixerVersionNumber;
this.matrix = matrix;
// this.oppositeMotors = oppositeMotors;
}
@Override
public void accept(RequestFrameVisitor o) throws IOException {
o.visit(this);
}
public int getMotorMixerVersionNumber() {
return motorMixerVersionNumber;
}
 
public int[][] getMatrix() {
return matrix;
}
 
/*
public int[] getOppositeMotors() {
return oppositeMotors;
}
*/
 
public int getConfigurationVersion() {
return configurationVersion;
}
 
public void setConfigurationVersion(int configurationVersion) {
this.configurationVersion = configurationVersion;
}
public int getDataLength() {
return matrix.length * 5; // return matrix.length * 4 + oppositeMotors.length;*/
}
}
/dongfang_FC_rewrite_tool/src/dongfang/mkt/frames/WriteMotorMixerResponseFrame.java
0,0 → 1,22
package dongfang.mkt.frames;
 
public class WriteMotorMixerResponseFrame extends ResponseFrame {
private boolean wasAccepted;
 
public WriteMotorMixerResponseFrame(int address) {
super(address);
}
 
@Override
public boolean isResponseTo(RequestFrame r) {
return r instanceof WriteMotorMixerRequestFrame;
}
public boolean wasAccepted() {
return wasAccepted;
}
public void setWasAccepted(boolean wasAccepted) {
this.wasAccepted = wasAccepted;
}
}
/dongfang_FC_rewrite_tool/src/dongfang/mkt/main/CodeGenerator.java
7,7 → 7,7
 
public class CodeGenerator {
static void generateDynamicSubstitutionCode(int paramSetVersion) throws IOException {
ConfigurationSet cs = ParameterSet.parseXMLConfigSet(paramSetVersion);
ConfigurationSet cs = ParameterSet.parseXMLParameterSet(paramSetVersion);
System.out.println(cs.generateDynamicSubstitutionCode());
}
/dongfang_FC_rewrite_tool/src/dongfang/mkt/main/MotorMixerConfigurator.java
0,0 → 1,110
package dongfang.mkt.main;
 
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
 
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
 
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
 
import dongfang.mkt.comm.FrameQueue;
import dongfang.mkt.comm.MKConnection;
import dongfang.mkt.comm.serial.RXTXSerialPort;
import dongfang.mkt.configuration.IMUConfiguration;
import dongfang.mkt.configuration.MotorMixer;
import dongfang.mkt.frames.ReadMotorMixerRequestFrame;
import dongfang.mkt.frames.ReadMotorMixerResponseFrame;
import dongfang.mkt.frames.WriteIMUConfigurationRequestFrame;
import dongfang.mkt.frames.WriteMotorMixerRequestFrame;
import dongfang.mkt.frames.WriteMotorMixerResponseFrame;
 
public class MotorMixerConfigurator {
private static void configure(String portIdentifier, WriteMotorMixerRequestFrame frame) throws IOException {
MKConnection port = new RXTXSerialPort();
port.init(portIdentifier);
FrameQueue q = new FrameQueue(port);
 
q.sendRequest(frame);
WriteMotorMixerResponseFrame r = (WriteMotorMixerResponseFrame) q.getResponseFor(frame, 5000);
if (r == null) {
System.err.println("ERROR. Timeout waiting for response.");
} else if (!r.wasAccepted()) {
System.err
.println("ERROR. Motor mixer not accepted. Check version against MK firmware EEPROM mixer version.");
} else {
System.out.println("Saved motor mixer.");
}
 
q.kill();
}
 
private static void writeConfiguration(String portIdentifier, String fileName) throws IOException {
System.out.println("Writing motor mixer from file: " + fileName);
InputStream inputStream = new FileInputStream(fileName);
MotorMixer mm = new MotorMixer();
mm.parseXMLMotorMixer(inputStream);
WriteMotorMixerRequestFrame frame = new WriteMotorMixerRequestFrame(1, mm.toBinary());
configure(portIdentifier, frame);
}
 
private static MotorMixer readMotorMixer(String portIdentifier) throws IOException {
MKConnection port = new RXTXSerialPort();
port.init(portIdentifier);
FrameQueue q = new FrameQueue(port);
MotorMixer mm = new MotorMixer();
 
ReadMotorMixerRequestFrame frame = new ReadMotorMixerRequestFrame();
q.sendRequest(frame);
ReadMotorMixerResponseFrame r = (ReadMotorMixerResponseFrame) q.getResponseFor(frame, 5000);
if (r == null) {
System.err.println("ERROR. Timeout waiting for response.");
} else {
int[][] matrix = r.getMatrix();
mm.setMatrix(matrix);
System.out.println(mm.toXML());
}
q.kill();
return mm;
}
 
private static void readMotorMixer(String portIdentifier, String fileName) throws IOException {
MotorMixer mm = readMotorMixer(portIdentifier);
if (mm != null) {
FileWriter fw = new FileWriter(fileName);
fw.write(mm.toXML());
fw.close();
}
}
 
static void help() {
System.err.println("Usage: MotorMixerConfigurator r [filename to write to]");
System.err.println("Usage: MotorMixerConfigurator w [filename to read from]");
System.exit(-1);
}
public static void main(String[] args) throws IOException {
if (!"r".equals(args[0]) && !"w".equals(args[0])) help();
if ("w".equals(args[0]) && (args.length!=2)) help();
if ("r".equals(args[0]) && (args.length!=2)) help();
 
String portIdentifier = null;
if ("r".equals(args[0])) {
readMotorMixer(portIdentifier, args[1]);
} else {
writeConfiguration(portIdentifier, args[1]);
}
System.exit(0);
}
}
/dongfang_FC_rewrite_tool/src/dongfang/mkt/main/VariablesReader.java
6,8 → 6,8
import dongfang.mkt.comm.MKConnection;
import dongfang.mkt.comm.serial.RXTXSerialPort;
import dongfang.mkt.frames.RequestFrame;
import dongfang.mkt.frames.VariablesRequestFrame;
import dongfang.mkt.frames.VariablesResponseFrame;
import dongfang.mkt.frames.ReadVariablesRequestFrame;
import dongfang.mkt.frames.ReadVariablesResponseFrame;
 
public class VariablesReader {
private static int[] readVariables(String portIdentifier) throws IOException {
16,9 → 16,9
FrameQueue q = new FrameQueue(port);
int[] variables = null;
VariablesRequestFrame frame = new VariablesRequestFrame(RequestFrame.FC_ADDRESS);
ReadVariablesRequestFrame frame = new ReadVariablesRequestFrame(RequestFrame.FC_ADDRESS);
q.sendRequest(frame);
VariablesResponseFrame r = (VariablesResponseFrame) q.getResponseFor(frame, 5000);
ReadVariablesResponseFrame r = (ReadVariablesResponseFrame) q.getResponseFor(frame, 5000);
if (r == null) {
System.err.println("ERROR. Timeout waiting for response.");
} else {