diff --git a/media/Server/eindverslag persoonlijke bijdrage/src/sections/2 data serialisatie.tex b/media/Server/eindverslag persoonlijke bijdrage/src/sections/2 data serialisatie.tex new file mode 100644 index 0000000..be9e0b2 --- /dev/null +++ b/media/Server/eindverslag persoonlijke bijdrage/src/sections/2 data serialisatie.tex @@ -0,0 +1,11 @@ +Er zijn vele opties om data te verzenden tussen client en server. +Voor dit project is er voor gekozen om data te serialiseren naar JSON. +Dit is een veelgebruikte methode om data te verzenden tussen client en server. +Het voordeel van JSON is dat het een lichtgewicht formaat is en dat het makkelijk te lezen is. +Dit is handig voor debugging en het is makkelijk om te zetten naar een object in JavaScript. +Een nadeel van JSON is dat het niet binair is en dat het niet zo snel is als binair. +Dit is echter geen probleem voor dit project, omdat de hoeveelheid data die verstuurd wordt klein is. +\subsection{Serialisatie} +\label{sec:serialisatie} +% Path: 2 data serialisatie.tex + diff --git a/src/Server/.idea/.idea.REST API/.idea/dataSources.local.xml b/src/Server/.idea/.idea.REST API/.idea/dataSources.local.xml index 00a143e..2403de8 100755 --- a/src/Server/.idea/.idea.REST API/.idea/dataSources.local.xml +++ b/src/Server/.idea/.idea.REST API/.idea/dataSources.local.xml @@ -4,6 +4,7 @@ " + true master_key diff --git a/src/Server/.idea/.idea.REST API/.idea/dataSources/469f6bec-f113-4467-9d39-7ac38558045a.xml b/src/Server/.idea/.idea.REST API/.idea/dataSources/469f6bec-f113-4467-9d39-7ac38558045a.xml index 4a1d21b..7f75353 100644 --- a/src/Server/.idea/.idea.REST API/.idea/dataSources/469f6bec-f113-4467-9d39-7ac38558045a.xml +++ b/src/Server/.idea/.idea.REST API/.idea/dataSources/469f6bec-f113-4467-9d39-7ac38558045a.xml @@ -8,33 +8,33 @@ 1 1 - - durable_writes:true -replication:{'class': 'org.apache.cassandra.locator.SimpleStrategy', 'replication_factor': '1'} - - - - durable_writes:true -replication:{'class': 'org.apache.cassandra.locator.LocalStrategy'} - - - - durable_writes:true -replication:{'class': 'org.apache.cassandra.locator.SimpleStrategy', 'replication_factor': '1'} - - - + 1 durable_writes:true replication:{'class': 'org.apache.cassandra.locator.NetworkTopologyStrategy', 'datacenter1': '1'} - + + durable_writes:true +replication:{'class': 'org.apache.cassandra.locator.LocalStrategy'} + + + + durable_writes:true +replication:{'class': 'org.apache.cassandra.locator.SimpleStrategy', 'replication_factor': '1'} + + + durable_writes:true replication:{'class': 'org.apache.cassandra.locator.SimpleStrategy', 'replication_factor': '3'} - + + durable_writes:true +replication:{'class': 'org.apache.cassandra.locator.EverywhereStrategy'} + + + durable_writes:true replication:{'class': 'org.apache.cassandra.locator.LocalStrategy'} @@ -44,17 +44,7 @@ replication:{'class': 'org.apache.cassandra.locator.LocalStrategy replication:{'class': 'org.apache.cassandra.locator.SimpleStrategy', 'replication_factor': '2'} - - durable_writes:true -replication:{'class': 'org.apache.cassandra.locator.EverywhereStrategy'} - - - - durable_writes:true -replication:{'class': 'org.apache.cassandra.locator.SimpleStrategy', 'replication_factor': '1'} - - - +
caching:{'keys': 'ALL', 'rows_per_partition': 'ALL'} compression:{'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'} bloom_filter_fp_chance:0.01 @@ -70,26 +60,54 @@ dclocal_read_repair_chance:0 compaction:{'class': 'SizeTieredCompactionStrategy'}
- + + 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'} + +
+ uuid|0s 1 - + int|0s 2 - + timestamp|0s 3 - + text|0s 4 - + id version|ASC date|ASC + + + + uuid|0s + 1 + + + text|0s + 2 + + + id diff --git a/src/Server/.idea/.idea.REST API/.idea/workspace.xml b/src/Server/.idea/.idea.REST API/.idea/workspace.xml index 900ce2f..d7f9255 100755 --- a/src/Server/.idea/.idea.REST API/.idea/workspace.xml +++ b/src/Server/.idea/.idea.REST API/.idea/workspace.xml @@ -9,12 +9,22 @@ + - - - - - + + + + + + + + + + + + + + - { + "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" ] } -}]]> +} diff --git a/src/Server/Console/Program.cs b/src/Server/Console/Program.cs index 3d2e120..ab867a4 100755 --- a/src/Server/Console/Program.cs +++ b/src/Server/Console/Program.cs @@ -19,6 +19,7 @@ internal static class Program { { "", new Root() }, { "database", new RouteDatabase(cassandraSession) }, { "database/metadata", new RouteMetadata(cassandraSession) }, + { "database/route", new RouteMap(cassandraSession) }, { "roomba/control", new RouteControl() } }; @@ -44,5 +45,12 @@ internal static class Program { Date timestamp, PRIMARY KEY (Id,Version,Date) )"); + + session.Execute(@" + CREATE TABLE IF NOT EXISTS Roommapper.Routes( + Id uuid, + Path text, + PRIMARY KEY (Id) + )"); } } diff --git a/src/Server/Console/Routes/RouteMap.cs b/src/Server/Console/Routes/RouteMap.cs new file mode 100644 index 0000000..98c8407 --- /dev/null +++ b/src/Server/Console/Routes/RouteMap.cs @@ -0,0 +1,126 @@ +using Cassandra; +using LibParse.Json; +using LibServer.Http; +using LibServer.Router; +using Console.JsonClasses; + +namespace Console.Routes; + +public class RouteMap(ISession cassandraSession): IRoute { + /// + /// GET request for the database/mapped route, return a map based on given criteria. + /// + 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> { + ["id"] = id => cassandraSession.Prepare(@"SELECT * FROM Roommapper.Routes WHERE Id = ?;").Bind(Guid.Parse(id)), + }; + + 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)."); + + + // 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); + } + } + + /// + /// POST request for the database route, insert a new map containing + /// object data into the database. + /// + 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(); + if (parsedBody?.objects == null) { + throw new Exception("objectData is null"); + } + + // 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); + } + } + + /// + /// Deletes a given row + /// + public HttpResponse Delete(HttpRequest request) { + try { + var parsedBody = request.Body?.FromJson(); + + // 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); + } + } + + /// + /// Casts a set of `RowSet` into a valid JSON string. + /// + /// The `RowSet` to cast. + /// A valid JSON string formed from `rowSet`. + private string RowsToString(RowSet rowSet) { + return rowSet.Select(row => new RowData { + Id = row.GetValue("id").ToString(), + Objects = $"[{row.GetValue("path")}]".FromJson() + }).ToList().ToJson(); + } +} diff --git a/src/Server/Console/bin/Debug/net8.0/Console b/src/Server/Console/bin/Debug/net8.0/Console index 4b14b11..d433124 100755 Binary files a/src/Server/Console/bin/Debug/net8.0/Console and b/src/Server/Console/bin/Debug/net8.0/Console differ diff --git a/src/Server/Console/bin/Debug/net8.0/Console.dll b/src/Server/Console/bin/Debug/net8.0/Console.dll index 4ce4914..5aa069c 100755 Binary files a/src/Server/Console/bin/Debug/net8.0/Console.dll and b/src/Server/Console/bin/Debug/net8.0/Console.dll differ diff --git a/src/Server/Console/bin/Debug/net8.0/Console.pdb b/src/Server/Console/bin/Debug/net8.0/Console.pdb index 1b1190c..b4ce524 100755 Binary files a/src/Server/Console/bin/Debug/net8.0/Console.pdb and b/src/Server/Console/bin/Debug/net8.0/Console.pdb differ diff --git a/src/Server/Console/bin/Debug/net8.0/LibParse.dll b/src/Server/Console/bin/Debug/net8.0/LibParse.dll index 1a7535f..f44c188 100755 Binary files a/src/Server/Console/bin/Debug/net8.0/LibParse.dll and b/src/Server/Console/bin/Debug/net8.0/LibParse.dll differ diff --git a/src/Server/Console/bin/Debug/net8.0/LibParse.pdb b/src/Server/Console/bin/Debug/net8.0/LibParse.pdb index 9c184df..82875fa 100755 Binary files a/src/Server/Console/bin/Debug/net8.0/LibParse.pdb and b/src/Server/Console/bin/Debug/net8.0/LibParse.pdb differ diff --git a/src/Server/Console/bin/Debug/net8.0/LibServer.dll b/src/Server/Console/bin/Debug/net8.0/LibServer.dll index 33acde8..904f362 100755 Binary files a/src/Server/Console/bin/Debug/net8.0/LibServer.dll and b/src/Server/Console/bin/Debug/net8.0/LibServer.dll differ diff --git a/src/Server/Console/bin/Debug/net8.0/LibServer.pdb b/src/Server/Console/bin/Debug/net8.0/LibServer.pdb index 743d628..ef3ff02 100755 Binary files a/src/Server/Console/bin/Debug/net8.0/LibServer.pdb and b/src/Server/Console/bin/Debug/net8.0/LibServer.pdb differ diff --git a/src/Server/LibParse/bin/Debug/net8.0/LibParse.dll b/src/Server/LibParse/bin/Debug/net8.0/LibParse.dll index 1a7535f..f44c188 100755 Binary files a/src/Server/LibParse/bin/Debug/net8.0/LibParse.dll and b/src/Server/LibParse/bin/Debug/net8.0/LibParse.dll differ diff --git a/src/Server/LibParse/bin/Debug/net8.0/LibParse.pdb b/src/Server/LibParse/bin/Debug/net8.0/LibParse.pdb index 9c184df..82875fa 100755 Binary files a/src/Server/LibParse/bin/Debug/net8.0/LibParse.pdb and b/src/Server/LibParse/bin/Debug/net8.0/LibParse.pdb differ diff --git a/src/Server/LibServer/Http/Request.cs b/src/Server/LibServer/Http/Request.cs index b574b7b..c1a9c96 100755 --- a/src/Server/LibServer/Http/Request.cs +++ b/src/Server/LibServer/Http/Request.cs @@ -47,7 +47,6 @@ public class HttpRequest { catch { return -1; } } - var querySplit = Route.Split("?"); if (querySplit.Length == 1) return 0; diff --git a/src/Server/LibServer/bin/Debug/net8.0/LibServer.dll b/src/Server/LibServer/bin/Debug/net8.0/LibServer.dll index 33acde8..904f362 100755 Binary files a/src/Server/LibServer/bin/Debug/net8.0/LibServer.dll and b/src/Server/LibServer/bin/Debug/net8.0/LibServer.dll differ diff --git a/src/Server/LibServer/bin/Debug/net8.0/LibServer.pdb b/src/Server/LibServer/bin/Debug/net8.0/LibServer.pdb index 743d628..ef3ff02 100755 Binary files a/src/Server/LibServer/bin/Debug/net8.0/LibServer.pdb and b/src/Server/LibServer/bin/Debug/net8.0/LibServer.pdb differ diff --git a/src/Server/REST API.sln.DotSettings.user b/src/Server/REST API.sln.DotSettings.user index 4bc068e..879bd4b 100755 --- a/src/Server/REST API.sln.DotSettings.user +++ b/src/Server/REST API.sln.DotSettings.user @@ -1,5 +1,5 @@  INFO - <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 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> \ No newline at end of file diff --git a/src/Web/src/components/mapCanvas.jsx b/src/Web/src/components/mapCanvas.jsx index 1e4f4ba..add36e6 100644 --- a/src/Web/src/components/mapCanvas.jsx +++ b/src/Web/src/components/mapCanvas.jsx @@ -51,11 +51,30 @@ export default class MapCanvas extends React.Component { } } + async getRoutePoints() { + try { + const { searchOption, inputValue } = this.state; + const url = `${API_ENDPOINT}/database/route?id=${inputValue}`; + const data = await (await fetch(url)).json(); + + // Combine all found sets into a singular array. + const map = []; + if (!Array.isArray(data) || data.length < 1) return map; + data.forEach((set) => set.Objects.forEach((coord) => map.push(coord))); + + return map; + } catch (ex) { + return; + // this.props.onError(ex); + } + } + async draw() { // 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 route = await this.getRoutePoints(); // Set the font for the text we will draw on the canvas ctx.font = "24px Comic Sans MS"; @@ -71,11 +90,19 @@ export default class MapCanvas extends React.Component { // Loop trough all coordinates, draw a dot at each point to form a top-down // view of the objects. + ctx.fillStyle = "#000"; points.forEach(point => { ctx.beginPath(); ctx.arc(point[1], point[0], 1, 0, 2 * Math.PI); ctx.fill(); }); + + ctx.fillStyle = "#800000"; + route.forEach(point => { + ctx.beginPath(); + ctx.arc(point[1], point[0], 1, 0, 2 * Math.PI); + ctx.fill(); + }); } render() {