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" />
|
<option name="autoReloadType" value="SELECTIVE" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<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="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||||
@@ -119,6 +132,8 @@
|
|||||||
<workItem from="1718443610193" duration="8513000" />
|
<workItem from="1718443610193" duration="8513000" />
|
||||||
<workItem from="1719219142765" duration="1400000" />
|
<workItem from="1719219142765" duration="1400000" />
|
||||||
<workItem from="1719253641573" duration="1910000" />
|
<workItem from="1719253641573" duration="1910000" />
|
||||||
|
<workItem from="1719827237918" duration="3979000" />
|
||||||
|
<workItem from="1719913000122" duration="2097000" />
|
||||||
</task>
|
</task>
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</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 string Name;
|
||||||
public DateTime Date;
|
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", new RouteDatabase(cassandraSession) },
|
||||||
{ "database/metadata", new RouteMetadata(cassandraSession) },
|
{ "database/metadata", new RouteMetadata(cassandraSession) },
|
||||||
{ "database/path", new RoutePath(cassandraSession) },
|
{ "database/path", new RoutePath(cassandraSession) },
|
||||||
{ "database/path/plan", new RoutePlan() },
|
{ "database/path/plan", new RoutePlan(cassandraSession) },
|
||||||
{ "roomba/control", new RouteControl() }
|
{ "roomba/control", new RouteControl() }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ public class RoutePath(ISession cassandraSession): IRoute {
|
|||||||
private string RowsToString(RowSet rowSet) {
|
private string RowsToString(RowSet rowSet) {
|
||||||
return rowSet.Select(row => new RowData {
|
return rowSet.Select(row => new RowData {
|
||||||
Id = row.GetValue<Guid>("id").ToString(),
|
Id = row.GetValue<Guid>("id").ToString(),
|
||||||
Objects = $"[{row.GetValue<string>("path")}]".FromJson<int[][]>()
|
Objects = row.GetValue<string>("path").FromJson<int[][]>(),
|
||||||
}).ToList().ToJson();
|
}).ToList().ToJson();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,20 +8,31 @@ using System.Drawing;
|
|||||||
|
|
||||||
namespace RobotControlServer.Routes;
|
namespace RobotControlServer.Routes;
|
||||||
|
|
||||||
public class RoutePlan: IRoute {
|
public class RoutePlan(ISession cassandraSession): IRoute {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// POST request for the database/path/plan route, generate a new path
|
/// POST request for the database/path/plan route, generate a new path
|
||||||
/// from a certain map of objects.
|
/// from a certain map of objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public HttpResponse Post(HttpRequest request) {
|
public HttpResponse Get(HttpRequest request) {
|
||||||
try {
|
try {
|
||||||
// Parse request body to Data object, give error if non-nullable fields
|
// Parse request body to Data object, give error if non-nullable fields
|
||||||
// are null.
|
// are null.
|
||||||
|
string id;
|
||||||
var parsedBody = request.Body?.FromJson<Data>();
|
var parsedBody = request.Body?.FromJson<Data>();
|
||||||
if (parsedBody?.objects == null) {
|
if (!request.QueryString.TryGetValue("id", out id)) {
|
||||||
throw new Exception("objectData is null");
|
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
|
// New instance of CPP with a 500x500 grid || where 0,0 = top-left and 499, 499 = bottom-right
|
||||||
CoveragePathPlanner planner = new CoveragePathPlanner(500, 500);
|
CoveragePathPlanner planner = new CoveragePathPlanner(500, 500);
|
||||||
@@ -30,7 +41,7 @@ public class RoutePlan: IRoute {
|
|||||||
List<Point> obstacles = new List<Point>();
|
List<Point> obstacles = new List<Point>();
|
||||||
|
|
||||||
// Add obstacle cells
|
// 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
|
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 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) {
|
} catch (Exception ex) {
|
||||||
// Return error message if failed for any reason
|
// Return error message if failed for any reason
|
||||||
return new HttpResponse($"{{\"message\": \"{ex.Message.Replace("\"", "\\\"")}\"}}", 400);
|
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 />
|
<Controlfield />
|
||||||
<div style={{ 'marginLeft': '50px' }}>
|
<div style={{ 'marginLeft': '50px' }}>
|
||||||
<MapCanvas
|
<MapCanvas
|
||||||
width={600}
|
width={500}
|
||||||
height={600}
|
height={500}
|
||||||
onError={Controlfield.handleError}
|
onError={Controlfield.handleError}
|
||||||
className='map-canvas'
|
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
|
// 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`.
|
// handler functionin order for it to be able to be used inside of `render`.
|
||||||
this.handleSubmit = this.handleSubmit.bind(this);
|
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
|
// Get a reference to the canvas element so we can draw on it
|
||||||
this.canvasRef = React.createRef();
|
this.canvasRef = React.createRef();
|
||||||
this.state = {
|
this.state = {
|
||||||
inputValue: '',
|
inputValue: '',
|
||||||
searchOption: 'name',
|
searchOption: 'name',
|
||||||
|
currentMapId: undefined,
|
||||||
|
currentMap: [],
|
||||||
canvasWidth: props.width || this.defaultWidth,
|
canvasWidth: props.width || this.defaultWidth,
|
||||||
canvasHeight: props.height || this.defaultHeight,
|
canvasHeight: props.height || this.defaultHeight,
|
||||||
};
|
};
|
||||||
@@ -26,6 +29,10 @@ export default class MapCanvas extends React.Component {
|
|||||||
await this.draw();
|
await this.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async handlePlan() {
|
||||||
|
await this.HandlePlan();
|
||||||
|
}
|
||||||
|
|
||||||
// This is a lifecycle method that is called after the component has been
|
// 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
|
// rendered to the DOM. This is where we will draw the initial state
|
||||||
// on the canvas.
|
// on the canvas.
|
||||||
@@ -42,9 +49,13 @@ export default class MapCanvas extends React.Component {
|
|||||||
|
|
||||||
// Combine all found sets into a singular array.
|
// Combine all found sets into a singular array.
|
||||||
const map = { 'id': data.length < 2 ? data[0].Id : undefined, 'points': [] };
|
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)));
|
data.forEach((set) => set.Objects.forEach((coord) => map.points.push(coord)));
|
||||||
|
|
||||||
|
this.setState({ currentMapId: data[0].Id, currentMap: data[0].Objects });
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
return;
|
return;
|
||||||
@@ -71,6 +82,15 @@ export default class MapCanvas extends React.Component {
|
|||||||
return;
|
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
|
// Loop trough all coordinates, draw a dot at each point to form a top-down
|
||||||
// view of the objects.
|
// view of the objects.
|
||||||
ctx.fillStyle = "#000";
|
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.arc(point[1], point[0], 1, 0, 2 * Math.PI);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
ctx.fillStyle = "#800000";
|
async HandlePlan() {
|
||||||
route.points.forEach(point => {
|
if (this.state.currentMap.length < 1) return;
|
||||||
ctx.beginPath();
|
const url = `${API_ENDPOINT}/database/path/plan?id=${this.state.currentMapId}`;
|
||||||
ctx.arc(point[1], point[0], 1, 0, 2 * Math.PI);
|
const data = await (await fetch(url)).json();
|
||||||
ctx.fill();
|
console.log(data);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -110,6 +130,7 @@ export default class MapCanvas extends React.Component {
|
|||||||
disabled={searchOption === 'all'}
|
disabled={searchOption === 'all'}
|
||||||
/>
|
/>
|
||||||
<Button type="default" htmlType="submit">Update Map</Button>
|
<Button type="default" htmlType="submit">Update Map</Button>
|
||||||
|
<Button type="default" onClick={this.handlePlan}>Plan</Button>
|
||||||
</Form>
|
</Form>
|
||||||
<div>
|
<div>
|
||||||
<canvas
|
<canvas
|
||||||
|
|||||||
Reference in New Issue
Block a user