/*
* Catroid: An on-device visual programming system for Android devices
* Copyright (C) 2010-2016 The Catrobat Team
* (<http://developer.catrobat.org/credits>)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* An additional term exception under section 7 of the GNU Affero
* General Public License, version 3, is available at
* http://developer.catrobat.org/license_additional_term
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.catrobat.catroid.common.bluetooth.models;
import org.catrobat.catroid.devices.mindstorms.ev3.EV3CommandByte;
import org.catrobat.catroid.devices.mindstorms.ev3.EV3CommandByte.EV3CommandOpCode;
import org.catrobat.catroid.devices.mindstorms.ev3.EV3CommandType;
import org.catrobat.catroid.devices.mindstorms.ev3.sensors.EV3Sensor;
import org.catrobat.catroid.devices.mindstorms.ev3.sensors.EV3SensorMode;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Random;
public class MindstormsEV3TestModel implements DeviceModel {
public static final byte DIRECT_REPLY = 0x02;
public static final byte DIRECT_REPLY_ERROR = 0x04;
private boolean isRunning = true;
private boolean replyRequired;
private EV3Sensor.Sensor[] sensors = { EV3Sensor.Sensor.NO_SENSOR, EV3Sensor.Sensor.NO_SENSOR,
EV3Sensor.Sensor.NO_SENSOR, EV3Sensor.Sensor.NO_SENSOR };
private byte[] portSensorMode = { 0, 0, 0, 0 };
private int[] portSensorValue = { 255, 255, 255, 255 };
private boolean[] portSensorActive = { false, false, false, false };
private int keepAliveTime = 0;
private boolean keepAliveSet = false;
protected byte[] createResponseFromClientRequest(byte[] message) {
byte[] msgNumber = { 0, 0 };
msgNumber[0] = message[0];
msgNumber[1] = message[1];
byte commandType = message[2];
byte commandOpCode = message[5];
EV3CommandOpCode opCode = EV3CommandOpCode.getOpCodeByValue(commandOpCode);
switch (opCode) {
case OP_UI_READ:
return new byte[0];
case OP_INPUT_DEVICE:
return handleInputDeviceMessage(message, msgNumber);
case OP_INPUT_READ:
return handleInputReadMessage(message, msgNumber);
case OP_INPUT_READ_SI:
return handleInputReadSiMessage(message, msgNumber);
case OP_KEEP_ALIVE:
return handleKeepAliveMessage(message);
default:
return handleUnknownMessage(msgNumber, commandType);
}
}
private byte[] handleKeepAliveMessage(byte[] message) {
replyRequired = false;
int keepAliveTime = message[6];
setKeepAliveTime(keepAliveTime);
return new byte[0];
}
private byte[] handleInputDeviceMessage(byte[] message, byte[] messageNumber) {
byte[] reply = null;
int port = -1;
boolean msgValid = true;
if (message.length < 10) {
msgValid = false;
} else {
port = message[8];
}
replyRequired = true;
if (message[6] == EV3CommandByte.EV3CommandByteCode.INPUT_DEVICE_STOP_ALL.getByte()) {
Arrays.fill(portSensorActive, false);
replyRequired = false;
return new byte[0];
}
if (msgValid && message[6] == EV3CommandByte.EV3CommandByteCode.INPUT_DEVICE_READY_RAW.getByte()) {
portSensorActive[port] = true;
}
if (msgValid && (message[6] == EV3CommandByte.EV3CommandByteCode.INPUT_DEVICE_READY_RAW.getByte()
|| message[6] == EV3CommandByte.EV3CommandByteCode.INPUT_DEVICE_GET_RAW.getByte())) {
reply = new byte[4];
reply[0] = messageNumber[0];
reply[1] = messageNumber[1];
reply[2] = DIRECT_REPLY;
byte value = (byte) portSensorValue[port];
reply[3] = value;
} else {
msgValid = false;
}
if (!msgValid) {
reply = new byte[3];
reply[0] = messageNumber[0];
reply[1] = messageNumber[1];
reply[2] = DIRECT_REPLY_ERROR;
}
return reply;
}
private byte[] handleInputReadMessage(byte[] message, byte[] messageNumber) {
byte[] reply = null;
replyRequired = true;
boolean msgValid = true;
int port = -1;
if (!isMessageLengthValid(message, 11)) {
msgValid = false;
} else {
port = message[7];
}
if (msgValid) {
reply = new byte[4];
reply[0] = messageNumber[0];
reply[1] = messageNumber[1];
reply[2] = DIRECT_REPLY;
byte value = (byte) portSensorValue[port];
reply[3] = value;
} else {
msgValid = false;
}
if (!msgValid) {
reply = new byte[3];
reply[0] = messageNumber[0];
reply[1] = messageNumber[1];
reply[2] = DIRECT_REPLY_ERROR;
}
return reply;
}
private byte[] handleInputReadSiMessage(byte[] message, byte[] messageNumber) {
byte[] reply = null;
replyRequired = true;
boolean msgValid = true;
int port = -1;
if (!isMessageLengthValid(message, 11)) {
msgValid = false;
} else {
port = message[7];
}
if (!msgValid) {
reply = new byte[3];
reply[0] = messageNumber[0];
reply[1] = messageNumber[1];
reply[2] = DIRECT_REPLY_ERROR;
} else {
portSensorMode[port] = message[9];
reply = new byte[4];
reply[0] = messageNumber[0];
reply[1] = messageNumber[1];
reply[2] = DIRECT_REPLY;
byte value = (byte) portSensorValue[port];
reply[3] = value;
}
return reply;
}
private byte[] handleUnknownMessage(byte[] messageNumber, byte commandType) {
byte[] reply = null;
replyRequired = true;
if (commandType == EV3CommandType.DIRECT_COMMAND_REPLY.getByte()) {
reply = new byte[3];
reply[0] = messageNumber[0];
reply[1] = messageNumber[1];
reply[2] = DIRECT_REPLY_ERROR;
}
return reply;
}
private boolean isMessageLengthValid(byte[] message, int expectedMessageLength) {
if (message.length != expectedMessageLength) {
return false;
}
return true;
}
private byte[] getMessageLength(byte[] message) {
byte[] messageLength = {
(byte) (message.length & 0x00FF),
(byte) ((message.length & 0xFF00) >> 8)
};
return messageLength;
}
@Override
public void start(DataInputStream inStream, OutputStream outStream) throws IOException {
byte[] messageLengthBuffer = new byte[2];
while (isRunning) {
inStream.readFully(messageLengthBuffer, 0, 2);
int expectedMessageLength = ((messageLengthBuffer[0] & 0xFF) | (messageLengthBuffer[1] & 0xFF) << 8);
handleClientMessage(expectedMessageLength, inStream, outStream);
}
}
@Override
public void stop() {
isRunning = false;
}
private void handleClientMessage(int expectedMessageLength, DataInputStream inStream, OutputStream outStream) throws IOException {
byte[] requestMessage = new byte[expectedMessageLength];
inStream.readFully(requestMessage, 0, expectedMessageLength);
byte[] responseMessage = createResponseFromClientRequest(requestMessage);
if (responseMessage == null || !replyRequired) {
return;
}
outStream.write(getMessageLength(responseMessage));
outStream.write(responseMessage);
outStream.flush();
}
public void generateSensorValue(int port) {
EV3Sensor.Sensor sensorType = sensors[port];
Random sensorValueRandom = new Random();
byte sensorMode = portSensorMode[port];
switch (sensorType) {
case NO_SENSOR:
setSensorValue(port, 255);
break;
case TOUCH:
setSensorValue(port, sensorValueRandom.nextInt(101));
break;
case COLOR:
if (sensorMode == EV3SensorMode.MODE2.getByte()) {
setSensorValue(port, sensorValueRandom.nextInt(8));
} else {
setSensorValue(port, 255);
}
break;
case COLOR_REFLECT:
if (sensorMode == EV3SensorMode.MODE0.getByte()) {
setSensorValue(port, sensorValueRandom.nextInt(101));
} else {
setSensorValue(port, 255);
}
break;
case COLOR_AMBIENT:
if (sensorMode == EV3SensorMode.MODE1.getByte()) {
setSensorValue(port, sensorValueRandom.nextInt(101));
} else {
setSensorValue(port, 255);
}
break;
case INFRARED:
if (sensorMode == EV3SensorMode.MODE0.getByte()) {
setSensorValue(port, sensorValueRandom.nextInt(101));
} else {
setSensorValue(port, 255);
}
break;
default:
setSensorValue(port, 255);
break;
}
}
public void setSensorValue(int port, int value) {
portSensorValue[port] = value;
}
public boolean isSensorActive(int port) {
return portSensorActive[port];
}
public void setSensorType(int port, EV3Sensor.Sensor sensorType) {
sensors[port] = sensorType;
}
public void setKeepAliveTime(int keepAliveTimeValue) {
keepAliveTime = keepAliveTimeValue;
keepAliveSet = true;
}
public int getKeepAliveTime() {
return keepAliveTime;
}
public boolean isKeepAliveSet() {
return keepAliveSet;
}
}