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:
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;
|
||||
}
|
||||
}
|
||||
|
||||
15713
src/Web/pnpm-lock.yaml
generated
15713
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