Merge branch 'main' of ssh://gitlab.fdmci.hva.nl/propedeuse-hbo-ict/onderwijs/2023-2024/out-a-se-ti/blok-4/muupooviixee66

This commit is contained in:
SebasKoedam
2024-06-03 14:28:42 +02:00
11 changed files with 42 additions and 94 deletions

View File

@@ -21,9 +21,9 @@ void Connectivity::connectWiFi(char* ssid, char* pass){
// }
const char* getServerURL = "http://145.92.8.132:443/get-ip";
String ipAddress = "";
char* ipAddress = "";
String Connectivity::fetchIPAddress() {
const char* Connectivity::fetchIPAddress() {
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
WiFiClient client;
@@ -32,7 +32,7 @@ String Connectivity::fetchIPAddress() {
int httpCode = http.GET();
if (httpCode > 0) {
if (httpCode == HTTP_CODE_OK) {
ipAddress = http.getString();
ipAddress = strdup(http.getString().c_str());
}
} else {
Serial.printf("GET request failed, error: %s\n", http.errorToString(httpCode).c_str());

View File

@@ -19,7 +19,7 @@ public:
void websocketSetup(char* ip, uint16_t port, char* adress);
void sendData(float roll, float pitch, float yaw);
int httpPost(const char *serverAddress, const char *serverSubPath, const unsigned short serverPort, const char *data, const size_t dataLength, const char *contentType);
String fetchIPAddress();
const char* fetchIPAddress();
private:
ESP8266WiFiMulti wifi;

View File

@@ -7,8 +7,6 @@ void setup() {
Serial.begin(9600);
}
unsigned long lastTime = 0; // will store the last time the code was run
void loop() {
SensorManager::eulerAngles eulerRotation = sensorManager.getEulerAngles();
// SensorManager::acceleration rotationAcceleration = sensorManager.getAcelleration();
@@ -19,6 +17,11 @@ struct acceleration {
float z = 9;
} accelData;
if (!ipAquired) {
serverIp = connectivity.fetchIPAddress();
ipAquired = true;
}
unsigned long lastTime = 0; // will store the last time the code was run
unsigned long currentTime = millis();
if (currentTime - lastTime >= 100) { // 100 ms has passed
memset(buffer, 0, BUFFER_SIZE);
@@ -34,7 +37,7 @@ struct acceleration {
accelData.z,
"data");
// %d = int, %f = floatation, %s = string
connectivity.httpPost(connectivity.fetchIPAddress(), "/", 3445, buffer, strlen(buffer), "application/json");
connectivity.httpPost(serverIp, "/", 3445, buffer, strlen(buffer), "application/json");
lastTime = currentTime;
}
}

View File

@@ -15,3 +15,5 @@ WebSocketsClient webSocket;
#define IP_ADDRESS "192.168.137.12"
char *buffer = (char *)malloc(sizeof(char) * BUFFER_SIZE);
const char* serverIp = NULL; // Declare serverIp here
bool ipAquired = false;

View File

@@ -15,3 +15,4 @@
local.properties
.idea
.vscode
/.idea/

View File

@@ -7,7 +7,7 @@ public class Exercise {
public final EMuscleGroup muscleGroup;
public final GesturePath leftPath;
public final GesturePath rightPath;
public final String title;
public final String name;
public final String description;
public final String imageUrl;
public final String videoUrl;
@@ -19,14 +19,14 @@ public class Exercise {
* @param muscleGroup The muscle group of the exercise.
* @param leftPath The path of the left hand.
* @param rightPath The path of the right hand.
* @param title The title of the exercise.
* @param name The title of the exercise.
* @param description The description of the exercise.
* @param imageUrl The URL of the image.
* @param videoUrl The URL of the video.
*/
public Exercise(EMuscleGroup muscleGroup, String title, String description, String imageUrl, String videoUrl, GesturePath leftPath, GesturePath rightPath, float exerciseTimeInSeconds) {
public Exercise(EMuscleGroup muscleGroup, String name, String description, String imageUrl, String videoUrl, GesturePath leftPath, GesturePath rightPath, float exerciseTimeInSeconds) {
this.name = name;
this.muscleGroup = muscleGroup;
this.title = title;
this.description = description;
this.leftPath = leftPath;
this.rightPath = rightPath;

View File

@@ -13,7 +13,7 @@ import java.net.URLConnection;
public class ExerciseManager {
private static final String HOST_ADDRESS = "http://145.92.8.132:443/";
private static final String HOST_ADDRESS = "http://145.92.8.132:443";
// The value of these property variables must be equivalent of
// the JSON data that the database sends back.
@@ -85,11 +85,12 @@ public class ExerciseManager {
*/
public static Exercise fetchExerciseFromDatabase() {
String response = sendHTTP(
HOST_ADDRESS, "POST", "application/json", null
HOST_ADDRESS, "POST", "application/json", "{}"
);
// Validate the response
if (response != null) {
try {
System.out.println(response);
JsonObject content = JsonParser.parseString(response).getAsJsonObject();
// Ensure all required properties are present

View File

@@ -6,6 +6,7 @@ import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
@@ -29,9 +30,10 @@ import org.joml.Vector3f;
public class FitnessActivity extends RobotActivity implements RobotLifecycleCallbacks {
// Private fields for the FitnessActivity class.
private ExerciseStatusElement personalMotionPreviewElement;
private ExerciseStatusElement exerciseStatusElement;
private InputProcessor motionProcessor;
private Exercise currentExercise;
private TextView exerciseNameTextView;
// Some nice little messages for the user
private static final String[] EXERCISE_NOT_FOUND_MESSAGES = new String[]{
@@ -56,6 +58,7 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall
// Remove the ugly ass bar on top of the view
setSpeechBarDisplayStrategy(SpeechBarDisplayStrategy.IMMERSIVE);
this.exerciseNameTextView = findViewById(R.id.textViewFitnessTitle);
// Find the VideoView by its ID
VideoView videoView = findViewById(R.id.videoView);
@@ -82,22 +85,23 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall
// Provide the context so that all queued actions can be performed.
Pepper.provideContext(qiContext, this.getClass());
personalMotionPreviewElement = findViewById(R.id.personalMotionPreviewElement);
exerciseStatusElement = findViewById(R.id.personalMotionPreviewElement);
// Initialize the element whenever it has been added to the screen.
// This will provide the element with the appropriate dimensions for drawing
// the canvas properly.
personalMotionPreviewElement.post(() -> {
exerciseStatusElement.post(() -> {
this.fetchExerciseAsync((exercise) -> {
// Acquire paths from the exercise and provide them to the motion processor
Vector3f[][] vectors = new Vector3f[][]{exercise.leftPath.getVectors(), exercise.rightPath.getVectors()};
motionProcessor = new InputProcessor(vectors, exercise.exerciseTimeInSeconds, SENSOR_SAMPLE_RATE);
personalMotionPreviewElement.initialize(exercise, motionProcessor, EXERCISE_COUNT);
exerciseStatusElement.initialize(exercise, motionProcessor, EXERCISE_COUNT);
motionProcessor.useExercise(exercise);
/* TODO: Remove if not needed */motionProcessor.setRecording(true, 10);
motionProcessor.startListening();
motionProcessor.setInputHandler(personalMotionPreviewElement);
motionProcessor.setInputHandler(exerciseStatusElement);
}, (n) -> {
int randomMessageIndex = (int) Math.floor(Math.random() * EXERCISE_NOT_FOUND_MESSAGES.length);
@@ -123,6 +127,7 @@ public class FitnessActivity extends RobotActivity implements RobotLifecycleCall
if (exercise == null) {
onFailedFetch.handle(null);
} else {
exerciseNameTextView.setText(exercise.name);
onSuccessfulFetch.handle(exercise);
}
})).start();

View File

@@ -38,16 +38,6 @@ public class ExerciseStatusElement extends View implements IInputHandler {
private final Paint borderPaint = new Paint();
private final Paint backgroundPaint = new Paint();
// TODO: Remove
private final Matrix4f viewMatrix = new Matrix4f(); // The view matrix for the 3D to 2D transformation.
private final Matrix4f projectionMatrix = new Matrix4f(); // The projection matrix for the 3D to 2D transformation.
private final Vector4f objectPosition = new Vector4f(0, 0, 0, 1); // The location of the object in 3D space.
private final ConcurrentLinkedQueue<Vector2f> vectors = new ConcurrentLinkedQueue<>();
// TODO: Remove
private Vector2f[] axisVectors = new Vector2f[0];
private static final String[] STARTING_PHRASES = {
"Veel success met de oefening!",
"Je kan het!",
@@ -93,16 +83,6 @@ public class ExerciseStatusElement extends View implements IInputHandler {
this.exercise = exercise;
this.exerciseCount = exerciseCount;
/* TODO: Remove */
this.axisVectors = new Vector2f[]{
projectVertex(new Vector3f(-5.0f, 0, 0), getWidth(), getHeight()),
projectVertex(new Vector3f(5.0f, 0, 0), getWidth(), getHeight()),
projectVertex(new Vector3f(0, -5.0f, 0), getWidth(), getHeight()),
projectVertex(new Vector3f(0, 5.0f, 0), getWidth(), getHeight()),
projectVertex(new Vector3f(0, 0, -5.0f), getWidth(), getHeight()),
projectVertex(new Vector3f(0, 0, 5.0f), getWidth(), getHeight())
};
Pepper.say(STARTING_PHRASES[(int) Math.floor(Math.random() * STARTING_PHRASES.length)]);
// Handler that is called every time the motion processor receives new data.
@@ -111,6 +91,10 @@ public class ExerciseStatusElement extends View implements IInputHandler {
Log.i("MotionProcessor", "Rotation vector received: " + rotationVector);
Log.i("MotionProcessor", "Last error offset:" + this.motionProcessor.getError(deviceId, this.motionProcessor.secondsPassed()));
// Check whether the current exercise has been completed.
// This is determined by the duration of the exercise, and the amount of time that has passed.
// The duration of the exercise originates from the database, and is stored in seconds.
// Whenever 'useExercise' is called, the timer resets and this method will be called again.
if (this.motionProcessor.hasFinished()) {
// If for some reason the parent activity is not defined,
// move back to the main screen.
@@ -124,6 +108,8 @@ public class ExerciseStatusElement extends View implements IInputHandler {
this.exerciseCount--;
this.parentActivity.fetchExerciseAsync((newExercise) -> {
this.motionProcessor.useExercise(newExercise);
// Whenever the database retrieval failed, we return to the main screen.
}, (failed) -> {
// Move to main screen
NavigationManager.navigateToActivity(parentActivity, MainActivity.class);
@@ -131,21 +117,8 @@ public class ExerciseStatusElement extends View implements IInputHandler {
} else {
// Finish the exercise.
NavigationManager.navigateToActivity(parentActivity, EndScreenActivity.class);
return;
}
}
/* TODO: Use raw vector */
vectors.add(projectVertex(rotationVector, this.getWidth(), this.getHeight()));
/* TODO: Remove */
Vector2f parsed = projectVertex(rotationVector, this.getWidth(), this.getHeight());
this.vectors.add(parsed /* TODO: Add regular vertex once exercise data is stored in DB*/);
// Remove the first element if the array is too big
if (this.vectors.size() > 100)
this.vectors.poll();
});
}
@@ -159,50 +132,12 @@ public class ExerciseStatusElement extends View implements IInputHandler {
this.exercise = exercise;
}
private Vector2f projectVertex(Vector3f point, int virtualWidth, int virtualHeight) {
viewMatrix
.identity()
.lookAt(new Vector3f(0, 0, -2), new Vector3f(0, 0, 0), new Vector3f(0, 1, 0));
// Transform the projection matrix to a perspective projection matrix
// Perspective transformation conserves the depth of the object
projectionMatrix
.identity()
.perspective((float) Math.toRadians(70), (float) virtualWidth / virtualHeight, .01f, 1000.0f);
// Convert world coordinates to screen-space using MVP matrix
Vector4f screenCoordinates = new Vector4f(point, 1.0f)
.mul(this.viewMatrix)
.mul(this.projectionMatrix);
// Normalize screen coordinates from (-1, 1) to (0, virtualWidth) and (0, virtualHeight)
float normalizedX = (screenCoordinates.x / screenCoordinates.w + 1.0f) * 0.5f * virtualWidth;
float normalizedY = (1.0f - screenCoordinates.y / screenCoordinates.w) * 0.5f * virtualHeight;
return new Vector2f(normalizedX, normalizedY);
}
@Override
public void onDraw(Canvas canvas) {
canvas.drawRect(0, 0, getWidth(), getHeight(), backgroundPaint);
this.setBackgroundColor(0xFF000000); // Black
/*if (this.exercise == null)
return;*/
/* TODO: Remove */
for (int i = 0, startX, endX, startY, endY; i < axisVectors.length / 2; i++) {
startX = (int) Math.max(0, Math.min(this.getWidth(), (int) axisVectors[i * 2].x));
endX = (int) Math.max(0, Math.min(this.getWidth(), (int) axisVectors[i * 2 + 1].x));
startY = (int) Math.max(0, Math.min(this.getHeight(), (int) axisVectors[i * 2].y));
endY = (int) Math.max(0, Math.min(this.getHeight(), (int) axisVectors[i * 2 + 1].y));
canvas.drawLine(startX, startY, endX, endY, this.borderPaint);
}
for (Vector2f point : this.vectors) {
canvas.drawRect(point.x, point.y, point.x + 5, point.y + 5, this.userProgressPaint);
}
/*
// Draw target circle
float targetRadius = (this.screenDimensions.x + this.screenDimensions.y) / 5.0f;

View File

@@ -80,6 +80,8 @@ public class InputProcessor {
this.selfRotationVectorPaths = new Vector3f[2][(int) (exercise.exerciseTimeInSeconds * this.sampleRate)];
this.targetRotationVectorPaths = new Vector3f[2][exercise.rightPath.getVectors().length];
this.targetRotationVectorPaths[0] = exercise.leftPath.getVectors();
this.targetRotationVectorPaths[1] = exercise.rightPath.getVectors();
this.exerciseDurationInSeconds = exercise.exerciseTimeInSeconds;
this.secondsPassed = 0.0D;
this.lastTime = System.currentTimeMillis();
@@ -99,7 +101,6 @@ public class InputProcessor {
if (recording) {
this.secondsPassed = 0.0D;
this.lastTime = System.currentTimeMillis();
}
}

View File

@@ -14,7 +14,7 @@ public class DatabaseFetchingTest {
assert exercise != null;
System.out.println("\n---------------------------------");
System.out.println("Exercise:");
System.out.println("Name: " + exercise.title);
System.out.println("Name: " + exercise.name);
System.out.println("Description: " + exercise.description);
System.out.println("Muscle Group: " + exercise.muscleGroup);
System.out.println("Image URL: " + exercise.imageUrl);