Added motion processing, fixed WebSocket packet processing, added Vector math to process motion data

This commit is contained in:
Luca Warmenhoven
2024-05-08 14:40:14 +02:00
parent c5b08a4907
commit d533ce97bb
11 changed files with 587 additions and 88 deletions

29
.idea/workspace.xml generated
View File

@@ -15,9 +15,14 @@
</component>
<component name="ChangeListManager">
<list default="true" id="00599d5b-7eb5-44da-ad7f-98bf42384c16" name="Changes" comment="Final update onderzoek-formulier.md">
<change afterPath="$PROJECT_DIR$/docs/personalDocs/Luca/weekly-reports/sprint-1-week-1.md" afterDir="false" />
<change afterPath="$PROJECT_DIR$/docs/personalDocs/Luca/weekly-reports/sprint-1-week-2.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/code/src/Fitbot/app/src/main/java/com/example/fitbot/MainActivity.java" beforeDir="false" afterPath="$PROJECT_DIR$/code/src/Fitbot/app/src/main/java/com/example/fitbot/MainActivity.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/server/WebSocketConnection.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/MotionData.java" beforeDir="false" afterPath="$PROJECT_DIR$/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/MotionData.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/MotionInputStream.java" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/MotionProcessor.java" beforeDir="false" afterPath="$PROJECT_DIR$/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/processing/MotionProcessor.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/server/IWebSocketHandler.java" beforeDir="false" afterPath="$PROJECT_DIR$/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/server/IWebSocketHandler.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/server/WebSocket.java" beforeDir="false" afterPath="$PROJECT_DIR$/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/server/WebSocket.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/server/WebSocketConnectionHandler.java" beforeDir="false" afterPath="$PROJECT_DIR$/code/src/Fitbot/app/src/main/java/com/example/fitbot/util/server/WebSocketConnectionHandler.java" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -70,10 +75,10 @@
<component name="MarkdownSettingsMigration">
<option name="stateVersion" value="1" />
</component>
<component name="ProjectColorInfo"><![CDATA[{
"customColor": "",
"associatedIndex": 7
}]]></component>
<component name="ProjectColorInfo">{
&quot;customColor&quot;: &quot;&quot;,
&quot;associatedIndex&quot;: 7
}</component>
<component name="ProjectId" id="2fE3N2CwEPDo9wBtexBLxU20tCJ" />
<component name="ProjectLevelVcsManager">
<ConfirmationsSetting value="2" id="Add" />
@@ -85,13 +90,16 @@
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"RunOnceActivity.OpenProjectViewOnStart": "true",
"git-widget-placeholder": "Merging main",
"RunOnceActivity.ShowReadmeOnStart": "true",
"git-widget-placeholder": "44-als-gebruiker-wil-ik-dat-ik-feedback-krijg-als-mijn-bewegingen-niet-op…-weet",
"kotlin-language-version-configured": "true",
"last_opened_file_path": "/Users/lucawarm/Jetbrains/Android Studio/muupooviixee66",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"settings.editor.selected.configurable": "preferences.pluginManager",
"vue.rearranger.settings.migration": "true"
}
}]]></component>
@@ -118,10 +126,7 @@
<option name="INSPECTION_WITHOUT_ACTIVITY_RESTART" value="false" />
<option name="TARGET_SELECTION_MODE" value="SHOW_DIALOG" />
<option name="DEBUGGER_TYPE" value="Java" />
<Java>
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
<option name="DEBUG_SANDBOX_SDK" value="false" />
</Java>
<Java />
<Profilers>
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
<option name="STARTUP_PROFILING_ENABLED" value="false" />

View File

@@ -1,14 +1,11 @@
package com.example.fitbot.util.processing;
import java.util.Objects;
public class MotionData {
// Data of the motion sensor
public float accelerationX;
public float accelerationY;
public float accelerationZ;
public float rotationX;
public float rotationY;
public float rotationZ;
public Vector3 acceleration, rotation;
// Delimiter for the data received from the motion sensor
private static final String DATA_DELIMITER = ";";
@@ -24,12 +21,19 @@ public class MotionData {
* @param rotationZ The rotation in the Z axis in degrees.
*/
public MotionData(float accelerationX, float accelerationY, float accelerationZ, float rotationX, float rotationY, float rotationZ) {
this.accelerationX = accelerationX;
this.accelerationY = accelerationY;
this.accelerationZ = accelerationZ;
this.rotationX = rotationX;
this.rotationY = rotationY;
this.rotationZ = rotationZ;
this.acceleration = new Vector3(accelerationX, accelerationY, accelerationZ);
this.rotation = new Vector3(rotationX, rotationY, rotationZ);
}
/**
* Constructor for the MotionData class.
*
* @param acceleration The acceleration vector in m/s^2.
* @param rotation The rotation vector in degrees.
*/
public MotionData(Vector3 acceleration, Vector3 rotation) {
this.acceleration = acceleration;
this.rotation = rotation;
}
/**
@@ -41,7 +45,12 @@ public class MotionData {
* @return An instance of MotionData.
*/
public static MotionData decode(String data) {
Objects.requireNonNull(data); // Ensure data is not null
String[] parts = data.split(DATA_DELIMITER);
if (parts.length != 6)
return null;
return new MotionData(
Float.parseFloat(parts[0]),
Float.parseFloat(parts[1]),

View File

@@ -1,38 +0,0 @@
package com.example.fitbot.util.processing;
import com.example.fitbot.util.server.WebSocket;
import java.io.InputStream;
/**
* Class representing the input stream of the motion sensor.
* This class will be responsible for reading the data from
* the motion sensor and processing it, by creating a
* server and starting a WebSocket connection with the ESP32.
*/
public class MotionInputStream extends InputStream {
/**
* Function for starting the listening process
* of the motion sensor. This function will
* @return An instance of MotionInputStream.
*/
public static MotionInputStream startListening()
{
// Create socket server
WebSocket socket = WebSocket.createServer(80);
// Check if the socket
if ( socket != null )
{
socket.startListening();
}
return null;
}
@Override
public int read() {
return 0;
}
}

View File

@@ -1,4 +1,95 @@
package com.example.fitbot.util.processing;
import android.util.Log;
import com.example.fitbot.util.server.IWebSocketHandler;
import com.example.fitbot.util.server.WebSocket;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class MotionProcessor {
private static final int MAX_PATH_LENGTH = 100;
public static final String DELIMITER = ";";
private final List<MotionData> motionData = new ArrayList<>();
private final Vector3[] relativePath = new Vector3[MAX_PATH_LENGTH];
private Vector3 ZERO = new Vector3(0, 0, 0);
private int sampleRate = 1;
public MotionProcessor() {}
/**
* Function for starting the listening process
* of the motion sensor. This function will create
* a new WebSocket server and start listening for
* incoming connections.
*/
public void startListening() {
// Create socket server
WebSocket socket = WebSocket.createServer(80);
// Check if the socket
if (socket != null) {
// Update event handler to match our functionality.
socket.setEventHandler(new IWebSocketHandler() {
@Override
public void onMessageReceived(WebSocket.Message message, WebSocket.MessageReply replier) {
parsePacket(message.message);
}
});
socket.startListening();
}
}
/**
* Function for parsing arbitrary packet data.
* @param data The data to parse.
*/
private void parsePacket(@NotNull String data) {
// If the message starts with 'data', it's a data packet.
if ( data.startsWith("data")) {
MotionData parsedData = MotionData.decode(data.split(" ")[1]);
if (parsedData != null) {
this.motionData.add(parsedData);
}
// Otherwise check if it starts with 'calibrate', this is the ZERO point.
} else if ( data.startsWith("calibrate")) { // message to calibrate device
String[] vectorData = data.split(" ")[1].split(DELIMITER);
ZERO = new Vector3(
Float.parseFloat(vectorData[0]),
Float.parseFloat(vectorData[1]),
Float.parseFloat(vectorData[2])
);
Log.i("MotionProcessor", "Device calibrated at " + ZERO.toString());
} else if ( data.startsWith("sampleRate")) {
this.sampleRate = Integer.parseInt(data.split(" ")[1]);
}
}
/**
* Function for calculating the path of the device.
* This only works when the ZERO point has been provided.
*/
private void calculatePath() {
int offset = Math.max(0, motionData.size() - MAX_PATH_LENGTH);
Vector3 previous = ZERO.copy();
for (int i = offset; i < motionData.size(); i++) {
MotionData data = motionData.get(i);
// Get relative vector from the ZERO point
Vector3 relativePosition = data.acceleration
.rotate(data.rotation.negate()) // Rotate acceleration vector with it's angle to get the acceleration relative to the vector of gravity
.map((vector) -> vector.) // Convert acceleration to relative spatial change
relativePath[i - offset]
}
}
}

View File

@@ -0,0 +1,165 @@
package com.example.fitbot.util.processing;
public class Vector3 {
public double x, y, z;
/**
* Constructor for creating a new vector.
*
* @param x The X component of the vector.
* @param y The Y component of the vector.
* @param z The Z component of the vector.
*/
public Vector3(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
/**
* Copy the vector.
*
* @return A new vector with the same values.
*/
public Vector3 copy() {
return new Vector3(this.x, this.y, this.z);
}
/**
* Subtract the vector from another vector.
*
* @param other The other vector to subtract.
* @return The new vector.
*/
public Vector3 subtract(Vector3 other) {
return new Vector3(this.x - other.x, this.y - other.y, this.z - other.z);
}
/**
* Add the vector to another vector.
*
* @param other The other vector to add.
* @return The new vector.
*/
public Vector3 add(Vector3 other) {
return new Vector3(this.x + other.x, this.y + other.y, this.z + other.z);
}
/**
* Multiply the vector by a scalar.
*
* @param scalar The scalar to multiply by.
* @return The multiplied vector.
*/
public Vector3 multiply(double scalar) {
return new Vector3(this.x * scalar, this.y * scalar, this.z * scalar);
}
/**
* Divide the vector by a scalar.
*
* @param scalar The scalar to divide by.
* @return The divided vector.
*/
public Vector3 divide(double scalar) {
if (scalar == 0) throw new IllegalArgumentException("Cannot divide by zero.");
return new Vector3(this.x / scalar, this.y / scalar, this.z / scalar);
}
/**
* Negate the vector.
*
* @return The negated vector.
*/
public Vector3 negate() {
return new Vector3(-this.x, -this.y, -this.z);
}
/**
* Rotate the vector around the X, Y, and Z axes.
*
* @param radX Rotation around the X axis in radians.
* @param radY Rotation around the Y axis in radians.
* @param radZ Rotation around the Z axis in radians.
* @return The rotated vector.
*/
public Vector3 rotate(double radX, double radY, double radZ) {
double cosX = Math.cos(radX);
double cosY = Math.cos(radY);
double cosZ = Math.cos(radZ);
double sinX = Math.sin(radX);
double sinY = Math.sin(radY);
double sinZ = Math.sin(radZ);
double newX = x * cosY * cosZ + y * cosY * sinZ - z * sinY;
double newY = x * (sinX * sinY * cosZ - cosX * sinZ) + y * (sinX * sinY * sinZ + cosX * cosZ) + z * sinX * cosY;
double newZ = x * (cosX * sinY * cosZ + sinX * sinZ) + y * (cosX * sinY * sinZ - sinX * cosZ) + z * cosX * cosY;
return new Vector3(newX, newY, newZ);
}
/**
* Rotate the vector around the X, Y, and Z axes.
*
* @param rotation The rotation vector.
* @return The rotated vector.
*/
public Vector3 rotate(Vector3 rotation) {
return rotate(rotation.x, rotation.y, rotation.z);
}
/**
* Rotate the vector around the X axis.
*
* @param angle Rotation around the X axis in radians.
* @return The rotated vector.
*/
public Vector3 rotateX(double angle) {
double sinTheta = Math.sin(angle);
double cosTheta = Math.cos(angle);
return new Vector3(
x,
y * cosTheta - z * sinTheta,
y * sinTheta + z * cosTheta
);
}
/**
* Rotate the vector around the Y axis.
*
* @param angle Rotation around the Y axis in radians.
* @return The rotated vector.
*/
public Vector3 rotateY(double angle) {
double sinTheta = Math.sin(angle);
double cosTheta = Math.cos(angle);
return new Vector3(
x * cosTheta + z * sinTheta,
y,
-x * sinTheta + z * cosTheta
);
}
/**
* Rotate the vector around the Z axis.
*
* @param angle Rotation around the Z axis in radians.
* @return The rotated vector.
*/
public Vector3 rotateZ(double angle) {
double sinTheta = Math.sin(angle);
double cosTheta = Math.cos(angle);
return new Vector3(
x * cosTheta - y * sinTheta,
x * sinTheta + y * cosTheta,
z
);
}
public Vector3 map(VectorMapFunction function) {
return function.apply(this);
}
public interface VectorMapFunction {
Vector3 apply(Vector3 vector);
}
}

View File

@@ -9,7 +9,10 @@ public interface IWebSocketHandler {
// Function for handling the connection of the WebSocket.
default void onConnected(Socket socket) {}
default void onDisconnected(Socket socket) {}
default void onMessageReceived(String message) {}
default void onMessageReceived(WebSocket.Message message, WebSocket.MessageReply replier) {}
default void onError(Socket socket, String error) {}
}

View File

@@ -48,6 +48,19 @@ public class WebSocket {
this.connectionHandler.listen();
}
/**
* Method for setting the event handler for this WebSocket server.
* @param handler The handler to use. This handler will parse all events
* that occur in this WebSocket connection. The events are the followed:
* - onMessageReceived(Socket, String)
* - onConnected(Socket)
* - onDisconnected(Socket)
* - onError(Socket, String)
*/
public void setEventHandler(IWebSocketHandler handler) {
this.eventHandler = handler;
}
/**
* Method for getting the ServerSocket connection
* @return The ServerSocket connection.
@@ -63,4 +76,61 @@ public class WebSocket {
public boolean isConnected() {
return !this.serverSocket.isClosed();
}
/**
* Class representing a message received from a WebSocket connection.
*/
public static class Message {
// Enumerable representing message type (opcode).
public enum Type {
CONTINUING((byte) 0x0),
TEXT((byte) 0x1),
BINARY((byte) 0x2),
RES0((byte) 0x3), RES1((byte) 0x4), RES2((byte) 0x5), RES3((byte) 0x6), RES4((byte) 0x7),
CLOSE_CONNECTION((byte) 0x8),
PING((byte) 0x9),
PONG((byte) 0xA),
RES5((byte) 0xB), RES6((byte) 0xC), RES7((byte) 0xD), RES8((byte) 0xE), RES9((byte) 0xF);
byte opcode;
Type(final byte opcode) {
this.opcode = opcode;
}
/**
* Method for decoding the opcode of a message.
* @param opcode The opcode to decode.
* @return The message type.
*/
public Type decode(byte opcode) {
return Type.values()[opcode & 0xF];
}
// Returns the opcode of this message type.
public byte getOpcode() { return this.opcode; }
}
public String message;
public WebSocketConnection connection;
/**
* Constructor for a WebSocket message.
* @param message The message that was sent
* @param connection The connection where the message came from.
*/
public Message(WebSocketConnection connection, String message) {
this.message = message;
this.connection = connection;
}
}
/**
* Interface for a message reply.
* This can be used for when a message has been received from a client
* to reply back to the client.
*/
public interface MessageReply {
void reply(String message);
}
}

View File

@@ -0,0 +1,35 @@
package com.example.fitbot.util.server;
import java.net.Socket;
public class WebSocketConnection {
private final WebSocket origin;
private final Socket socket;
/**
* Constructor for creating an arbitrary WebSocket connection (Client)
* @param connection The server connection
* @param socket The client socket
*/
public WebSocketConnection(WebSocket connection, Socket socket) {
this.origin = connection;
this.socket = socket;
}
/**
* Getter method for retrieving the WebSocket connection
* @return The WebSocket instance.
*/
public WebSocket getOrigin() {
return origin;
}
/**
* Getter method for retrieving the Client Socket connection.
* @return The Socket connection.
*/
public Socket getSocket() {
return socket;
}
}

View File

@@ -5,6 +5,8 @@ import android.support.annotation.RequiresApi;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
@@ -22,6 +24,7 @@ public class WebSocketConnectionHandler implements Runnable {
/**
* Constructor for WebSocketConnectionHandler.
* This class handles all new incoming Socket connections.
*
* @param webSocket The socket to check for new connections.
*/
protected WebSocketConnectionHandler(WebSocket webSocket) {
@@ -31,37 +34,22 @@ public class WebSocketConnectionHandler implements Runnable {
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void run() {
// Listen for new connections until the socket
// closes.
while (theSocket.isConnected()) {
try {
// Find a new connection
Socket newSocket = this.theSocket.getSocket().accept();
this.theSocket.eventHandler.onConnected(newSocket);
Scanner scanner = new Scanner(newSocket.getInputStream(), "UTF-8");
String data = scanner.useDelimiter("\\r\\n\\r\\n").next();
Matcher header = Pattern.compile("^GET").matcher(data);
// Check if the header contains the GET keyword
// If this is the case, upgrade the HTTP connection to WebSocket.
if (header.find()) {
Matcher match = Pattern.compile("Sec-WebSocket-Key: (.*)").matcher(data);
match.find();
String SECAccept = Base64.getEncoder().encodeToString(
MessageDigest.getInstance("SHA-1").digest((match.group(1) + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes(StandardCharsets.UTF_8)));
byte[] response = (
"HTTP/1.1 101 Switching Protocols\r\n" +
"Connection: Upgrade\r\n" +
"Upgrade: websocket\r\n" +
"Sec-WebSocket-Accept: " +
SECAccept + "\r\n\r\n").getBytes(StandardCharsets.UTF_8);
newSocket.getOutputStream().write(response, 0, response.length);
// TODO: Read data from socket and provide in onMessageReceived.
InputStream streamIn = newSocket.getInputStream();
OutputStream streamOut = newSocket.getOutputStream();
// Check if the connection was successfully upgraded to WebSocket
if (upgradeConnection(streamIn, streamOut)) {
applyMessageDecoder(streamIn);
}
} catch (IOException | NoSuchAlgorithmException error) {
} catch (IOException error) {
String reason = error.getMessage() == null ? "Unknown reason" : error.getMessage();
Log.e("WebSocketConnectionHandler", "Error listening to Socket connections: " + reason);
break;
@@ -69,9 +57,121 @@ public class WebSocketConnectionHandler implements Runnable {
}
}
/**
* Method for upgrading a HTTP connection to a WebSocket connection.
* This checks whether the client sent a GET header and sends back
* the required headers to upgrade the connection.
* @param streamIn The InputStream of the client socket connection.
* @param streamOut The OutputStream of the client socket connection.
* @return Whether or not the connection was successfully upgraded.
*/
@RequiresApi(api = Build.VERSION_CODES.O)
private boolean upgradeConnection(InputStream streamIn, OutputStream streamOut) {
Scanner scanner = new Scanner(streamIn, "UTF-8");
String data = scanner.useDelimiter("\\r\\n\\r\\n").next();
Matcher header = Pattern.compile("^GET").matcher(data);
// Check if the header contains the GET keyword
// If this is the case, upgrade the HTTP connection to WebSocket.
if (!header.find())
return false;
Matcher match = Pattern.compile("Sec-WebSocket-Key: (.*)").matcher(data);
match.find(); // Get next match
try {
String SECAccept = Base64.getEncoder().encodeToString(
MessageDigest.getInstance("SHA-1").digest((match.group(1) + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes(StandardCharsets.UTF_8)));
byte[] response = (
"HTTP/1.1 101 Switching Protocols\r\n" +
"Connection: Upgrade\r\n" +
"Upgrade: websocket\r\n" +
"Sec-WebSocket-Accept: " +
SECAccept + "\r\n\r\n").getBytes(StandardCharsets.UTF_8);
streamOut.write(response, 0, response.length);
} catch (IOException | NoSuchAlgorithmException error) {
Log.e("WebSocketConnectionHandler", "Failed upgrading HTTP to WebSocket connection" + error.getMessage());
return false;
}
return true;
}
/**
* Method for applying a message decoder for whenever a socket receives data.
* This method attemps to decode a message received from a WebSocket connection.
* This message is in the
* @param streamIn The message stream to decode.
*/
private void applyMessageDecoder(InputStream streamIn) {
}
/**
* Method for decoding an encoded WebSocket message
* @param bytes The message to decode, in UTF-8 format.
* @return The decoded message.
* @throws IllegalArgumentException When the `frame` content is in an incorrect format.
*/
public static String decodeWebSocketMessage(byte[] bytes) {
// Check if the packet isn't corrupted
if (bytes.length <= 2 || (bytes[0] & 0b1110) != 0)
throw new IllegalArgumentException("Attempted to decode corrupted WebSocket frame data");
byte opcode = (byte) (bytes[0] & 0b11110000); // Opcode (4 bits)
byte payloadLength = (byte) (bytes[1] & 0b01111111); // Payload size (7 bits)
boolean fin = (bytes[0] & 0b1) != 0; // Whether this is the whole message
boolean masked = (bytes[1] & 0b10000000) != 0; // Whether the 9th bit is masked
System.out.printf("Fin: %b, Opcode: %d, masked: %b\n", fin, opcode, masked);
long extendedPayloadLength = 0;
int byteOffset = 2;
// Check whether the payload length is 16-bit
if (payloadLength == 126) {
// 16-bit extended payload length (byte 2 and 3)
extendedPayloadLength = ((bytes[2] & 0xFF) << 8) | (bytes[3] & 0xFF);
byteOffset += 2;
// Check whether payload length is 64-bit
} else if (payloadLength == 127) {
// 64-bit extended payload length
for (int i = 0; i < 8; i++)
extendedPayloadLength |= (long) (bytes[2 + i] & 0xFF) << ((7 - i) * 8);
byteOffset += 8;
} else {
extendedPayloadLength = payloadLength;
}
byte[] maskingKey = null;
byte[] payloadData = new byte[(int) extendedPayloadLength];
// Check if the MASK bit was set, if so, copy the key to the `maskingKey` array.
if (masked) {
maskingKey = new byte[4];
System.arraycopy(bytes, byteOffset, maskingKey, 0, 4); // move mask bytes
byteOffset += 4;
}
// Copy payload bytes into `payloadData` array.
System.arraycopy(bytes, byteOffset, payloadData, 0, payloadData.length);
// If mask is present, decode the payload data with the mask.
if (masked)
for (int i = 0; i < payloadData.length; i++)
payloadData[i] ^= maskingKey[i % 4];
// Convert payload data to string
return new String(payloadData, StandardCharsets.UTF_8);
}
/**
* Method for checking whether the connection handler is actively listening.
* @return Whether or not it's listening.
* @return Whether it's listening.
*/
public boolean isActive() {
return this.thread.isAlive();

View File

@@ -0,0 +1,37 @@
package com.example.fitbot;
import com.example.fitbot.util.server.WebSocketConnectionHandler;
import org.junit.Test;
/**
* Created on 07/05/2024 at 18:27
* by Luca Warmenhoven.
*/
public class WebSocketMessageParsingTest {
@Test
public void parseWebSocketMessage() {
String reference = "abcdef";
final byte[] encoded = {
(byte) 129, (byte) 134, (byte) 167,
(byte) 225, (byte) 225, (byte) 210,
(byte) 198, (byte) 131, (byte) 130,
(byte) 182, (byte) 194, (byte) 135
};
String decoded = "";
try {
decoded = WebSocketConnectionHandler.decodeWebSocketMessage(encoded);
} catch (Exception e) {
System.err.println("Error occurred whilst attempting to parse input" + e.getMessage());
}
System.out.println("Parsed output: '" + decoded + "'");
if (decoded.equals(reference)) {
System.out.println("Test passed");
} else {
System.out.println("Test failed");
}
}
}

View File

@@ -1,3 +1,25 @@
## Hoe kan technologie worden ingezet om cognitieve achteruitgang tegen te gaan?
### Welke oorzaken zijn er voor cognitieve achteruitgang?
### Wat voor methodes zijn er om cognitieve achteruitgang te remmen?
### Hoe kan techniek worden toegepast om
Inleiding: ~10%
Conclusie: ~10%
~300 woorden totaal
Minimaal 5 bronnen zoeken
## Hoe kunnen ethische richtlijnen worden ontwikkeld voor het toepassen van kunstmatige intelligentie bij het aanpakken van eenzaamheid onder ouderen?
---