mirror of
https://github.com/Wessel/Roommapper.git
synced 2026-07-21 15:33:57 +02:00
Merge branch 'master' of https://github.com/Wessel/Roommapper
This commit is contained in:
@@ -1,136 +1,136 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
|
||||
public class CoveragePathPlanner
|
||||
{
|
||||
private int _width;
|
||||
private int _height;
|
||||
private List<Cell> _cells;
|
||||
private int _width;
|
||||
private int _height;
|
||||
private List<Cell> _cells;
|
||||
|
||||
public CoveragePathPlanner(int width, int height)
|
||||
public CoveragePathPlanner(int width, int height)
|
||||
{
|
||||
_width = width;
|
||||
_height = height;
|
||||
_cells = new List<Cell>();
|
||||
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
_width = width;
|
||||
_height = height;
|
||||
_cells = new List<Cell>();
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
_cells.Add(new Cell { X = x, Y = y, IsObstacle = false, IsVisited = false, Cost = double.MaxValue });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int x = 0; x < width; x++)
|
||||
public List<Point> PlanPath(List<Obstacle> obstacles)
|
||||
{
|
||||
// Add obstacles to the grid
|
||||
foreach (Obstacle obstacle in obstacles)
|
||||
{
|
||||
foreach (Cell cell in _cells)
|
||||
{
|
||||
if (cell.IsWithinObstacle(obstacle))
|
||||
{
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
_cells.Add(new Cell { X = x, Y = y, IsObstacle = false, IsVisited = false, Cost = double.MaxValue });
|
||||
}
|
||||
cell.IsObstacle = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<Point> PlanPath(List<Obstacle> obstacles)
|
||||
// Initialize the start cell
|
||||
Cell startCell = _cells[0];
|
||||
startCell.Cost = 0;
|
||||
|
||||
// Create a priority queue to hold the cells to be processed
|
||||
var queue = new PriorityQueue<Cell, double>();
|
||||
queue.Enqueue(startCell, 0);
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
// Add obstacles to the grid
|
||||
foreach (Obstacle obstacle in obstacles)
|
||||
// Dequeue the cell with the lowest cost
|
||||
Cell currentCell = queue.Dequeue();
|
||||
|
||||
// If the cell is the goal, construct the path
|
||||
if (currentCell.X == _width - 1 && currentCell.Y == _height - 1)
|
||||
{
|
||||
List<Point> path = new List<Point>();
|
||||
while (currentCell != null)
|
||||
{
|
||||
for (int x = obstacle.X; x < obstacle.X + obstacle.Width; x++)
|
||||
{
|
||||
for (int y = obstacle.Y; y < obstacle.Y + obstacle.Height; y++)
|
||||
{
|
||||
_cells[x + y * _width].IsObstacle = true;
|
||||
}
|
||||
}
|
||||
path.Add(new Point(currentCell.X, currentCell.Y));
|
||||
currentCell = currentCell.Parent;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
// Initialize the start cell
|
||||
Cell startCell = _cells[0];
|
||||
startCell.Cost = 0;
|
||||
// Mark the cell as visited
|
||||
currentCell.IsVisited = true;
|
||||
|
||||
// Create a priority queue to hold the cells to be processed
|
||||
var queue = new PriorityQueue<Cell, double>();
|
||||
queue.Enqueue(startCell, 0);
|
||||
|
||||
while (queue.Count > 0)
|
||||
// Explore the neighbors
|
||||
foreach (Cell neighbor in currentCell.GetNeighbors(_cells))
|
||||
{
|
||||
if (!neighbor.IsVisited && !neighbor.IsObstacle)
|
||||
{
|
||||
// Dequeue the cell with the lowest cost
|
||||
Cell currentCell = queue.Dequeue();
|
||||
|
||||
// If the cell is the goal, construct the path
|
||||
if (currentCell.X == _width - 1 && currentCell.Y == _height - 1)
|
||||
{
|
||||
List<Point> path = new List<Point>();
|
||||
while (currentCell != null)
|
||||
{
|
||||
path.Add(new Point(currentCell.X, currentCell.Y));
|
||||
currentCell = currentCell.Parent;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
// Mark the cell as visited
|
||||
currentCell.IsVisited = true;
|
||||
|
||||
// Explore the neighbors
|
||||
foreach (Cell neighbor in GetNeighbors(currentCell))
|
||||
{
|
||||
if (!neighbor.IsVisited && !neighbor.IsObstacle)
|
||||
{
|
||||
// Calculate the cost of the neighbor
|
||||
double cost = currentCell.Cost + 1;
|
||||
if (cost < neighbor.Cost)
|
||||
{
|
||||
neighbor.Cost = cost;
|
||||
neighbor.Parent = currentCell;
|
||||
queue.Enqueue(neighbor, cost + Heuristic(neighbor));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Calculate the cost of the neighbor
|
||||
double cost = currentCell.Cost + 1;
|
||||
if (cost < neighbor.Cost)
|
||||
{
|
||||
neighbor.Cost = cost;
|
||||
neighbor.Parent = currentCell;
|
||||
queue.Enqueue(neighbor, cost + currentCell.Heuristic(_width, _height));
|
||||
}
|
||||
}
|
||||
|
||||
// If no path is found, return an empty list
|
||||
return new List<Point>();
|
||||
}
|
||||
|
||||
private List<Cell> GetNeighbors(Cell cell)
|
||||
{
|
||||
List<Cell> neighbors = new List<Cell>();
|
||||
|
||||
for (int x = -1; x <= 1; x++)
|
||||
{
|
||||
for (int y = -1; y <= 1; y++)
|
||||
{
|
||||
if (x == 0 && y == 0) continue;
|
||||
|
||||
int neighborX = cell.X + x;
|
||||
int neighborY = cell.Y + y;
|
||||
|
||||
if (neighborX >= 0 && neighborX < _width && neighborY >= 0 && neighborY < _height)
|
||||
{
|
||||
neighbors.Add(_cells[neighborX + neighborY * _width]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return neighbors;
|
||||
}
|
||||
|
||||
private double Heuristic(Cell cell)
|
||||
{
|
||||
// Manhattan distance heuristic
|
||||
return Math.Abs(cell.X - (_width - 1)) + Math.Abs(cell.Y - (_height - 1));
|
||||
}
|
||||
}
|
||||
|
||||
// If no path is found, return an empty list
|
||||
return new List<Point>();
|
||||
}
|
||||
}
|
||||
|
||||
public class Obstacle
|
||||
{
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
public int Width { get; set; }
|
||||
public int Height { get; set; }
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
public int Width { get; set; }
|
||||
public int Height { get; set; }
|
||||
}
|
||||
|
||||
public class Cell
|
||||
{
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
public bool IsObstacle { get; set; }
|
||||
public bool IsVisited { get; set; }
|
||||
public double Cost { get; set; }
|
||||
public Cell Parent { get; set; }
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
public bool IsObstacle { get; set; }
|
||||
public bool IsVisited { get; set; }
|
||||
public double Cost { get; set; }
|
||||
public Cell Parent { get; set; }
|
||||
|
||||
public bool IsWithinObstacle(Obstacle obstacle)
|
||||
{
|
||||
return X >= obstacle.X && X < obstacle.X + obstacle.Width &&
|
||||
Y >= obstacle.Y && Y < obstacle.Y + obstacle.Height;
|
||||
}
|
||||
|
||||
public IEnumerable<Cell> GetNeighbors(List<Cell> cells)
|
||||
{
|
||||
for (int x = -1; x <= 1; x++)
|
||||
{
|
||||
for (int y = -1; y <= 1; y++)
|
||||
{
|
||||
if (x == 0 && y == 0) continue;
|
||||
|
||||
int neighborX = X + x;
|
||||
int neighborY = Y + y;
|
||||
|
||||
if (neighborX >= 0 && neighborX < cells.Count / cells[0].Y && neighborY >= 0 && neighborY < cells[0].Y)
|
||||
{
|
||||
yield return cells[neighborX + neighborY * cells.Count / cells[0].Y];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public double Heuristic(int width, int height)
|
||||
{
|
||||
// Manhattan distance heuristic
|
||||
return Math.Abs(X - (width - 1)) + Math.Abs(Y - (height - 1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<data-source name="roommapper@localhost" uuid="469f6bec-f113-4467-9d39-7ac38558045a">
|
||||
<database-info product="Cassandra" version="3.0.8" jdbc-version="4.2" driver-name="Cassandra JDBC Driver" driver-version="1.4" dbms="CASSANDRA" exact-version="3.0.8" exact-driver-version="1.4">
|
||||
<identifier-quote-string>"</identifier-quote-string>
|
||||
<jdbc-catalog-is-schema>true</jdbc-catalog-is-schema>
|
||||
</database-info>
|
||||
<case-sensitivity plain-identifiers="lower" quoted-identifiers="exact" />
|
||||
<secret-storage>master_key</secret-storage>
|
||||
|
||||
@@ -8,33 +8,33 @@
|
||||
<CanLogin>1</CanLogin>
|
||||
<SuperRole>1</SuperRole>
|
||||
</role>
|
||||
<schema id="3" parent="1" name="system_auth">
|
||||
<Properties>durable_writes:true
|
||||
replication:{'class': 'org.apache.cassandra.locator.SimpleStrategy', 'replication_factor': '1'}
|
||||
</Properties>
|
||||
</schema>
|
||||
<schema id="4" parent="1" name="system_schema">
|
||||
<Properties>durable_writes:true
|
||||
replication:{'class': 'org.apache.cassandra.locator.LocalStrategy'}
|
||||
</Properties>
|
||||
</schema>
|
||||
<schema id="5" parent="1" name="eve">
|
||||
<Properties>durable_writes:true
|
||||
replication:{'class': 'org.apache.cassandra.locator.SimpleStrategy', 'replication_factor': '1'}
|
||||
</Properties>
|
||||
</schema>
|
||||
<schema id="6" parent="1" name="roommapper">
|
||||
<schema id="3" parent="1" name="roommapper">
|
||||
<Current>1</Current>
|
||||
<Properties>durable_writes:true
|
||||
replication:{'class': 'org.apache.cassandra.locator.NetworkTopologyStrategy', 'datacenter1': '1'}
|
||||
</Properties>
|
||||
</schema>
|
||||
<schema id="7" parent="1" name="system_distributed">
|
||||
<schema id="4" parent="1" name="system">
|
||||
<Properties>durable_writes:true
|
||||
replication:{'class': 'org.apache.cassandra.locator.LocalStrategy'}
|
||||
</Properties>
|
||||
</schema>
|
||||
<schema id="5" parent="1" name="system_auth">
|
||||
<Properties>durable_writes:true
|
||||
replication:{'class': 'org.apache.cassandra.locator.SimpleStrategy', 'replication_factor': '1'}
|
||||
</Properties>
|
||||
</schema>
|
||||
<schema id="6" parent="1" name="system_distributed">
|
||||
<Properties>durable_writes:true
|
||||
replication:{'class': 'org.apache.cassandra.locator.SimpleStrategy', 'replication_factor': '3'}
|
||||
</Properties>
|
||||
</schema>
|
||||
<schema id="8" parent="1" name="system">
|
||||
<schema id="7" parent="1" name="system_distributed_everywhere">
|
||||
<Properties>durable_writes:true
|
||||
replication:{'class': 'org.apache.cassandra.locator.EverywhereStrategy'}
|
||||
</Properties>
|
||||
</schema>
|
||||
<schema id="8" parent="1" name="system_schema">
|
||||
<Properties>durable_writes:true
|
||||
replication:{'class': 'org.apache.cassandra.locator.LocalStrategy'}
|
||||
</Properties>
|
||||
@@ -44,53 +44,5 @@ replication:{'class': 'org.apache.cassandra.locator.LocalStrategy
|
||||
replication:{'class': 'org.apache.cassandra.locator.SimpleStrategy', 'replication_factor': '2'}
|
||||
</Properties>
|
||||
</schema>
|
||||
<schema id="10" parent="1" name="system_distributed_everywhere">
|
||||
<Properties>durable_writes:true
|
||||
replication:{'class': 'org.apache.cassandra.locator.EverywhereStrategy'}
|
||||
</Properties>
|
||||
</schema>
|
||||
<schema id="11" parent="1" name="discordlogger">
|
||||
<Properties>durable_writes:true
|
||||
replication:{'class': 'org.apache.cassandra.locator.SimpleStrategy', 'replication_factor': '1'}
|
||||
</Properties>
|
||||
</schema>
|
||||
<table id="12" parent="6" name="maps">
|
||||
<Properties>caching:{'keys': 'ALL', 'rows_per_partition': 'ALL'}
|
||||
compression:{'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'}
|
||||
bloom_filter_fp_chance:0.01
|
||||
default_time_to_live:0
|
||||
speculative_retry:99.0PERCENTILE
|
||||
gc_grace_seconds:864000
|
||||
max_index_interval:2048
|
||||
memtable_flush_period_in_ms:0
|
||||
min_index_interval:128
|
||||
read_repair_chance:0
|
||||
crc_check_chance:1
|
||||
dclocal_read_repair_chance:0
|
||||
compaction:{'class': 'SizeTieredCompactionStrategy'}
|
||||
</Properties>
|
||||
</table>
|
||||
<column id="13" parent="12" name="id">
|
||||
<DasType>uuid|0s</DasType>
|
||||
<Position>1</Position>
|
||||
</column>
|
||||
<column id="14" parent="12" name="version">
|
||||
<DasType>int|0s</DasType>
|
||||
<Position>2</Position>
|
||||
</column>
|
||||
<column id="15" parent="12" name="date">
|
||||
<DasType>timestamp|0s</DasType>
|
||||
<Position>3</Position>
|
||||
</column>
|
||||
<column id="16" parent="12" name="objects">
|
||||
<DasType>text|0s</DasType>
|
||||
<Position>4</Position>
|
||||
</column>
|
||||
<key id="17" parent="12" name="primary key">
|
||||
<Columns>id
|
||||
version|ASC
|
||||
date|ASC
|
||||
</Columns>
|
||||
</key>
|
||||
</database-model>
|
||||
</dataSource>
|
||||
64
src/Server/.idea/.idea.REST API/.idea/workspace.xml
generated
64
src/Server/.idea/.idea.REST API/.idea/workspace.xml
generated
@@ -8,12 +8,14 @@
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="79f184c3-e88e-45be-9116-5fa813562754" name="Changes" comment="">
|
||||
<change beforePath="$PROJECT_DIR$/.idea/.idea.REST API/.idea/dataSources.local.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.REST API/.idea/dataSources.local.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/.idea.REST API/.idea/dataSources/469f6bec-f113-4467-9d39-7ac38558045a.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.REST API/.idea/dataSources/469f6bec-f113-4467-9d39-7ac38558045a.xml" 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$/../Web/public/favicon.ico" beforeDir="false" afterPath="$PROJECT_DIR$/../Web/public/favicon.ico" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../Web/public/index.html" beforeDir="false" afterPath="$PROJECT_DIR$/../Web/public/index.html" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../Web/src/components/controlField.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/../Web/src/components/controlField.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../Web/src/components/layout.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/../Web/src/components/layout.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Console/JsonClasses/Database.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Console/JsonClasses/Database.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Console/Program.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Console/Program.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Console/Routes/RouteDatabase.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Console/Routes/RouteDatabase.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Console/Routes/RoutePath.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Console/Routes/RoutePath.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Console/bin/Debug/net8.0/Console.dll" beforeDir="false" afterPath="$PROJECT_DIR$/Console/bin/Debug/net8.0/Console.dll" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Console/bin/Debug/net8.0/Console.pdb" beforeDir="false" afterPath="$PROJECT_DIR$/Console/bin/Debug/net8.0/Console.pdb" 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" />
|
||||
@@ -42,34 +44,34 @@
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent"><![CDATA[{
|
||||
"keyToString": {
|
||||
".NET Project.Console.executor": "Run",
|
||||
"ASKED_ADD_EXTERNAL_FILES": "true",
|
||||
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"d452b4e4-aa15-4fa9-847f-33d4d7fab23d.executor": "Run",
|
||||
"git-widget-placeholder": "master",
|
||||
"ignore.virus.scanning.warn.message": "true",
|
||||
"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": "HotReloadSettingsPage",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
".NET Project.Console.executor": "Run",
|
||||
"ASKED_ADD_EXTERNAL_FILES": "true",
|
||||
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"d452b4e4-aa15-4fa9-847f-33d4d7fab23d.executor": "Run",
|
||||
"git-widget-placeholder": "master",
|
||||
"ignore.virus.scanning.warn.message": "true",
|
||||
"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": "HotReloadSettingsPage",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
},
|
||||
"keyToStringList": {
|
||||
"DatabaseDriversLRU": [
|
||||
"cassandra"
|
||||
"keyToStringList": {
|
||||
"DatabaseDriversLRU": [
|
||||
"cassandra"
|
||||
],
|
||||
"rider.external.source.directories": [
|
||||
"/home/wessel/.config/JetBrains/Rider2024.1/resharper-host/DecompilerCache",
|
||||
"/home/wessel/.config/JetBrains/Rider2024.1/resharper-host/SourcesCache",
|
||||
"/home/wessel/.local/share/Symbols/src"
|
||||
"rider.external.source.directories": [
|
||||
"/home/wessel/.config/JetBrains/Rider2024.1/resharper-host/DecompilerCache",
|
||||
"/home/wessel/.config/JetBrains/Rider2024.1/resharper-host/SourcesCache",
|
||||
"/home/wessel/.local/share/Symbols/src"
|
||||
]
|
||||
}
|
||||
}]]></component>
|
||||
}</component>
|
||||
<component name="RunManager">
|
||||
<configuration name="Console" type="DotNetProject" factoryName=".NET Project">
|
||||
<option name="EXE_PATH" value="$PROJECT_DIR$/Console/bin/Debug/net8.0/Console.exe" />
|
||||
@@ -118,6 +120,10 @@
|
||||
<workItem from="1717492595765" duration="6218000" />
|
||||
<workItem from="1717584416884" duration="5757000" />
|
||||
<workItem from="1717602912720" duration="1420000" />
|
||||
<workItem from="1717668473610" duration="971000" />
|
||||
<workItem from="1717670832494" duration="901000" />
|
||||
<workItem from="1717673464254" duration="1428000" />
|
||||
<workItem from="1718093417281" duration="4387000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
|
||||
@@ -6,11 +6,13 @@ public class Data {
|
||||
public int version;
|
||||
public string date;
|
||||
public string id;
|
||||
public string name;
|
||||
}
|
||||
|
||||
public class RowData {
|
||||
public string Id;
|
||||
public int[][] Objects;
|
||||
public int Version;
|
||||
public string Name;
|
||||
public DateTime Date;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ internal static class Program {
|
||||
{ "", new Root() },
|
||||
{ "database", new RouteDatabase(cassandraSession) },
|
||||
{ "database/metadata", new RouteMetadata(cassandraSession) },
|
||||
{ "database/path", new RoutePath(cassandraSession) },
|
||||
{ "roomba/control", new RouteControl() }
|
||||
};
|
||||
|
||||
@@ -39,10 +40,18 @@ internal static class Program {
|
||||
session.Execute(@"
|
||||
CREATE TABLE IF NOT EXISTS Roommapper.Maps(
|
||||
Id uuid,
|
||||
Objects text,
|
||||
Version int,
|
||||
Name text,
|
||||
Objects text,
|
||||
Date timestamp,
|
||||
PRIMARY KEY (Id,Version,Date)
|
||||
PRIMARY KEY (Id,Name)
|
||||
)");
|
||||
|
||||
session.Execute(@"
|
||||
CREATE TABLE IF NOT EXISTS Roommapper.Routes(
|
||||
Id uuid,
|
||||
Path text,
|
||||
PRIMARY KEY (Id)
|
||||
)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,13 +12,13 @@ public class RouteDatabase(ISession cassandraSession): IRoute {
|
||||
/// </summary>
|
||||
public HttpResponse Get(HttpRequest request) {
|
||||
try {
|
||||
|
||||
// Prepare the select statement based on the given criteria
|
||||
// If no criteria is given, throw an exception.
|
||||
// Using a prepare due to the potential of SQL injection when accepting
|
||||
// any form of user data.
|
||||
var queries = new Dictionary<string, Func<string, BoundStatement>> {
|
||||
["id"] = id => cassandraSession.Prepare(@"SELECT * FROM Roommapper.Maps WHERE Id = ?;").Bind(Guid.Parse(id)),
|
||||
["name"] = name => cassandraSession.Prepare(@"SELECT * FROM Roommapper.Maps WHERE Name = ?;").Bind(name),
|
||||
["date"] = date => cassandraSession.Prepare(@"SELECT * FROM roommapper.maps WHERE Date = ?;").Bind(DateTime.Parse(date)),
|
||||
["version"] = version => cassandraSession.Prepare(@"SELECT * FROM roommapper.maps WHERE Version = ?;").Bind(int.Parse(version)),
|
||||
["all"] = _ => cassandraSession.Prepare(@"SELECT * FROM roommapper.maps;").Bind()
|
||||
@@ -29,7 +29,7 @@ public class RouteDatabase(ISession cassandraSession): IRoute {
|
||||
.Select(query => query.Value(request.QueryString[query.Key]))
|
||||
.FirstOrDefault();
|
||||
|
||||
if (selectStatement == null) throw new Exception("No valid search criteria given, give one of (id, date, version).");
|
||||
if (selectStatement == null) throw new Exception("No valid search criteria given, give one of (id, date, version, name).");
|
||||
|
||||
|
||||
// Execute the query and return all rows in an array
|
||||
@@ -65,9 +65,9 @@ public class RouteDatabase(ISession cassandraSession): IRoute {
|
||||
var uuid = Guid.NewGuid();
|
||||
|
||||
var insertStatement = cassandraSession.Prepare(@"
|
||||
INSERT INTO Roommapper.Maps(Id, Date, Version, Objects)
|
||||
VALUES (?, toTimeStamp(now()), ?, ?);
|
||||
").Bind(uuid, 1, parsedBody.objects);
|
||||
INSERT INTO Roommapper.Maps(Id, Name, Date, Version, Objects)
|
||||
VALUES (?, ?, toTimeStamp(now()), ?, ?);
|
||||
").Bind(uuid, parsedBody.name, 1, parsedBody.objects);
|
||||
|
||||
cassandraSession.Execute(insertStatement);
|
||||
|
||||
@@ -126,7 +126,8 @@ public class RouteDatabase(ISession cassandraSession): IRoute {
|
||||
Id = row.GetValue<Guid>("id").ToString(),
|
||||
Objects = $"[{row.GetValue<string>("objects")}]".FromJson<int[][]>(),
|
||||
Version = row.GetValue<int>("version"),
|
||||
Date = row.GetValue<DateTime>("date")
|
||||
Date = row.GetValue<DateTime>("date"),
|
||||
Name = row.GetValue<string>("name")
|
||||
}).ToList().ToJson();
|
||||
}
|
||||
}
|
||||
|
||||
142
src/Server/Console/Routes/RoutePath.cs
Normal file
142
src/Server/Console/Routes/RoutePath.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using Cassandra;
|
||||
using LibParse.Json;
|
||||
using LibServer.Http;
|
||||
using LibServer.Router;
|
||||
using Console.JsonClasses;
|
||||
|
||||
namespace Console.Routes;
|
||||
|
||||
public class RoutePath(ISession cassandraSession): IRoute {
|
||||
/// <summary>
|
||||
/// GET request for the database/path route, return a path of a map based on given criteria.
|
||||
/// </summary>
|
||||
public HttpResponse Get(HttpRequest request) {
|
||||
try {
|
||||
// Prepare the select statement based on the given criteria
|
||||
// If no criteria is given, throw an exception.
|
||||
// Using a prepare due to the potential of SQL injection when accepting
|
||||
// any form of user data.
|
||||
var queries = new Dictionary<string, Func<string, BoundStatement>> {
|
||||
["id"] = id => cassandraSession.Prepare(@"SELECT * FROM Roommapper.Routes WHERE Id = ?;").Bind(Guid.Parse(id)),
|
||||
["name"] = name => cassandraSession.Prepare(@"SELECT * FROM Roommapper.Routes WHERE Id = ?;").Bind(name)
|
||||
};
|
||||
|
||||
var selectStatement = queries
|
||||
.Where(query => request.QueryString.ContainsKey(query.Key))
|
||||
.Select(query => query.Value(request.QueryString[query.Key]))
|
||||
.FirstOrDefault();
|
||||
|
||||
if (selectStatement == null) throw new Exception("No valid search criteria given, give one of (id, name).");
|
||||
|
||||
// Execute the query and return all rows in an array
|
||||
var rowSet = cassandraSession.Execute(selectStatement);
|
||||
|
||||
// Create a list to hold the row data, convert it to a JSON string
|
||||
var rows = RowsToString(rowSet);
|
||||
|
||||
// Return the JSON string to the user
|
||||
return new HttpResponse(rows);
|
||||
} catch (Exception ex) {
|
||||
// Return error message if failed for any reason
|
||||
return new HttpResponse($"{{\"message\": \"{ex.Message.Replace("\"", "\\\"")}\"}}", 400);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// POST request for the database/path route, insert a new path containing
|
||||
/// routing data into the database.
|
||||
/// </summary>
|
||||
public HttpResponse Post(HttpRequest request) {
|
||||
try {
|
||||
// Parse request body to Data object, give error if non-nullable fields
|
||||
// are null.
|
||||
var parsedBody = request.Body?.FromJson<Data>();
|
||||
if (parsedBody?.objects == null) {
|
||||
throw new Exception("objectData is null");
|
||||
}
|
||||
|
||||
if (parsedBody?.id == null) {
|
||||
throw new Exception("id is null");
|
||||
}
|
||||
|
||||
var selectStatement =
|
||||
parsedBody?.id != null ? cassandraSession.Prepare(@"SELECT * FROM Roommapper.Maps WHERE Id = ?;")
|
||||
.Bind(Guid.Parse(parsedBody.id)) :
|
||||
throw new Exception("No valid search criteria given, give one of (id).");
|
||||
|
||||
// Execute the query and return all rows in an array
|
||||
var rowSet = cassandraSession.Execute(selectStatement);
|
||||
|
||||
if (rowSet.IsExhausted()) {
|
||||
throw new Exception("No matching map found with the given criteria.");
|
||||
}
|
||||
|
||||
// Prepare the insert statement, bind the values, and execute the query
|
||||
// Using a prepare due to the potential of SQL injection when accepting
|
||||
// any form of user data.
|
||||
var uuid = Guid.Parse(parsedBody.id);
|
||||
|
||||
var insertStatement = cassandraSession.Prepare(@"
|
||||
INSERT INTO Roommapper.Routes(Id, path)
|
||||
VALUES (?, ?);
|
||||
").Bind(uuid, parsedBody.objects);
|
||||
|
||||
cassandraSession.Execute(insertStatement);
|
||||
|
||||
// Return success message with the UUID of the inserted row
|
||||
return new HttpResponse($"{{\"message\":\"success\",\"id\":\"{uuid}\"}}");
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Return error message if failed for any reason
|
||||
return new HttpResponse($"{{\"message\": \"{ex.Message.Replace("\"", "\\\"")}\"}}", 400);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a given row
|
||||
/// </summary>
|
||||
public HttpResponse Delete(HttpRequest request) {
|
||||
try {
|
||||
var parsedBody = request.Body?.FromJson<Data>();
|
||||
|
||||
// Prepare the select statement based on the given criteria
|
||||
// If no criteria is given, throw an exception.
|
||||
// Using a prepare due to the potential of SQL injection when accepting
|
||||
// any form of user data.
|
||||
var selectStatement =
|
||||
parsedBody?.id != null ? cassandraSession.Prepare(@"SELECT * FROM Roommapper.Routes WHERE Id = ?;")
|
||||
.Bind(Guid.Parse(parsedBody.id)) :
|
||||
throw new Exception("No valid search criteria given, give one of (id).");
|
||||
|
||||
// Execute the query and return all rows in an array
|
||||
var rowSet = cassandraSession.Execute(selectStatement);
|
||||
|
||||
if (rowSet.IsExhausted()) {
|
||||
throw new Exception("No rows found with the given criteria.");
|
||||
}
|
||||
|
||||
// Prepare and execute delete statement
|
||||
var deleteStatement = cassandraSession.Prepare(@"DELETE FROM Roommapper.Routes WHERE Id = ?;")
|
||||
.Bind(Guid.Parse(parsedBody.id));
|
||||
|
||||
cassandraSession.Execute(deleteStatement);
|
||||
|
||||
return new HttpResponse($"{{\"message\":\"success\",\"entry\":{RowsToString(rowSet)}}}");
|
||||
|
||||
} catch (Exception ex) {
|
||||
return new HttpResponse($"{{\"message\": \"{ex.Message.Replace("\"", "\\\"")}\"}}", 400);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Casts a set of `RowSet` into a valid JSON string.
|
||||
/// </summary>
|
||||
/// <param name="rowSet">The `RowSet` to cast.</param>
|
||||
/// <returns>A valid JSON string formed from `rowSet`.</returns>
|
||||
private string RowsToString(RowSet rowSet) {
|
||||
return rowSet.Select(row => new RowData {
|
||||
Id = row.GetValue<Guid>("id").ToString(),
|
||||
Objects = $"[{row.GetValue<string>("path")}]".FromJson<int[][]>()
|
||||
}).ToList().ToJson();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -47,7 +47,6 @@ public class HttpRequest {
|
||||
catch { return -1; }
|
||||
}
|
||||
|
||||
|
||||
var querySplit = Route.Split("?");
|
||||
if (querySplit.Length == 1) return 0;
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,5 +1,5 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeInspection/PencilsConfiguration/ActualSeverity/@EntryValue">INFO</s:String>
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=d452b4e4_002Daa15_002D4fa9_002D847f_002D33d4d7fab23d/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="ParseJsonToAnonymousObject" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||
<Project Location="C:\Users\wesse\OneDrive - Hogeschool Inholland\roommapper\src\Server\Tests" Presentation="&lt;Tests&gt;" />
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=d452b4e4_002Daa15_002D4fa9_002D847f_002D33d4d7fab23d/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="ParseJsonToAnonymousObject" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||
<Project Location="C:\Users\wesse\OneDrive - Hogeschool Inholland\roommapper\src\Server\Tests" Presentation="&lt;Tests&gt;" />
|
||||
</SessionState></s:String></wpf:ResourceDictionary>
|
||||
@@ -15,8 +15,8 @@ export default class MapCanvas extends React.Component {
|
||||
// Get a reference to the canvas element so we can draw on it
|
||||
this.canvasRef = React.createRef();
|
||||
this.state = {
|
||||
inputValue: '8a97354d-d4ac-43c6-8b32-528cc24e3ede',
|
||||
searchOption: 'id',
|
||||
inputValue: '',
|
||||
searchOption: 'name',
|
||||
canvasWidth: props.width || this.defaultWidth,
|
||||
canvasHeight: props.height || this.defaultHeight,
|
||||
};
|
||||
@@ -33,16 +33,18 @@ export default class MapCanvas extends React.Component {
|
||||
this.draw();
|
||||
}
|
||||
|
||||
async getLinePoints() {
|
||||
async getLinePoints(endpoint = '', altKey, altVal) {
|
||||
try {
|
||||
const { searchOption, inputValue } = this.state;
|
||||
const url = `${API_ENDPOINT}/database?${searchOption}=${inputValue}`;
|
||||
const key = altKey || this.state.searchOption;
|
||||
const val = altVal || this.state.inputValue;
|
||||
const url = `${API_ENDPOINT}/database/${endpoint}?${key}=${val}`;
|
||||
const data = await (await fetch(url)).json();
|
||||
|
||||
// Combine all found sets into a singular array.
|
||||
const map = [];
|
||||
const map = { 'id': data.length < 2 ? data[0].Id : undefined, 'points': [] };
|
||||
if (!Array.isArray(data) || data.length < 1) return map;
|
||||
data.forEach((set) => set.Objects.forEach((coord) => map.push(coord)));
|
||||
data.forEach((set) => set.Objects.forEach((coord) => map.points.push(coord)));
|
||||
|
||||
return map;
|
||||
} catch (ex) {
|
||||
@@ -55,7 +57,8 @@ export default class MapCanvas extends React.Component {
|
||||
// Get the 2D context from the canvas element
|
||||
const ctx = this.canvasRef.current.getContext('2d');
|
||||
// Get the points to draw from the server
|
||||
const points = await this.getLinePoints();
|
||||
const map = await this.getLinePoints();
|
||||
const route = await this.getLinePoints('path', 'id', map ? map.id : '');
|
||||
|
||||
// Set the font for the text we will draw on the canvas
|
||||
ctx.font = "24px Comic Sans MS";
|
||||
@@ -64,14 +67,22 @@ export default class MapCanvas extends React.Component {
|
||||
ctx.clearRect(0, 0, this.state.canvasWidth, this.state.canvasHeight);
|
||||
|
||||
// If there are no points, display a message and return
|
||||
if (!points || points.length < 1) {
|
||||
if (!map || map.points.length < 1) {
|
||||
ctx.fillText("No valid entry found.", 10, this.state.canvasHeight / 2);
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop trough all coordinates, draw a dot at each point to form a top-down
|
||||
// view of the objects.
|
||||
points.forEach(point => {
|
||||
ctx.fillStyle = "#000";
|
||||
map.points.forEach(point => {
|
||||
ctx.beginPath();
|
||||
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();
|
||||
@@ -89,9 +100,8 @@ export default class MapCanvas extends React.Component {
|
||||
value={searchOption}
|
||||
onChange={(val) => this.setState({ searchOption: val })}
|
||||
>
|
||||
<Select value="name">Name</Select>
|
||||
<Select value="id">ID</Select>
|
||||
<Select value="version">Version</Select>
|
||||
<Select value="date">Date</Select>
|
||||
<Select value="all">All</Select>
|
||||
</Select>
|
||||
<Input
|
||||
|
||||
Reference in New Issue
Block a user