diff --git a/IOTManagment/.vscode/launch.json b/IOTManagment/.vscode/launch.json new file mode 100644 index 0000000..2b2ec5b --- /dev/null +++ b/IOTManagment/.vscode/launch.json @@ -0,0 +1,35 @@ +{ + "version": "0.2.0", + "configurations": [ + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "name": ".NET Core Launch (web)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/API-Web/bin/Debug/net6.0/API-Web.dll", + "args": [], + "cwd": "${workspaceFolder}/API-Web", + "stopAtEntry": false, + // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)" + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + } + ] +} \ No newline at end of file diff --git a/IOTManagment/.vscode/tasks.json b/IOTManagment/.vscode/tasks.json new file mode 100644 index 0000000..f637419 --- /dev/null +++ b/IOTManagment/.vscode/tasks.json @@ -0,0 +1,41 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/API-Web/API-Web.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/API-Web/API-Web.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "--project", + "${workspaceFolder}/API-Web/API-Web.csproj" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/IOTManagment/API-Web/API-Web.csproj b/IOTManagment/API-Web/API-Web.csproj index bf91fc4..9738fee 100644 --- a/IOTManagment/API-Web/API-Web.csproj +++ b/IOTManagment/API-Web/API-Web.csproj @@ -9,6 +9,17 @@ + + + + + + + + + + + diff --git a/IOTManagment/IOTManagment.sln b/IOTManagment/IOTManagment.sln index d90b72b..e1fecf2 100644 --- a/IOTManagment/IOTManagment.sln +++ b/IOTManagment/IOTManagment.sln @@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Model", "Model\Model.csproj EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Services", "Services\Services.csproj", "{14640077-5376-4D81-92C0-5EC70D84E54E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{D92E85EE-E28A-4B66-8A88-83AE6EDDB1B4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -33,6 +35,10 @@ Global {14640077-5376-4D81-92C0-5EC70D84E54E}.Debug|Any CPU.Build.0 = Debug|Any CPU {14640077-5376-4D81-92C0-5EC70D84E54E}.Release|Any CPU.ActiveCfg = Release|Any CPU {14640077-5376-4D81-92C0-5EC70D84E54E}.Release|Any CPU.Build.0 = Release|Any CPU + {D92E85EE-E28A-4B66-8A88-83AE6EDDB1B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D92E85EE-E28A-4B66-8A88-83AE6EDDB1B4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D92E85EE-E28A-4B66-8A88-83AE6EDDB1B4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D92E85EE-E28A-4B66-8A88-83AE6EDDB1B4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/IOTManagment/Model/Class1.cs b/IOTManagment/Model/Class1.cs deleted file mode 100644 index 2572ecc..0000000 --- a/IOTManagment/Model/Class1.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Model -{ - public class Class1 - { - - } -} \ No newline at end of file diff --git a/IOTManagment/Model/Messages/Message.cs b/IOTManagment/Model/Messages/Message.cs new file mode 100644 index 0000000..6f800f7 --- /dev/null +++ b/IOTManagment/Model/Messages/Message.cs @@ -0,0 +1,32 @@ +using Model.Nodes; +using System; +using System.Net; + +namespace Model.Messages +{ + public class Message + { + public Guid messageId { get; set; } = Guid.NewGuid(); + public string messageBody { get; } // = PayloadBody + public MessageType messageType { get; } + public string senderIP { get; } + public int senderPort { get; } + + public Message(string messageBody, MessageType messageType, string senderIP,int senderPort) + { + this.messageBody = messageBody; + this.messageType = messageType; + this.senderIP = senderIP; + this.senderPort = senderPort; + } + } + + public enum MessageType + { + CONNECT=1, + FORWARD=2, + RESPONSEAPI=3, + QUERY = 4, + STOP = 5, + }; +} \ No newline at end of file diff --git a/IOTManagment/Model/Messages/SelectVariableResult.cs b/IOTManagment/Model/Messages/SelectVariableResult.cs new file mode 100644 index 0000000..4fddd0a --- /dev/null +++ b/IOTManagment/Model/Messages/SelectVariableResult.cs @@ -0,0 +1,14 @@ +using Model.Queries.Variables; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model.Messages +{ + public class SelectVariableResult : SelectVariable + { + public double? Value { get; set; } + } +} diff --git a/IOTManagment/Model/Nodes/Enum/DataType.cs b/IOTManagment/Model/Nodes/Enum/DataType.cs new file mode 100644 index 0000000..ecee4ef --- /dev/null +++ b/IOTManagment/Model/Nodes/Enum/DataType.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model.Nodes.Enum +{ + public enum DataType + { + TEMPERATURE_CPU = 1, + TEMPERATURE_GPU = 2, + TEST_VAR = 3, + } +} diff --git a/IOTManagment/Model/Nodes/Enum/NodeType.cs b/IOTManagment/Model/Nodes/Enum/NodeType.cs new file mode 100644 index 0000000..409ae19 --- /dev/null +++ b/IOTManagment/Model/Nodes/Enum/NodeType.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model.Nodes.Enum +{ + public enum NodeType + { + SENSOR = 1, + NODE = 2, + ROUTENODE = 3, + ROOT = 4 + } +} diff --git a/IOTManagment/Model/Nodes/Enum/Status.cs b/IOTManagment/Model/Nodes/Enum/Status.cs new file mode 100644 index 0000000..c78e948 --- /dev/null +++ b/IOTManagment/Model/Nodes/Enum/Status.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model.Nodes.Enum +{ + public enum Status + { + INACTIVE = 0, + ACTIVE = 1 + } +} diff --git a/IOTManagment/Model/Nodes/INode.cs b/IOTManagment/Model/Nodes/INode.cs new file mode 100644 index 0000000..f3a97a6 --- /dev/null +++ b/IOTManagment/Model/Nodes/INode.cs @@ -0,0 +1,26 @@ +using Model.Nodes.Enum; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace Model.Nodes +{ + public interface INode + { + + public string? Parent { get; set; } + public int? ParentPort { get; set; } + + public string Address { get; set; } + public int AddressPort { get; set; } + + public NodeType Type { get; set; } + public Status Status { get; set; } + public DataType DataType { get; set; } + + + } +} diff --git a/IOTManagment/Model/Nodes/Node.cs b/IOTManagment/Model/Nodes/Node.cs new file mode 100644 index 0000000..ed65aad --- /dev/null +++ b/IOTManagment/Model/Nodes/Node.cs @@ -0,0 +1,26 @@ +using Model.Nodes.Enum; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; + + +namespace Model.Nodes +{ + public class Node : INode + { + public string? Parent { get; set; } + public int? ParentPort { get; set; } + + public string Address { get; set; } + public int AddressPort { get; set; } + + public NodeType Type { get; set; } + public Status Status { get; set; } + public DataType DataType { get; set; } + + + } +} diff --git a/IOTManagment/Model/Queries/Enums/SelectOperator.cs b/IOTManagment/Model/Queries/Enums/SelectOperator.cs new file mode 100644 index 0000000..36fd348 --- /dev/null +++ b/IOTManagment/Model/Queries/Enums/SelectOperator.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model.Queries.Enums +{ + public enum SelectOperator + { + None, + Sum + } +} diff --git a/IOTManagment/Model/Queries/Enums/WhereExpOperator.cs b/IOTManagment/Model/Queries/Enums/WhereExpOperator.cs new file mode 100644 index 0000000..6b9b0d8 --- /dev/null +++ b/IOTManagment/Model/Queries/Enums/WhereExpOperator.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model.Queries.Enums +{ + public enum WhereExpOperator + { + GreaterThan, + LessThan, + LessThanOrEqual, + GreaterThenOrEqual, + NotEqual, + Equal + } +} diff --git a/IOTManagment/Model/Queries/Enums/WhereOperator.cs b/IOTManagment/Model/Queries/Enums/WhereOperator.cs new file mode 100644 index 0000000..b134770 --- /dev/null +++ b/IOTManagment/Model/Queries/Enums/WhereOperator.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model.Queries.Enums +{ + public enum WhereOperator + { + None, + And, + Or + } +} diff --git a/IOTManagment/Model/Queries/Expressions/WhereExpression.cs b/IOTManagment/Model/Queries/Expressions/WhereExpression.cs new file mode 100644 index 0000000..acccdd7 --- /dev/null +++ b/IOTManagment/Model/Queries/Expressions/WhereExpression.cs @@ -0,0 +1,16 @@ +using Model.Queries.Enums; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model.Queries.Expressions +{ + public class WhereExpression + { + public string exp1 { get; set; } + public string exp2 { get; set; } + public WhereExpOperator Operator { get; set; } + } +} diff --git a/IOTManagment/Model/Queries/Query.cs b/IOTManagment/Model/Queries/Query.cs new file mode 100644 index 0000000..e4b7de4 --- /dev/null +++ b/IOTManagment/Model/Queries/Query.cs @@ -0,0 +1,17 @@ +using Model.Queries.Statements; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model.Queries +{ + public class Query + { + public Guid Id { get; set; } = Guid.NewGuid(); + public SelectStatement SelectStatement { get; set; } + public IntervalStatement IntervalStatement { get; set; } + public WhereStatement? WhereStatement { get; set; } + } +} diff --git a/IOTManagment/Model/Queries/Statements/IntervalStatement.cs b/IOTManagment/Model/Queries/Statements/IntervalStatement.cs new file mode 100644 index 0000000..c76e8aa --- /dev/null +++ b/IOTManagment/Model/Queries/Statements/IntervalStatement.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model.Queries.Statements +{ + public class IntervalStatement + { + public int Interval { get; set; } + } +} diff --git a/IOTManagment/Model/Queries/Statements/SelectStatement.cs b/IOTManagment/Model/Queries/Statements/SelectStatement.cs new file mode 100644 index 0000000..c033cc6 --- /dev/null +++ b/IOTManagment/Model/Queries/Statements/SelectStatement.cs @@ -0,0 +1,14 @@ +using Model.Queries.Variables; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model.Queries.Statements +{ + public class SelectStatement + { + public List Variables { get; set; } = new List(); + } +} diff --git a/IOTManagment/Model/Queries/Statements/WhereStatement.cs b/IOTManagment/Model/Queries/Statements/WhereStatement.cs new file mode 100644 index 0000000..a2e069c --- /dev/null +++ b/IOTManagment/Model/Queries/Statements/WhereStatement.cs @@ -0,0 +1,18 @@ +using Model.Queries.Enums; +using Model.Queries.Variables; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model.Queries.Statements +{ + public class WhereStatement + { + public List Variables { get; set; } = new List(); + public WhereOperator Operator { get; set; } + + + } +} diff --git a/IOTManagment/Model/Queries/Variables/SelectVariable.cs b/IOTManagment/Model/Queries/Variables/SelectVariable.cs new file mode 100644 index 0000000..23d379a --- /dev/null +++ b/IOTManagment/Model/Queries/Variables/SelectVariable.cs @@ -0,0 +1,15 @@ +using Model.Queries.Enums; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model.Queries.Variables +{ + public class SelectVariable + { + public string Variable { get; set; } + public SelectOperator Operator { get; set; } + } +} diff --git a/IOTManagment/Model/Queries/Variables/WhereVariable.cs b/IOTManagment/Model/Queries/Variables/WhereVariable.cs new file mode 100644 index 0000000..39f8c87 --- /dev/null +++ b/IOTManagment/Model/Queries/Variables/WhereVariable.cs @@ -0,0 +1,17 @@ +using Model.Queries.Enums; +using Model.Queries.Expressions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model.Queries.Variables +{ + public class WhereVariable + { + public List Expressions { get; set; } = new List(); // < > = + public List Operators { get; set; } = new List(); // AND OR + + } +} diff --git a/IOTManagment/NodeEngine/Jobs/QueryExecutionJob.cs b/IOTManagment/NodeEngine/Jobs/QueryExecutionJob.cs new file mode 100644 index 0000000..3bdf0d7 --- /dev/null +++ b/IOTManagment/NodeEngine/Jobs/QueryExecutionJob.cs @@ -0,0 +1,46 @@ +using Model.Messages; +using Model.Queries; +using Model.Queries.Statements; +using NodeEngine.Networking; +using NodeEngine.Services; +using Quartz; +using Serilog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.ServiceModel.Channels; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + +namespace NodeEngine.Jobs +{ + public class QueryExecutionJob : IJob + { + + public async Task Execute(IJobExecutionContext context) + { + SensorManager sensorManager = new SensorManager(); + QueryHandler handler = new QueryHandler(sensorManager); + + JobDataMap dataMap = context.JobDetail.JobDataMap; + SelectStatement? selectStatement = JsonSerializer.Deserialize(dataMap.GetString("Select")); + WhereStatement? whereStatement = JsonSerializer.Deserialize(dataMap.GetString("Where")); + + if(whereStatement != null && handler.CheckWhereStatement(whereStatement)) + { + IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(dataMap.GetString("IP")), int.Parse(dataMap.GetString("Port"))); + Log.Logger.Information("Job-Query has been evaluated returning true"); + + // Select The data specified and send it to parent //TODO : make payload with data + string payload = JsonSerializer.Serialize(handler.GetSelectResults(selectStatement)); + + var msg = new Model.Messages.Message(payload, MessageType.RESPONSEAPI,null,-1); // TODO: add local ip and port + var sender = new NetworkSender(endPoint, JsonSerializer.Serialize(msg)); + var senderThread = new Thread(() => sender.SendMessage()); + senderThread.Start(); + } + } + } +} diff --git a/IOTManagment/NodeEngine/Networking/MessageHandler.cs b/IOTManagment/NodeEngine/Networking/MessageHandler.cs new file mode 100644 index 0000000..da33936 --- /dev/null +++ b/IOTManagment/NodeEngine/Networking/MessageHandler.cs @@ -0,0 +1,113 @@ +using System; +using System.Net; +using System.Text.Json; +using Model.Messages; +using Model.Queries; +using NodeEngine.Services; +using Serilog; + +namespace NodeEngine.Networking +{ + public class MessageHandler + { + List nodeChildren; + QueryScheduler scheduler; + IPEndPoint parentEndPoint; + public MessageHandler(IPEndPoint parent) + { + nodeChildren = new List(); + scheduler = new QueryScheduler(); + parentEndPoint = parent; + } + + public async void HandleMessage(string message) + { + try + { + var msg = JsonSerializer.Deserialize(message); + + // MessageType + switch (msg.messageType) + { + case MessageType.CONNECT: + { + Console.WriteLine("MessageType: CONNECT"); + var node = new IPEndPoint(IPAddress.Parse(msg.senderIP), msg.senderPort); + if (nodeChildren.Any(x => x.Address.Equals(IPAddress.Parse(msg.senderIP)))) + { + Console.WriteLine("Node already exists."); + break; + } + nodeChildren.Add(node); + break; + } + case MessageType.FORWARD: + { + Console.WriteLine("MessageType: FOWARD"); + + foreach (IPEndPoint child in nodeChildren) + { + var sender = new NetworkSender(child, message); + sender.SendMessage(); + } + + //TODO: Do something to Query + + break; + } + case MessageType.RESPONSEAPI: + { + Console.WriteLine("MessageType: RESPONSEAPI"); + break; + } + case MessageType.QUERY: + { + Console.WriteLine("MessageType: QUERY"); + Query q = JsonSerializer.Deserialize(msg.messageBody); + if (q == null) throw new Exception("Query is null"); + + await scheduler.AddQueryJobAsync(q,parentEndPoint); + foreach (IPEndPoint child in nodeChildren) + { + var sender = new NetworkSender(child, message); + sender.SendMessage(); + } + break; + } + case MessageType.STOP: + { + Console.WriteLine($"MessageType: STOP QUERY {msg.messageBody}"); + + try + { + var id = JsonSerializer.Deserialize(msg.messageBody); + await scheduler.RemoveQueryjobAsync(id); + + foreach (IPEndPoint child in nodeChildren) + { + var sender = new NetworkSender(child, message); + sender.SendMessage(); + } + } + catch (Exception e) + { + Console.WriteLine($"Could not parse {msg.messageBody} as GUID"); + Console.WriteLine(e); + } + break; + } + default: + // TODO: Handle if no type is given + Log.Warning("No msgType: " + message); + break; + } + } + catch (Exception e) + { + Console.WriteLine($"Could not deserialize. Message: {message}"); + Console.WriteLine($"Exception: {e}"); + } + } + } +} + diff --git a/IOTManagment/NodeEngine/Networking/NetworkListener.cs b/IOTManagment/NodeEngine/Networking/NetworkListener.cs new file mode 100644 index 0000000..af65e16 --- /dev/null +++ b/IOTManagment/NodeEngine/Networking/NetworkListener.cs @@ -0,0 +1,38 @@ +using System; +using NetMQ.Sockets; +using NetMQ; + +namespace NodeEngine.Networking +{ + public class NetworkListener + { + + MessageHandler messageHandler; + public NetworkListener(MessageHandler handler) + { + messageHandler = handler; + } + + public void StartListener() + { + using (var listener = new PullSocket()) + { + Console.WriteLine("Started Listening on port 6001..."); + listener.Bind("tcp://0.0.0.0:6001"); + + while (true) + { + // Listen for messsages + string msg = listener.ReceiveFrameString(); + Console.WriteLine("Received frame: {0}", msg); + + + // Handle the Message + messageHandler.HandleMessage(msg); + } + } + + } + } +} + diff --git a/IOTManagment/NodeEngine/Networking/NetworkSender.cs b/IOTManagment/NodeEngine/Networking/NetworkSender.cs new file mode 100644 index 0000000..563cc8f --- /dev/null +++ b/IOTManagment/NodeEngine/Networking/NetworkSender.cs @@ -0,0 +1,34 @@ +using System; +using System.Net; +using NetMQ; +using NetMQ.Sockets; +using Model.Messages; + +namespace NodeEngine.Networking +{ + public class NetworkSender + { + IPEndPoint Receiver; + string Message; + + public NetworkSender(IPEndPoint receiver, string message) + { + this.Receiver = receiver; + this.Message = message; + } + + public void SendMessage() + { + var sender = new PushSocket(); + + Console.WriteLine("Connecting to socket..."); + sender.Connect($"tcp://{Receiver.Address}:{Receiver.Port}"); + + sender.SendFrame(Message); + + Console.WriteLine("Message sent. Closing thread."); + + } + } +} + diff --git a/IOTManagment/NodeEngine/NodeEngine.csproj b/IOTManagment/NodeEngine/NodeEngine.csproj index 74abf5c..2aac0d2 100644 --- a/IOTManagment/NodeEngine/NodeEngine.csproj +++ b/IOTManagment/NodeEngine/NodeEngine.csproj @@ -1,4 +1,4 @@ - + Exe @@ -7,4 +7,29 @@ enable + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/IOTManagment/NodeEngine/Program.cs b/IOTManagment/NodeEngine/Program.cs index 3751555..5ae6301 100644 --- a/IOTManagment/NodeEngine/Program.cs +++ b/IOTManagment/NodeEngine/Program.cs @@ -1,2 +1,81 @@ // See https://aka.ms/new-console-template for more information -Console.WriteLine("Hello, World!"); +using NodeEngine.Services; +using Services; +using System.Net; +using System.Text.Json; +using NodeEngine.Jobs; +using Model.Queries; +using Model.Queries.Statements; +using Serilog; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.DependencyInjection; +using NodeEngine.Networking; +using Model.Messages; +using System.Net.Sockets; +using Model.Nodes; +using Model.Nodes.Enum; + +// Setup logger +var log = new LoggerConfiguration() + .MinimumLevel.Information() + .WriteTo.Console() + .CreateLogger(); +// Set global logger +Log.Logger = log; +// create log factory for scheduler +var logFactory = new LoggerFactory() + .AddSerilog(log); +// sets the logger for the Scheduler +Quartz.Logging.LogContext.SetCurrentLogProvider(logFactory); + +// Setup Receiver for CONNECT Message +Console.WriteLine("Enter Connection ip"); +var recieverIp = Console.ReadLine(); +Console.WriteLine("Enter Connection Port"); +int recieverPort = int.Parse(Console.ReadLine()); + +var reciever = new IPEndPoint(IPAddress.Parse(recieverIp), recieverPort); +MessageHandler handler = new MessageHandler(reciever); + +// Listener (OBS: LISTNER SHOULD RUN FIRST - CAN'T SEND WITHOUT LISTENER) +var listener = new NetworkListener(handler); +var listenerThread = new Thread(() => listener.StartListener()); +listenerThread.Start(); +Console.WriteLine("Started Listener on port 6001"); + +// Get Local IP Address +string nodeIp = default; + +var host = Dns.GetHostEntry(Dns.GetHostName()); +foreach (var localIp in host.AddressList) +{ + if (localIp.AddressFamily == AddressFamily.InterNetwork && localIp.ToString() != "127.0.0.1") + { + nodeIp = localIp.ToString(); + Console.WriteLine("IP Address of this node = " + nodeIp); + } +} + +//EndPoint +var NodeEndPoint = new IPEndPoint(IPAddress.Parse(nodeIp), 6001); + +//Node & serialization +var node = new Node +{ + Parent = recieverIp, + ParentPort = recieverPort, + Address = nodeIp, + AddressPort = 6001, + Type = NodeType.NODE, + Status = Status.ACTIVE, + DataType = DataType.TEMPERATURE_CPU, +}; + +var jsonNode = JsonSerializer.Serialize(node); + +// Sender-connect message to server. +var msg = new Message(jsonNode, MessageType.CONNECT, nodeIp, 6001); +var json = JsonSerializer.Serialize(msg); +var sender = new NetworkSender(reciever, json); +var senderThread = new Thread(() => sender.SendMessage()); +senderThread.Start(); \ No newline at end of file diff --git a/IOTManagment/NodeEngine/Services/IQueryHandler.cs b/IOTManagment/NodeEngine/Services/IQueryHandler.cs new file mode 100644 index 0000000..e9314e6 --- /dev/null +++ b/IOTManagment/NodeEngine/Services/IQueryHandler.cs @@ -0,0 +1,18 @@ +using Model.Messages; +using Model.Queries.Statements; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NodeEngine.Services +{ + public interface IQueryHandler + { + public bool CheckWhereStatement(WhereStatement statement); + + public ListGetSelectResults(SelectStatement statement); + + } +} diff --git a/IOTManagment/NodeEngine/Services/ISensorManager.cs b/IOTManagment/NodeEngine/Services/ISensorManager.cs new file mode 100644 index 0000000..75aeca9 --- /dev/null +++ b/IOTManagment/NodeEngine/Services/ISensorManager.cs @@ -0,0 +1,14 @@ +using Model.Nodes.Enum; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NodeEngine.Services +{ + public interface ISensorManager + { + public string GetSensorData(DataType type); + } +} diff --git a/IOTManagment/NodeEngine/Services/QueryHandler.cs b/IOTManagment/NodeEngine/Services/QueryHandler.cs new file mode 100644 index 0000000..9d1d033 --- /dev/null +++ b/IOTManagment/NodeEngine/Services/QueryHandler.cs @@ -0,0 +1,165 @@ +using Model.Messages; +using Model.Nodes.Enum; +using Model.Queries.Enums; +using Model.Queries.Expressions; +using Model.Queries.Statements; +using Model.Queries.Variables; +using Serilog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +namespace NodeEngine.Services +{ + public class QueryHandler : IQueryHandler + { + private readonly ISensorManager sensorManager; + public QueryHandler(SensorManager sensorManager) + { + this.sensorManager = sensorManager; + } + + + public bool CheckWhereStatement(WhereStatement statement) + { + try + { + switch (statement.Operator) + { + case WhereOperator.None: + return CheckVariable(statement.Variables[0]); + case WhereOperator.And: + return CheckVariable(statement.Variables[0]) && CheckVariable(statement.Variables[0]); + case WhereOperator.Or: + return CheckVariable(statement.Variables[0]) || CheckVariable(statement.Variables[0]); + default: + break; + } + return false; + } + catch (Exception ex) + { + + throw; + } + } + + + private bool CheckVariable(WhereVariable variable) + { + if (variable == null) return false; + bool result = CheckExpression(variable.Expressions.First()); + variable.Expressions.RemoveAt(0); + + foreach (var op in variable.Operators) + { + switch (op) + { + case WhereOperator.And: + bool res = CheckExpression(variable.Expressions.First()); + variable.Expressions.RemoveAt(0); + if(res) result = true; + break; + case WhereOperator.Or: + if (result == false) return false; + result = CheckExpression(variable.Expressions.First()); + variable.Expressions.RemoveAt(0); + break; + default: + break; + } + } + + return result; + } + + // checks and evals the expression and returns if the expression is true or not + private bool CheckExpression(WhereExpression exp) + { + try + { + switch (exp.Operator) + { + case WhereExpOperator.GreaterThan: + return GetExpValue(exp.exp1) > GetExpValue(exp.exp2); + case WhereExpOperator.LessThan: + return GetExpValue(exp.exp1) < GetExpValue(exp.exp2); + case WhereExpOperator.LessThanOrEqual: + return GetExpValue(exp.exp1) <= GetExpValue(exp.exp2); + case WhereExpOperator.GreaterThenOrEqual: + return GetExpValue(exp.exp1) >= GetExpValue(exp.exp2); + case WhereExpOperator.NotEqual: + return GetExpValue(exp.exp1) != GetExpValue(exp.exp2); + case WhereExpOperator.Equal: + return GetExpValue(exp.exp1) == GetExpValue(exp.exp2); + default: + throw new ArgumentException("Unkown Operator"); + break; + } + } + catch (Exception ex) + { + Log.Logger.Error("Error when checking expression with message: " + ex.Message); + throw; + } + } + + private double GetExpValue(string exp) + { + try + { + // check if the exp is a datatype, if yes then gets the data from sensor + if (Enum.GetNames(typeof(DataType)).Any(x => x.ToLower() == exp)) + { + DataType dataType = Enum.Parse(exp,true); + string data = sensorManager.GetSensorData(dataType); + Double result = -1; + + if (dataType == DataType.TEMPERATURE_CPU) + { + result = double.Parse(data) / 1000; + } + + if (dataType == DataType.TEMPERATURE_GPU) + { + data = data.Replace("temp=", ""); + data = data.Replace("'C", ""); + result = double.Parse(data); + } + + if (dataType == DataType.TEST_VAR) + { + result = double.Parse(data); + } + + return result; + } + + return double.Parse(exp); + } + catch (Exception ex) + { + Log.Logger.Error("A Error happened, when trying to get expressions value with message: " + ex.Message); + throw; + } + } + + public List GetSelectResults(SelectStatement statement) + { + List result = new List(); + + foreach (SelectVariable variable in statement.Variables) + { + SelectVariableResult resultItem = new SelectVariableResult() + { + Variable = variable.Variable, + Operator = variable.Operator, + Value = GetExpValue(variable.Variable) + }; + result.Add(resultItem); + } + return result; + } + } +} diff --git a/IOTManagment/NodeEngine/Services/QueryScheduler.cs b/IOTManagment/NodeEngine/Services/QueryScheduler.cs new file mode 100644 index 0000000..e5959b4 --- /dev/null +++ b/IOTManagment/NodeEngine/Services/QueryScheduler.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Quartz.Impl; +using Quartz; +using Model.Queries; +using NodeEngine.Jobs; +using System.Text.Json; +using System.Net; + +namespace NodeEngine.Services +{ + public class QueryScheduler : IDisposable + { + + IScheduler scheduler; + + public QueryScheduler() + { + + // using defaults + StdSchedulerFactory factory = new StdSchedulerFactory(); + + scheduler = factory.GetScheduler().Result; + scheduler.Start().Wait(); + + } + + public async Task AddQueryJobAsync(Query query, IPEndPoint parent) + { + int interval = query.IntervalStatement.Interval; + + string selectStatement = JsonSerializer.Serialize(query.SelectStatement); + + string whereStatement = JsonSerializer.Serialize(query.WhereStatement); + + // create the job and inputs the query data into the jobs datamap as String + IJobDetail job = JobBuilder.Create() + .WithIdentity($"Job-{query.Id}", "Queries") + .UsingJobData("Select", selectStatement) + .UsingJobData("Where", whereStatement) + .UsingJobData("IP",parent.Address.ToString()) + .UsingJobData("Port",parent.Port.ToString()) + .Build(); + + // Create the jobs trigger with the interval specified in the given query + ITrigger trigger = TriggerBuilder.Create() + .WithIdentity($"Trigger-{query.Id}", "QETriggers") + .StartNow() + .WithSimpleSchedule(x => x + .WithInterval(TimeSpan.FromMilliseconds(interval)) + .RepeatForever()) + .Build(); + + // Schedule the job using The created trigger + await scheduler.ScheduleJob(job, trigger); + + } + + // Remove a job from the schedule + public async Task RemoveQueryjobAsync(Guid queryId) + { + await scheduler.DeleteJob(new JobKey($"Job-{queryId}", "Queries")); + } + + public void Dispose() + { + scheduler.Shutdown(); + + } + + + } +} diff --git a/IOTManagment/NodeEngine/Services/SensorManager.cs b/IOTManagment/NodeEngine/Services/SensorManager.cs new file mode 100644 index 0000000..637b625 --- /dev/null +++ b/IOTManagment/NodeEngine/Services/SensorManager.cs @@ -0,0 +1,58 @@ +using Model.Nodes.Enum; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NodeEngine.Services +{ + public class SensorManager : ISensorManager + { + + // TODO: Add commands for more datatypes + public string GetSensorData(DataType type) + { + + switch (type) + { + case DataType.TEMPERATURE_CPU: + return ExecuteCommand("cat /sys/class/thermal/thermal_zone0/temp"); + case DataType.TEMPERATURE_GPU: + return ExecuteCommand("vcgencmd measure_temp"); + case DataType.TEST_VAR: + Random rnd = new Random(); + return (rnd.NextDouble() * (80 - 0) + 0).ToString(); + default: + return null; + + } + + } + + // IMPORTANT: Commands will only work when running on linux system + private string ExecuteCommand(string command) + { + string result = ""; + using (System.Diagnostics.Process proc = new System.Diagnostics.Process()) + { + proc.StartInfo.FileName = "/bin/bash"; + proc.StartInfo.Arguments = "-c \" " + command + " \""; + proc.StartInfo.UseShellExecute = false; + proc.StartInfo.RedirectStandardOutput = true; + proc.StartInfo.RedirectStandardError = true; + proc.Start(); + + result += proc.StandardOutput.ReadToEnd(); + result += proc.StandardError.ReadToEnd(); + + proc.WaitForExit(); + } + return result; + } + } + + + +} diff --git a/IOTManagment/Server/Networking/MessageHandler.cs b/IOTManagment/Server/Networking/MessageHandler.cs new file mode 100644 index 0000000..3bbdd1f --- /dev/null +++ b/IOTManagment/Server/Networking/MessageHandler.cs @@ -0,0 +1,115 @@ +using System; +using System.Net; +using System.Text.Json; +using Model.Messages; +using Model.Nodes; +using Model.Queries; +using Services; + +namespace Server.Networking +{ + public class MessageHandler + { + List nodeChildren; + private readonly ITopologyManager _topologyManager; + private IPEndPoint localIp = new IPEndPoint(IPAddress.Parse("0.0.0.0"), 6000); + + public MessageHandler(ITopologyManager topologyManager) + { + _topologyManager = topologyManager; + nodeChildren = new List(); + } + + public void HandleMessage(string message) + { + Console.WriteLine("Received something"); + try + { + var msg = JsonSerializer.Deserialize(message); + + // MessageType + switch (msg.messageType) + { + case MessageType.CONNECT: + { + Console.WriteLine("MessageType: CONNECT"); + var node = JsonSerializer.Deserialize(msg.messageBody); + var NodeEndPoint = new IPEndPoint(IPAddress.Parse(node.Address), node.AddressPort); + if (nodeChildren.Any(x => x.Address.Equals(IPAddress.Parse(msg.senderIP)))) + { + Console.WriteLine($"Node {msg.senderIP} reconnected."); + break; + } + _topologyManager.AddNode(NodeEndPoint, node); + nodeChildren.Add(NodeEndPoint); + break; + } + + case MessageType.FORWARD: + { + Console.WriteLine("MessageType: FORWARD"); + + // Serialize message + var sendMsg = JsonSerializer.Serialize(msg); + + foreach (IPEndPoint child in nodeChildren) + { + var sender = new NetworkSender(child, sendMsg); + sender.SendMessage(); + } + + break; + } + case MessageType.RESPONSEAPI: + { + Console.WriteLine("MessageType: RESPONSEAPI"); + var varResults = JsonSerializer.Deserialize>(msg.messageBody); + + foreach (var item in varResults) + { + Console.WriteLine($"{item.Variable} has the value: {item.Value}"); + } + break; + } + default: + // TODO: Handle if no type is given + Console.WriteLine("No msgType" + msg); + break; + } + } + catch + { + Console.WriteLine($"Could not deserialize. Message: {message}"); + } + } + + public void SendStop(Guid id) + { + string body = JsonSerializer.Serialize(id); + Message msg = new Message(body, MessageType.STOP, localIp.Address.ToString(), localIp.Port); + // Serialize message + var sendMsg = JsonSerializer.Serialize(msg); + + foreach (IPEndPoint child in nodeChildren) + { + var sender = new NetworkSender(child, sendMsg); + sender.SendMessage(); + } + } + + public void SendQuery(Query query) + { + string body = JsonSerializer.Serialize(query); + Message msg = new Message(body, MessageType.QUERY,localIp.Address.ToString(), localIp.Port); + // Serialize message + var sendMsg = JsonSerializer.Serialize(msg); + + foreach (IPEndPoint child in nodeChildren) + { + var sender = new NetworkSender(child, sendMsg); + sender.SendMessage(); + } + } + } +} + diff --git a/IOTManagment/Server/Networking/NetworkListener.cs b/IOTManagment/Server/Networking/NetworkListener.cs new file mode 100644 index 0000000..2951b35 --- /dev/null +++ b/IOTManagment/Server/Networking/NetworkListener.cs @@ -0,0 +1,36 @@ +using System; +using NetMQ; +using NetMQ.Sockets; +using Model.Messages; + +namespace Server.Networking +{ + public class NetworkListener + { + private readonly MessageHandler _messageHandler; + public NetworkListener(MessageHandler messageHandler) + { + _messageHandler = messageHandler; + } + + public void StartListener() + { + using (var listener = new PullSocket()) + { + listener.Bind("tcp://0.0.0.0:6000"); + while (true) + { + // Listen for messsages + string msg = listener.ReceiveFrameString(); + Console.WriteLine("Received frame: {0}", msg); + + + // Handle the Message + _messageHandler.HandleMessage(msg); + } + } + + + } + } +} diff --git a/IOTManagment/Server/Networking/NetworkSender.cs b/IOTManagment/Server/Networking/NetworkSender.cs new file mode 100644 index 0000000..5d3c39b --- /dev/null +++ b/IOTManagment/Server/Networking/NetworkSender.cs @@ -0,0 +1,34 @@ +using System; +using System.Net; +using NetMQ; +using NetMQ.Sockets; +using Model.Messages; + +namespace Server.Networking +{ + public class NetworkSender + { + IPEndPoint receiver; + string message; + + public NetworkSender(IPEndPoint receiver, string message) + { + this.receiver = receiver; + this.message = message; + } + + public void SendMessage() + { + var sender = new PushSocket(); + + Console.WriteLine("Connecting to socket..."); + sender.Connect($"tcp://{receiver.Address}:{receiver.Port}"); + + sender.SendFrame(message); + + Console.WriteLine("Message sent. Closing thread."); + + } + } +} + diff --git a/IOTManagment/Server/Program.cs b/IOTManagment/Server/Program.cs new file mode 100644 index 0000000..47814ad --- /dev/null +++ b/IOTManagment/Server/Program.cs @@ -0,0 +1,356 @@ +using NetMQ.Sockets; +using NetMQ; +using Model.Messages; +using Services; +using System.Threading; +using System.Text.Json; +using Server.Networking; +using System.Net; +using Model.Queries; +using Model.Nodes; + + + +Console.WriteLine("Server starting..."); + +TopologyManager topologyManager = new(); + +MessageHandler messageHandler = new(topologyManager); +QueryParser queryParser = new QueryParser(); +List queries = new List(); +// Listener +var listener = new NetworkListener(messageHandler); +var listenerThread = new Thread(() => listener.StartListener()); +listenerThread.Start(); +Console.WriteLine("Started Listener on port 6000"); + + +Console.Clear(); +while (true) +{ + printCommands(0); + string input = Console.ReadLine().ToLower().Trim(); + bool isActive = true; + + switch (input) + { + case "stop"://Shutdown console-application + Environment.Exit(0); + break; + case "query": + //Query options + Console.Clear(); + while (isActive) { + printCommands(1); + string options = Console.ReadLine().ToLower().Trim(); + switch (options) + { + case "input": + // Gets query string from console and parses it, and sends it out to child nodes + bool innerIsActive = true; + Console.WriteLine("Input Query:"); + while (innerIsActive) + { + string query = Console.ReadLine().Trim(); + if (query == "quit") { Console.Clear(); break; } + if (query != "") + { + try + { + Query q = queryParser.ParserQuery(query); + queries.Add(q); + messageHandler.SendQuery(q); + Console.Clear(); + colorConsole("Query sent", ConsoleColor.Yellow, ConsoleColor.Black); + break; + } + catch (Exception ex) + { + colorConsole("Incorrect format: Use Query formating", ConsoleColor.Red, ConsoleColor.White); + Console.WriteLine("Re-enter query or write quit"); + } + } + else + { + Console.WriteLine("Re-enter Query or write quit"); + } + } + break; + case "info": //TODO: Create list of all queries + Console.Clear(); + int counter = 1; + Console.Clear(); + Console.WriteLine(" Active Quries "); + Console.WriteLine("----------------------------------------"); + + if (queries.Count() == 0) + { + colorConsole("No Active quries...", ConsoleColor.Red, ConsoleColor.White); + Console.WriteLine(""); + break; + } + foreach (Query x in queries) + { + Console.WriteLine($"{counter}| QueryId: {x.Id}"); + Console.WriteLine("----------------------------------------"); + counter++; + } + break; + case "stop": //TODO: send a stop message and remove query from list + Console.WriteLine("Input query id"); + Guid id = default; + while (true) + { + string value = Console.ReadLine().Trim(); + if (value == "quit") { Console.Clear(); break; } + if (value != "") + { + Guid.TryParse(value, out id); + var foundQ = queries.Where(x => x.Id == id).FirstOrDefault(); + if (foundQ != null) + { + Console.Clear(); + queries.Remove(foundQ); + colorConsole($"Query removed {id}", ConsoleColor.Yellow, ConsoleColor.Black); + messageHandler.SendStop(id); + break; + } + else { colorConsole("No such Query found", ConsoleColor.Red, ConsoleColor.White); Console.WriteLine("Please re-enter query id or write quit"); } + } + else + { + Console.WriteLine("Re-enter query id or write quit"); + } + } + break; + case "-help": + Console.Clear(); + break; + case "back": + Console.Clear(); + isActive = false; + break; + default: + Console.Clear(); + colorConsole("Unknown option. Use '-help' for an overview of options", ConsoleColor.Red, ConsoleColor.White); + break; + } + } + break; + case "nodes": + Console.Clear(); + while (isActive) + { + printCommands(2); + string option = Console.ReadLine().ToLower().Trim(); + switch (option) + { + case "all": + var listofnodes = topologyManager.GetIPAdresses().Values.ToList(); + int counter = 1; + Console.Clear(); + Console.WriteLine(" Active Nodes "); + Console.WriteLine("----------------------------------------"); + + if (listofnodes.Count() == 0) + { + colorConsole("No Active nodes...", ConsoleColor.Red, ConsoleColor.White); + Console.WriteLine(""); + + break; + } + foreach (var tmp in listofnodes) + { + Console.WriteLine($"{counter}| NodeIP: {tmp.Address}:{tmp.AddressPort}"); + Console.WriteLine("----------------------------------------"); + counter++; + } + Console.WriteLine(""); + break; + + case "node": + Console.WriteLine("Enter IPEndPoint:"); + bool innerIsActive = true; + + string parseIp = default; + IPEndPoint ip = default(IPEndPoint); + while (innerIsActive) + { + parseIp = Console.ReadLine().ToLower().Trim(); + if (parseIp == "quit") { Console.Clear(); break; } + if (parseIp != "") + { + try + { + IPEndPoint.TryParse(parseIp, out ip); + var node = topologyManager.GetNodeByIP(ip); + if (node != null) + { + #region META data printed + Console.Clear(); + colorConsole("------------------INFO-------------------",ConsoleColor.Yellow,ConsoleColor.Black); + Console.WriteLine($"NodeIP: {node.Address}:{node.AddressPort}"); + Console.WriteLine("----------------------------------------"); + Console.WriteLine($"Parent: {node.Parent}:{node.ParentPort}"); + Console.WriteLine($"Type: {node.Type}"); + Console.WriteLine($"Status: {node.Status}"); + Console.WriteLine($"DataType: {node.DataType}"); + Console.WriteLine(""); + #endregion + break; + } + else + { + Console.WriteLine($"No such IPEndPoint exists: {ip}"); + Console.WriteLine("Re-enter IPEndPoint or write quit"); + } + } + catch (Exception ex) + { + colorConsole("Incorrect format: Use IP formating", ConsoleColor.Red, ConsoleColor.White); + Console.WriteLine("Re-enter IPEndPoint or write quit"); + } + }else { + Console.WriteLine("Re-enter IPEndPoint or write quit"); + } + } + break; + case "back": + Console.Clear(); + isActive = false; + break; + case "-help": + Console.Clear(); + break; + default: + Console.Clear(); + colorConsole("Unknown option. Use '-help' to see all options", ConsoleColor.Red, ConsoleColor.White); + break; + } + } + break; + case "clear": + Console.Clear(); + break; + case "-help": + Console.Clear(); + break; + case "logo": + Console.Clear(); + printCommands(3); + break; + default: + Console.Clear(); + colorConsole("Unknown command. Use '-help' to see all commands",ConsoleColor.Red,ConsoleColor.White); + break; + } +} + +static void printCommands(int index) +{ + switch(index) + { + case 0: //Main + #region Main Printstatement + colorConsole("__________MAIN___________", ConsoleColor.DarkGreen, ConsoleColor.White); + colorConsole("Available commands: ", ConsoleColor.DarkGreen, ConsoleColor.White); + colorConsoleSame("stop ", ConsoleColor.White, ConsoleColor.Blue); + Console.WriteLine(" - Shutdown console-application."); + colorConsoleSame("query", ConsoleColor.White, ConsoleColor.Blue); + Console.WriteLine(" - Query interface."); + colorConsoleSame("nodes", ConsoleColor.White, ConsoleColor.Blue); + Console.WriteLine(" - Node interface."); + colorConsoleSame("clear", ConsoleColor.White, ConsoleColor.Blue); + Console.WriteLine(" - Clears the current Console text."); + Console.WriteLine("Please enter a command:"); + #endregion + break; + case 1: //Query + #region Query Printstatement + colorConsole("__________QUERY___________", ConsoleColor.DarkGreen, ConsoleColor.White); + colorConsole("Available options: ", ConsoleColor.DarkGreen, ConsoleColor.White); + colorConsoleSame("input", ConsoleColor.White, ConsoleColor.Blue); + Console.WriteLine(" - Send query to nodes"); + colorConsoleSame("info ", ConsoleColor.White, ConsoleColor.Blue); + Console.WriteLine(" - List of all current quries"); + colorConsoleSame("stop ", ConsoleColor.White, ConsoleColor.Blue); + Console.WriteLine(" - Specific node by id"); + colorConsoleSame("back ", ConsoleColor.White, ConsoleColor.Blue); + Console.WriteLine(" - Go back to previous 'page'"); + #endregion + break; + case 2://Node + #region Node Printstatement + colorConsole("___________NODE___________", ConsoleColor.DarkGreen, ConsoleColor.White); + colorConsole("Select a following option:", ConsoleColor.DarkGreen, ConsoleColor.White); + colorConsoleSame("all ", ConsoleColor.White, ConsoleColor.Blue); + Console.WriteLine(" - Displays all current nodes"); + colorConsoleSame("node", ConsoleColor.White, ConsoleColor.Blue); + Console.WriteLine(" - Displays information about a specific node via IPEndPoint"); + colorConsoleSame("back", ConsoleColor.White, ConsoleColor.Blue); + Console.WriteLine(" - Go back to previous 'page'"); + #endregion + break; + case 3://Funny + #region logo + //Normal + /* + colorConsole(@"###########################################", ConsoleColor.White, ConsoleColor.Black); + colorConsole(@"# ______ _ #", ConsoleColor.White, ConsoleColor.Black); + colorConsole(@"# |___ / | | #", ConsoleColor.White, ConsoleColor.Black); + colorConsole(@"# / / ___ _ __ ___ ___ __| | ___ #", ConsoleColor.White, ConsoleColor.Black); + colorConsole(@"# / / / _ \ '_ \ / __/ _ \ / _` |/ _ \ #", ConsoleColor.White, ConsoleColor.Black); + colorConsole(@"# / /_| __/ | | | (_| (_) | (_| | __/ #", ConsoleColor.White, ConsoleColor.Black); + colorConsole(@"# /_____\___|_| |_|\___\___/ \__,_|\___| #", ConsoleColor.White, ConsoleColor.Black); + colorConsole(@"# #", ConsoleColor.White, ConsoleColor.Black); + colorConsole(@"###########################################", ConsoleColor.White, ConsoleColor.Black); + */ + + //Ukraine-flag + + colorConsole(@"###########################################", ConsoleColor.Blue, ConsoleColor.White); + colorConsole(@"# ______ _ #", ConsoleColor.Blue, ConsoleColor.White); + colorConsole(@"# |___ / | | #", ConsoleColor.Blue, ConsoleColor.White); + colorConsole(@"# / / ___ _ __ ___ ___ __| | ___ #", ConsoleColor.Blue, ConsoleColor.White); + colorConsole(@"# / / / _ \ '_ \ / __/ _ \ / _` |/ _ \ #", ConsoleColor.Blue, ConsoleColor.White); + colorConsole(@"# / /_| __/ | | | (_| (_) | (_| | __/ #", ConsoleColor.Yellow, ConsoleColor.Black); + colorConsole(@"# /_____\___|_| |_|\___\___/ \__,_|\___| #", ConsoleColor.Yellow, ConsoleColor.Black); + colorConsole(@"# #", ConsoleColor.Yellow, ConsoleColor.Black); + colorConsole(@"###########################################", ConsoleColor.Yellow, ConsoleColor.Black); + + + #endregion + break; + } + +} + +static void colorConsoleSame(string text, ConsoleColor back, ConsoleColor fore) +{ + Console.ForegroundColor = fore; + Console.BackgroundColor = back; + Console.Write(text); + Console.ResetColor(); +} + +static void colorConsole(string text, ConsoleColor back, ConsoleColor fore) +{ + Console.ForegroundColor = fore; + Console.BackgroundColor = back; + Console.WriteLine(text); + Console.ResetColor(); +} + + +// Sender +/*var msg = new Message(Guid.NewGuid(), "hej fra server", MessageType.CONNECT, "127.0.0.1", 6000); +var json = JsonSerializer.Serialize(msg); +var sender = new NetworkSender(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6000), json); +var senderThread = new Thread(() => sender.SendMessage()); +senderThread.Start(); + +var msg2 = new Message(Guid.NewGuid(), "hej fra server 2", MessageType.CONNECT, "127.0.0.1", 6001); +var json2 = JsonSerializer.Serialize(msg2); +var sender2 = new NetworkSender(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6001), json2); +var senderThread2 = new Thread(() => sender2.SendMessage()); +senderThread2.Start();*/ \ No newline at end of file diff --git a/IOTManagment/Server/Server.csproj b/IOTManagment/Server/Server.csproj new file mode 100644 index 0000000..6399176 --- /dev/null +++ b/IOTManagment/Server/Server.csproj @@ -0,0 +1,24 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + + + + + + + + + + + diff --git a/IOTManagment/Services/Class1.cs b/IOTManagment/Services/Class1.cs deleted file mode 100644 index 3ca3ace..0000000 --- a/IOTManagment/Services/Class1.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Services -{ - public class Class1 - { - - } -} \ No newline at end of file diff --git a/IOTManagment/Services/ClientService.cs b/IOTManagment/Services/ClientService.cs new file mode 100644 index 0000000..ae90d99 --- /dev/null +++ b/IOTManagment/Services/ClientService.cs @@ -0,0 +1,28 @@ +using System; +using NetMQ; +using NetMQ.Sockets; + +namespace Services +{ + public class ClientService + { + public ClientService() + { + + } + + public void BuildClient() + { + using (var client = new RequestSocket()) + { + Console.WriteLine("Starting Client..."); + client.Connect("tcp://127.0.0.1:6000"); + client.SendFrame("Hello"); + var msg = client.ReceiveFrameString(); + Console.WriteLine("From Server: {0}", msg); + //client.ReceiveFrameString(); + } + } + } +} + diff --git a/IOTManagment/Services/CommunicationService.cs b/IOTManagment/Services/CommunicationService.cs new file mode 100644 index 0000000..87d9638 --- /dev/null +++ b/IOTManagment/Services/CommunicationService.cs @@ -0,0 +1,19 @@ +using System; +using System.Net; +using Model.Messages; + +namespace Services +{ + public class CommunicationService : ICommunicationService + { + public void SendMessage(IPAddress ip, Message outgoing) + { + throw new NotImplementedException(); + } + + public void ReceiveMessage(Message incoming) + { + throw new NotImplementedException(); + } + } +} diff --git a/IOTManagment/Services/Helpers/IntervalParseHelper.cs b/IOTManagment/Services/Helpers/IntervalParseHelper.cs new file mode 100644 index 0000000..a51a633 --- /dev/null +++ b/IOTManagment/Services/Helpers/IntervalParseHelper.cs @@ -0,0 +1,22 @@ +using Model.Queries.Statements; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Services.Helpers +{ + public class IntervalParseHelper + { + public IntervalStatement ParseInterval(string statement) + { + int interval = int.Parse(statement); + + return new IntervalStatement + { + Interval = interval, + }; + } + } +} diff --git a/IOTManagment/Services/Helpers/SelectParseHelper.cs b/IOTManagment/Services/Helpers/SelectParseHelper.cs new file mode 100644 index 0000000..890fd42 --- /dev/null +++ b/IOTManagment/Services/Helpers/SelectParseHelper.cs @@ -0,0 +1,51 @@ +using Model.Queries.Enums; +using Model.Queries.Statements; +using Model.Queries.Variables; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Services.Helpers +{ + public class SelectParseHelper + { + + public SelectStatement ParseSelect (string selectStatement) + { + SelectStatement ResultStatment = new SelectStatement (); + + string[] statements = selectStatement.Split (','); + + foreach (string statement in statements) + { + if(statement.Contains('(')) // if contains ( then an operator is present + { + ResultStatment.Variables.Add(new SelectVariable + { + Variable = statement.Split('(', ')')[1], + Operator = GetOperator (statement), + }); + } else + { + ResultStatment.Variables.Add(new SelectVariable + { + Variable = statement, + Operator = SelectOperator.None + }); + } + } + + return ResultStatment; + } + + + public SelectOperator GetOperator (string statement) + { + string ops = (statement.Split('(')[0]).ToLower(); + + return Enum.Parse(ops,true); + } + } +} diff --git a/IOTManagment/Services/Helpers/WhereParseHelper.cs b/IOTManagment/Services/Helpers/WhereParseHelper.cs new file mode 100644 index 0000000..4388663 --- /dev/null +++ b/IOTManagment/Services/Helpers/WhereParseHelper.cs @@ -0,0 +1,128 @@ +using Model.Queries.Enums; +using Model.Queries.Expressions; +using Model.Queries.Statements; +using Model.Queries.Variables; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Services.Helpers +{ + public class WhereParseHelper + { + public WhereStatement ParseWhere(string whereStatment) + { + whereStatment = whereStatment.Replace(" ", string.Empty); + var variables = whereStatment.Split('(', ')').Where(x => !string.IsNullOrEmpty(x)).ToArray(); + + WhereStatement statement = new WhereStatement(); + + foreach (string expr in variables) + { + if(!CheckIfOperator(expr)) + { + statement.Variables.Add(ParseVariable(expr)); + // the expr is a variable + + } else + { + statement.Operator = ParseOperator(expr); + } + } + + return statement; + } + + + private bool CheckIfOperator(string expr) + { + switch (expr) + { + case "&&": + return true; + break; + case "||": + return true; + break; + default: + return false; + break; + } + } + + private WhereVariable ParseVariable(string expr) + { + var variable = new WhereVariable(); + + string pattern = @"(&&|\|\|)"; + string[] operators = Regex.Matches(expr, pattern).Select(x => x.Value).ToArray(); + + foreach (var ops in operators) + { + variable.Operators.Add(ParseOperator(ops)); + } + + var exprs = expr.Split(new[] { "&&", "||" }, StringSplitOptions.TrimEntries); + foreach (var item in exprs) + { + var x = item.Split(new[] { "<", ">","<=",">=","=","!=" }, StringSplitOptions.TrimEntries); + + variable.Expressions.Add(new WhereExpression + { + exp1 = x[0], + exp2 = x[1], + Operator = ParseExpOperator(item.Replace(x[0], "").Replace(x[1], "")) + }); + } + + return variable; + } + + private WhereExpOperator ParseExpOperator(string ops) + { + switch (ops) + { + case "<": + return WhereExpOperator.LessThan; + break; + case ">": + return WhereExpOperator.GreaterThan; + break; + case "<=": + return WhereExpOperator.LessThanOrEqual; + break; + case">=": + return WhereExpOperator.GreaterThenOrEqual; + break; + case"=": + return WhereExpOperator.Equal; + break; + case"!=": + return WhereExpOperator.NotEqual; + break; + default: + throw new ArgumentOutOfRangeException(nameof(ops), ops); + break; + } + } + + private WhereOperator ParseOperator(string ops) + { + switch (ops) + { + case "||": + return WhereOperator.Or; + break; + case "&&": + return WhereOperator.And; + break; + default: + throw new ArgumentOutOfRangeException(nameof(ops), ops); + break; + } + } + } +} diff --git a/IOTManagment/Services/ICommunicationService.cs b/IOTManagment/Services/ICommunicationService.cs new file mode 100644 index 0000000..8eb8f15 --- /dev/null +++ b/IOTManagment/Services/ICommunicationService.cs @@ -0,0 +1,14 @@ +using System; +using System.Net; +using Model.Messages; + +namespace Services +{ + public interface ICommunicationService + { + + void SendMessage(IPAddress ip, Message outgoing); + void ReceiveMessage(Message incoming); + } +} + diff --git a/IOTManagment/Services/ITopologyManager.cs b/IOTManagment/Services/ITopologyManager.cs new file mode 100644 index 0000000..cac349a --- /dev/null +++ b/IOTManagment/Services/ITopologyManager.cs @@ -0,0 +1,29 @@ +using Model.Nodes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace Services +{ + public interface ITopologyManager + { + /* + ˄ 0 + | / | \ + | / | \ + | 0 0 0 Communication needs to go both ways (missing child atm) How should the proper data-structure look/build like. + | / \ / \ / \ + | 0 0 0 0 0 0 + ˅ + */ + + //TODO: Maybe change the Key-type from object to something comparable? + Dictionary IPAdresses { get; } + public void AddNode(IPEndPoint nodeAdd, INode Node); + + public void UpdateNode(IPEndPoint nodeAdd, INode node); + } +} diff --git a/IOTManagment/Services/QueryParser.cs b/IOTManagment/Services/QueryParser.cs new file mode 100644 index 0000000..382f577 --- /dev/null +++ b/IOTManagment/Services/QueryParser.cs @@ -0,0 +1,40 @@ +using Model.Queries; +using Services.Helpers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Services +{ + public class QueryParser + { + SelectParseHelper SelectParser; + IntervalParseHelper IntervalParser; + WhereParseHelper WhereParser; + public QueryParser() + { + SelectParser = new SelectParseHelper(); + IntervalParser = new IntervalParseHelper(); + WhereParser = new WhereParseHelper(); + } + public Query ParserQuery(string query) + { + // TODO: ERROR HANDLING + // split the different statement parts + string[] statmenets = query.ToLower().Split(new []{"select","interval","where"},StringSplitOptions.TrimEntries); + + var selectStatement = SelectParser.ParseSelect(statmenets[1]); + var intervalStatement = IntervalParser.ParseInterval(statmenets[2]); + var whereStatemennt = WhereParser.ParseWhere(statmenets[3]); + + return new Query + { + SelectStatement = selectStatement, + IntervalStatement = intervalStatement, + WhereStatement = whereStatemennt + }; + } + } +} diff --git a/IOTManagment/Services/Services.csproj b/IOTManagment/Services/Services.csproj index 132c02c..bf5de91 100644 --- a/IOTManagment/Services/Services.csproj +++ b/IOTManagment/Services/Services.csproj @@ -1,4 +1,4 @@ - + net6.0 @@ -6,4 +6,14 @@ enable + + + + + + + + + + diff --git a/IOTManagment/Services/TopologyManager.cs b/IOTManagment/Services/TopologyManager.cs new file mode 100644 index 0000000..39c139e --- /dev/null +++ b/IOTManagment/Services/TopologyManager.cs @@ -0,0 +1,42 @@ +using Model.Nodes; +using System; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace Services +{ + public class TopologyManager : ITopologyManager + { + + public Dictionary IPAdresses { get;} + + public TopologyManager() { + IPAdresses = new Dictionary(); + } + + public void AddNode(IPEndPoint nodeAdd, INode node) + { + IPAdresses.Add(nodeAdd, node); + } + + public void UpdateNode(IPEndPoint nodeAdd, INode node) + { + IPAdresses[nodeAdd] = node; + } + + public Dictionary GetIPAdresses() + { + return IPAdresses; + } + + public INode GetNodeByIP(IPEndPoint ip) { + if (!IPAdresses.ContainsKey(ip)) + { + return null; + } + return IPAdresses[ip]; + } + } +}