mirror of
https://github.com/Wessel/Roommapper.git
synced 2026-06-08 14:07:59 +02:00
feat: Voeg routeplanning aan frontend toe
This commit is contained in:
BIN
Eindverslag Wessel Tip 696770 Project Robotica.pdf
Normal file
BIN
Eindverslag Wessel Tip 696770 Project Robotica.pdf
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
17
src/Server/.idea/.idea.REST API/.idea/workspace.xml
generated
17
src/Server/.idea/.idea.REST API/.idea/workspace.xml
generated
@@ -7,7 +7,20 @@
|
||||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="79f184c3-e88e-45be-9116-5fa813562754" name="Changes" comment="" />
|
||||
<list default="true" id="79f184c3-e88e-45be-9116-5fa813562754" name="Changes" comment="">
|
||||
<change beforePath="$PROJECT_DIR$/../CoveragePathPlanner/bin/Debug/net8.0/CoveragePathPlanner.dll" beforeDir="false" afterPath="$PROJECT_DIR$/../CoveragePathPlanner/bin/Debug/net8.0/CoveragePathPlanner.dll" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../CoveragePathPlanner/bin/Debug/net8.0/CoveragePathPlanner.pdb" beforeDir="false" afterPath="$PROJECT_DIR$/../CoveragePathPlanner/bin/Debug/net8.0/CoveragePathPlanner.pdb" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/.idea.REST API/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.REST API/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/LibParse/bin/Debug/net8.0/LibParse.dll" beforeDir="false" afterPath="$PROJECT_DIR$/LibParse/bin/Debug/net8.0/LibParse.dll" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/LibParse/bin/Debug/net8.0/LibParse.pdb" beforeDir="false" afterPath="$PROJECT_DIR$/LibParse/bin/Debug/net8.0/LibParse.pdb" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/LibServer/bin/Debug/net8.0/LibServer.dll" beforeDir="false" afterPath="$PROJECT_DIR$/LibServer/bin/Debug/net8.0/LibServer.dll" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/LibServer/bin/Debug/net8.0/LibServer.pdb" beforeDir="false" afterPath="$PROJECT_DIR$/LibServer/bin/Debug/net8.0/LibServer.pdb" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/RobotControlServer/JsonClasses/Database.cs" beforeDir="false" afterPath="$PROJECT_DIR$/RobotControlServer/JsonClasses/Database.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/RobotControlServer/Program.cs" beforeDir="false" afterPath="$PROJECT_DIR$/RobotControlServer/Program.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/RobotControlServer/Routes/RoutePlan.cs" beforeDir="false" afterPath="$PROJECT_DIR$/RobotControlServer/Routes/RoutePlan.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../Web/pnpm-lock.yaml" beforeDir="false" afterPath="$PROJECT_DIR$/../Web/pnpm-lock.yaml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../Web/src/components/mapCanvas.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/../Web/src/components/mapCanvas.jsx" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
@@ -119,6 +132,8 @@
|
||||
<workItem from="1718443610193" duration="8513000" />
|
||||
<workItem from="1719219142765" duration="1400000" />
|
||||
<workItem from="1719253641573" duration="1910000" />
|
||||
<workItem from="1719827237918" duration="3979000" />
|
||||
<workItem from="1719913000122" duration="2097000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -16,3 +16,10 @@ public class RowData {
|
||||
public string Name;
|
||||
public DateTime Date;
|
||||
}
|
||||
public class PathData {
|
||||
public string Id;
|
||||
public string Objects;
|
||||
public int Version;
|
||||
public string Name;
|
||||
public DateTime Date;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ internal static class Program {
|
||||
{ "database", new RouteDatabase(cassandraSession) },
|
||||
{ "database/metadata", new RouteMetadata(cassandraSession) },
|
||||
{ "database/path", new RoutePath(cassandraSession) },
|
||||
{ "database/path/plan", new RoutePlan() },
|
||||
{ "database/path/plan", new RoutePlan(cassandraSession) },
|
||||
{ "roomba/control", new RouteControl() }
|
||||
};
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ public class RoutePath(ISession cassandraSession): IRoute {
|
||||
private string RowsToString(RowSet rowSet) {
|
||||
return rowSet.Select(row => new RowData {
|
||||
Id = row.GetValue<Guid>("id").ToString(),
|
||||
Objects = $"[{row.GetValue<string>("path")}]".FromJson<int[][]>()
|
||||
Objects = row.GetValue<string>("path").FromJson<int[][]>(),
|
||||
}).ToList().ToJson();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,20 +8,31 @@ using System.Drawing;
|
||||
|
||||
namespace RobotControlServer.Routes;
|
||||
|
||||
public class RoutePlan: IRoute {
|
||||
public class RoutePlan(ISession cassandraSession): IRoute {
|
||||
/// <summary>
|
||||
/// POST request for the database/path/plan route, generate a new path
|
||||
/// from a certain map of objects.
|
||||
/// </summary>
|
||||
public HttpResponse Post(HttpRequest request) {
|
||||
public HttpResponse Get(HttpRequest request) {
|
||||
try {
|
||||
// Parse request body to Data object, give error if non-nullable fields
|
||||
// are null.
|
||||
string id;
|
||||
var parsedBody = request.Body?.FromJson<Data>();
|
||||
if (parsedBody?.objects == null) {
|
||||
throw new Exception("objectData is null");
|
||||
if (!request.QueryString.TryGetValue("id", out id)) {
|
||||
throw new Exception("id is null");
|
||||
}
|
||||
// ParsedBody?.objects = [[0,1],[0,2],...]
|
||||
|
||||
|
||||
var selectStatement =
|
||||
cassandraSession.Prepare(@"SELECT * FROM Roommapper.Maps WHERE Id = ?;").Bind(Guid.Parse(id));
|
||||
var rowSet = cassandraSession.Execute(selectStatement);
|
||||
|
||||
if (rowSet.IsExhausted()) {
|
||||
throw new Exception("No map found with the given id");
|
||||
}
|
||||
|
||||
var map = rowSet.First();
|
||||
|
||||
// New instance of CPP with a 500x500 grid || where 0,0 = top-left and 499, 499 = bottom-right
|
||||
CoveragePathPlanner planner = new CoveragePathPlanner(500, 500);
|
||||
@@ -30,7 +41,7 @@ public class RoutePlan: IRoute {
|
||||
List<Point> obstacles = new List<Point>();
|
||||
|
||||
// Add obstacle cells
|
||||
foreach (var obj in $"[{parsedBody?.objects}]".FromJson<int[][]>())
|
||||
foreach (var obj in $"[{map.GetValue<string>("objects")}]".FromJson<int[][]>()) // $"[{parsedBody?.objects}]".FromJson<int[][]>()
|
||||
{
|
||||
obstacles.Add(new Point(obj[0], obj[1])); // Get x and y for every coordinate in the parsedbody array
|
||||
}
|
||||
@@ -43,10 +54,26 @@ public class RoutePlan: IRoute {
|
||||
}
|
||||
|
||||
// Return success message with the path
|
||||
return new HttpResponse($"{{\"message\":\"success\",\"path\":{points.ToJson()}}}");
|
||||
|
||||
var insertStatement = cassandraSession.Prepare(@"
|
||||
INSERT INTO Roommapper.Routes(Id, path)
|
||||
VALUES (?, ?);
|
||||
").Bind(Guid.Parse(id), points.ToJson());
|
||||
|
||||
cassandraSession.Execute(insertStatement);
|
||||
|
||||
// Return success message with the UUID of the inserted row
|
||||
return new HttpResponse($"{{\"message\":\"success\",\"id\":\"{id}\"}}");
|
||||
// return new HttpResponse($"{{\"message\":\"success\",\"path\":{points.ToJson()}}}");
|
||||
} catch (Exception ex) {
|
||||
// Return error message if failed for any reason
|
||||
return new HttpResponse($"{{\"message\": \"{ex.Message.Replace("\"", "\\\"")}\"}}", 400);
|
||||
}
|
||||
}
|
||||
|
||||
public HttpResponse Options(HttpRequest request) {
|
||||
var response = new HttpResponse("{\"message\": \"options\"}");
|
||||
response.Headers.Add("Allow", "GET, POST, OPTIONS");
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
15653
src/Web/pnpm-lock.yaml
generated
15653
src/Web/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -60,8 +60,8 @@ const App = () => {
|
||||
<Controlfield />
|
||||
<div style={{ 'marginLeft': '50px' }}>
|
||||
<MapCanvas
|
||||
width={600}
|
||||
height={600}
|
||||
width={500}
|
||||
height={500}
|
||||
onError={Controlfield.handleError}
|
||||
className='map-canvas'
|
||||
/>
|
||||
|
||||
@@ -12,11 +12,14 @@ export default class MapCanvas extends React.Component {
|
||||
// Due to `draw`'s asynchronous behavior, we need to bind `this` to the
|
||||
// handler functionin order for it to be able to be used inside of `render`.
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.handlePlan = this.handlePlan.bind(this);
|
||||
// Get a reference to the canvas element so we can draw on it
|
||||
this.canvasRef = React.createRef();
|
||||
this.state = {
|
||||
inputValue: '',
|
||||
searchOption: 'name',
|
||||
currentMapId: undefined,
|
||||
currentMap: [],
|
||||
canvasWidth: props.width || this.defaultWidth,
|
||||
canvasHeight: props.height || this.defaultHeight,
|
||||
};
|
||||
@@ -26,6 +29,10 @@ export default class MapCanvas extends React.Component {
|
||||
await this.draw();
|
||||
}
|
||||
|
||||
async handlePlan() {
|
||||
await this.HandlePlan();
|
||||
}
|
||||
|
||||
// This is a lifecycle method that is called after the component has been
|
||||
// rendered to the DOM. This is where we will draw the initial state
|
||||
// on the canvas.
|
||||
@@ -42,9 +49,13 @@ export default class MapCanvas extends React.Component {
|
||||
|
||||
// Combine all found sets into a singular array.
|
||||
const map = { 'id': data.length < 2 ? data[0].Id : undefined, 'points': [] };
|
||||
if (!Array.isArray(data) || data.length < 1) return map;
|
||||
console.log(data);
|
||||
if (data.length < 1) return map;
|
||||
// if (typeof data[0].Objects === 'string') return { id: data[0].Id, points: JSON.parse(data[0].Objects)};
|
||||
data.forEach((set) => set.Objects.forEach((coord) => map.points.push(coord)));
|
||||
|
||||
this.setState({ currentMapId: data[0].Id, currentMap: data[0].Objects });
|
||||
|
||||
return map;
|
||||
} catch (ex) {
|
||||
return;
|
||||
@@ -71,6 +82,15 @@ export default class MapCanvas extends React.Component {
|
||||
return;
|
||||
}
|
||||
|
||||
if (route || route.points.length > 0) {
|
||||
ctx.fillStyle = "#800000";
|
||||
route.points.forEach(point => {
|
||||
ctx.beginPath();
|
||||
ctx.arc(point[1], point[0], 1, 0, 2 * Math.PI);
|
||||
ctx.fill();
|
||||
});
|
||||
}
|
||||
|
||||
// Loop trough all coordinates, draw a dot at each point to form a top-down
|
||||
// view of the objects.
|
||||
ctx.fillStyle = "#000";
|
||||
@@ -79,13 +99,13 @@ export default class MapCanvas extends React.Component {
|
||||
ctx.arc(point[1], point[0], 1, 0, 2 * Math.PI);
|
||||
ctx.fill();
|
||||
});
|
||||
}
|
||||
|
||||
ctx.fillStyle = "#800000";
|
||||
route.points.forEach(point => {
|
||||
ctx.beginPath();
|
||||
ctx.arc(point[1], point[0], 1, 0, 2 * Math.PI);
|
||||
ctx.fill();
|
||||
});
|
||||
async HandlePlan() {
|
||||
if (this.state.currentMap.length < 1) return;
|
||||
const url = `${API_ENDPOINT}/database/path/plan?id=${this.state.currentMapId}`;
|
||||
const data = await (await fetch(url)).json();
|
||||
console.log(data);
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -110,6 +130,7 @@ export default class MapCanvas extends React.Component {
|
||||
disabled={searchOption === 'all'}
|
||||
/>
|
||||
<Button type="default" htmlType="submit">Update Map</Button>
|
||||
<Button type="default" onClick={this.handlePlan}>Plan</Button>
|
||||
</Form>
|
||||
<div>
|
||||
<canvas
|
||||
|
||||
Reference in New Issue
Block a user