From 43b3debe4b347dc0863f2f74e75224425b6af511 Mon Sep 17 00:00:00 2001 From: vjirovsky-msft Date: Mon, 14 Nov 2022 16:53:20 +0100 Subject: [PATCH 1/2] sim upgraded to .NET6, added ASCII GUI to sim --- .../Resources/Simulation/CameraSimulation.cs | 49 ++- .../Simulation/Events/VehicleRegistered.cs | 4 + .../Student/Resources/Simulation/Program.cs | 36 +- .../Proxies/HttpTrafficControlService.cs | 2 + .../Resources/Simulation/Simulation.csproj | 7 +- .../Simulation/UIRenderingService.cs | 323 ++++++++++++++++++ 6 files changed, 413 insertions(+), 8 deletions(-) create mode 100644 047-TrafficControlWithDapr/Student/Resources/Simulation/UIRenderingService.cs diff --git a/047-TrafficControlWithDapr/Student/Resources/Simulation/CameraSimulation.cs b/047-TrafficControlWithDapr/Student/Resources/Simulation/CameraSimulation.cs index 21ad8ae260..20a5541ea2 100644 --- a/047-TrafficControlWithDapr/Student/Resources/Simulation/CameraSimulation.cs +++ b/047-TrafficControlWithDapr/Student/Resources/Simulation/CameraSimulation.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Net.Http; using System.Net.Http.Json; using System.Text.Json; @@ -11,6 +12,7 @@ namespace Simulation public class CameraSimulation { private readonly ITrafficControlService _trafficControlService; + private readonly UiRenderingService _uiRenderingService; private Random _rnd; private int _camNumber; private int _minEntryDelayInMS = 50; @@ -18,10 +20,11 @@ public class CameraSimulation private int _minExitDelayInS = 4; private int _maxExitDelayInS = 10; - public CameraSimulation(int camNumber, ITrafficControlService trafficControlService) + public CameraSimulation(int camNumber, ITrafficControlService trafficControlService, UiRenderingService uiRenderingService) { _camNumber = camNumber; _trafficControlService = trafficControlService; + _uiRenderingService = uiRenderingService; } public void Start() @@ -47,10 +50,15 @@ public void Start() { Lane = _camNumber, LicenseNumber = GenerateRandomLicenseNumber(), - Timestamp = entryTimestamp + Timestamp = entryTimestamp, + BackgroundColor = GenerateRandomColor() + }; _trafficControlService.SendVehicleEntry(vehicleRegistered); - Console.WriteLine($"Simulated ENTRY of vehicle with license-number {vehicleRegistered.LicenseNumber} in lane {vehicleRegistered.Lane}"); + Debug.WriteLine($"Simulated ENTRY of vehicle with license-number {vehicleRegistered.LicenseNumber} in lane {vehicleRegistered.Lane}"); + _uiRenderingService.AddCarToBeAnimatedInWindow(0, _camNumber, vehicleRegistered.LicenseNumber, "#FFFFFF", vehicleRegistered.BackgroundColor); + + // simulate exit TimeSpan exitDelay = TimeSpan.FromSeconds(_rnd.Next(_minExitDelayInS, _maxExitDelayInS) + _rnd.NextDouble()); @@ -58,7 +66,8 @@ public void Start() vehicleRegistered.Timestamp = DateTime.Now; vehicleRegistered.Lane = _rnd.Next(1, 4); _trafficControlService.SendVehicleExit(vehicleRegistered); - Console.WriteLine($"Simulated EXIT of vehicle with license-number {vehicleRegistered.LicenseNumber} in lane {vehicleRegistered.Lane}"); + Debug.WriteLine($"Simulated EXIT of vehicle with license-number {vehicleRegistered.LicenseNumber} in lane {vehicleRegistered.Lane}"); + _uiRenderingService.AddCarToBeAnimatedInWindow(1, _camNumber, vehicleRegistered.LicenseNumber, "#FFFFFF", vehicleRegistered.BackgroundColor); }); } catch (Exception ex) @@ -107,6 +116,38 @@ private string GenerateRandomLicenseNumber() return kenteken; } + + private string GenerateRandomColor() + { + int type = _rnd.Next(1, 9); + string mycolor = null; + switch (type) + { + default: + case 1: // silver + mycolor = "#A5BCB6"; + break; + case 2: // blue + mycolor = "#1524EC"; + break; + case 3: // yellow + mycolor = "#ffe599"; + break; + case 4: // white + mycolor = "#FFFFFF"; + break; + case 5: // red + mycolor = "#ea9999"; + break; + case 6: // green #2 + mycolor = "#d9ead3"; + break; + } + + return mycolor; + } + + private string GenerateRandomCharacters(int aantal) { char[] chars = new char[aantal]; diff --git a/047-TrafficControlWithDapr/Student/Resources/Simulation/Events/VehicleRegistered.cs b/047-TrafficControlWithDapr/Student/Resources/Simulation/Events/VehicleRegistered.cs index f45c14b349..5a5573cb2c 100644 --- a/047-TrafficControlWithDapr/Student/Resources/Simulation/Events/VehicleRegistered.cs +++ b/047-TrafficControlWithDapr/Student/Resources/Simulation/Events/VehicleRegistered.cs @@ -1,4 +1,5 @@ using System; +using System.Text.Json.Serialization; namespace Simulation.Events { @@ -7,5 +8,8 @@ public class VehicleRegistered public int Lane { get; set; } public string LicenseNumber { get; set; } public DateTime Timestamp { get; set; } + + [JsonIgnore] + public string BackgroundColor { get; set; } } } \ No newline at end of file diff --git a/047-TrafficControlWithDapr/Student/Resources/Simulation/Program.cs b/047-TrafficControlWithDapr/Student/Resources/Simulation/Program.cs index d32144e595..cac784f6c3 100644 --- a/047-TrafficControlWithDapr/Student/Resources/Simulation/Program.cs +++ b/047-TrafficControlWithDapr/Student/Resources/Simulation/Program.cs @@ -1,7 +1,10 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Pastel; using Simulation.Proxies; namespace Simulation @@ -12,12 +15,43 @@ static void Main(string[] args) { var httpClient = new HttpClient(); int lanes = 3; + + var rand = new Random(); + int lanesOffsetWindow1 = rand.Next(0, 5); + int lanesOffsetWindow2 = rand.Next(0, 5); + + var uiRenderingService = new UiRenderingService(Console.BufferWidth, Console.BufferHeight, lanes); + + Task.Run(() => + { + while (true) + { + uiRenderingService.RedrawScene(Console.BufferWidth - 1, Console.BufferHeight - 2, lanes, lanesOffsetWindow1, lanesOffsetWindow2); + Thread.Sleep(1000); + } + } + ); + + Task.Run(() => + { + + while (true) + { + uiRenderingService.MoveAllCarsForWindow(0); + uiRenderingService.MoveAllCarsForWindow(1); + Thread.Sleep(100); + } + } + ); + + + CameraSimulation[] cameras = new CameraSimulation[lanes]; for (var i = 0; i < lanes; i++) { int camNumber = i + 1; var trafficControlService = new HttpTrafficControlService(httpClient); - cameras[i] = new CameraSimulation(camNumber, trafficControlService); + cameras[i] = new CameraSimulation(camNumber, trafficControlService, uiRenderingService); } Parallel.ForEach(cameras, cam => cam.Start()); diff --git a/047-TrafficControlWithDapr/Student/Resources/Simulation/Proxies/HttpTrafficControlService.cs b/047-TrafficControlWithDapr/Student/Resources/Simulation/Proxies/HttpTrafficControlService.cs index e9bc694d35..81ea3ef261 100644 --- a/047-TrafficControlWithDapr/Student/Resources/Simulation/Proxies/HttpTrafficControlService.cs +++ b/047-TrafficControlWithDapr/Student/Resources/Simulation/Proxies/HttpTrafficControlService.cs @@ -1,3 +1,5 @@ +using System; +using System.Diagnostics; using System.Net.Http; using System.Net.Http.Json; using System.Text.Json; diff --git a/047-TrafficControlWithDapr/Student/Resources/Simulation/Simulation.csproj b/047-TrafficControlWithDapr/Student/Resources/Simulation/Simulation.csproj index 680291a4cf..d30274b5aa 100644 --- a/047-TrafficControlWithDapr/Student/Resources/Simulation/Simulation.csproj +++ b/047-TrafficControlWithDapr/Student/Resources/Simulation/Simulation.csproj @@ -1,8 +1,9 @@ - Exe - netcoreapp5.0 + net6.0 - + + + diff --git a/047-TrafficControlWithDapr/Student/Resources/Simulation/UIRenderingService.cs b/047-TrafficControlWithDapr/Student/Resources/Simulation/UIRenderingService.cs new file mode 100644 index 0000000000..dac407771b --- /dev/null +++ b/047-TrafficControlWithDapr/Student/Resources/Simulation/UIRenderingService.cs @@ -0,0 +1,323 @@ +using Pastel; +using Simulation.Events; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Simulation +{ + public class UiRenderingService + { + private static ConcurrentDictionary> sceneMatrix = new ConcurrentDictionary>(); + + private const bool isDebugMatrixHighlighted = false; + + private const string DEFAULT_BACKGROUND = "#000000"; + private const string ROAD_COLOR = "#575757"; + private const string GRAY_COLOR = "#666666"; + private const string BLACK_COLOR = "#000000"; + private const string WHITE_COLOR = "#FFFFFF"; + private const string YELLOW_COLOR = "#FFFF00"; + private const string GREEN_COLOR = "#069420"; + + private int _sceneWidth; + private int _sceneHeight; + + private int _lanesCount; + + private Dictionary _windowStartX = new Dictionary(); + private Dictionary _windowSize = new Dictionary(); + + + private ConcurrentDictionary> _windowCars = new ConcurrentDictionary>(); + + public UiRenderingService(int sceneWidth, int sceneHeight, int lanesCount) + { + _sceneWidth = sceneWidth; + _sceneHeight = sceneHeight; + _lanesCount = lanesCount; + } + + /// returns last x position of the string's char + public int SaveStringIntoMatrix(int y, int startX, string input, string foregroundColor, string backgroundColor, int ignoreLowerThanX = 0, int ignoreHigherThanX = 99999, int ignoreLowerThanY = 0, int ignoreHigherThanY = 99999) + { + var chars = input.ToCharArray(); + int x = startX; + foreach (var myChar in chars) + { + if (x > ignoreHigherThanX || x < ignoreLowerThanX || y < ignoreLowerThanY || y > ignoreHigherThanY) + { + x++; + continue; + } + if (!sceneMatrix.ContainsKey(y)) + { + continue; + } + + sceneMatrix[y][x] = myChar.ToString(); + if (!string.IsNullOrEmpty(foregroundColor)) + { + sceneMatrix[y][x] = sceneMatrix[y][x].Pastel(foregroundColor); + } + + if (!string.IsNullOrEmpty(backgroundColor)) + { + sceneMatrix[y][x] = sceneMatrix[y][x].PastelBg(backgroundColor); + } + + x++; + } + + return x; + } + + + private int RedrawSubWindow(int windowId, int startXPosition, int lanesOffset = 0) + { + _windowStartX[windowId] = startXPosition; + + //heading + int currentY = 6; + string tempText = $"▼ Camera {windowId + 1} ▼"; + int windowStartXPosition = startXPosition; + int windowTitlePaddingLength = (_sceneWidth / 4) - (tempText.Length / 2) - 5; + startXPosition = SaveStringIntoMatrix(currentY, startXPosition, "".PadRight(windowTitlePaddingLength), "#000000", GRAY_COLOR); + + startXPosition = SaveStringIntoMatrix(currentY, startXPosition, tempText, BLACK_COLOR, GRAY_COLOR); + startXPosition = SaveStringIntoMatrix(currentY, startXPosition, "".PadRight(windowTitlePaddingLength), "#000000", GRAY_COLOR); + + int windowSize = startXPosition - windowStartXPosition; + + currentY++; + + //grass + SaveStringIntoMatrix(currentY, windowStartXPosition, "".PadRight(windowSize), GREEN_COLOR, GREEN_COLOR); + currentY++; + + SaveStringIntoMatrix(currentY, windowStartXPosition, "".PadRight(windowSize), ROAD_COLOR, ROAD_COLOR); + currentY++; + + //highway divider + SaveStringIntoMatrix(currentY, windowStartXPosition, "".PadRight(windowSize), YELLOW_COLOR, YELLOW_COLOR); + currentY++; + + //lanes + + int lanesStartY = currentY; + + for (int i = 0; i < _lanesCount; i++) + { + SaveStringIntoMatrix(currentY, windowStartXPosition, "".PadRight(windowSize), BLACK_COLOR, ROAD_COLOR); + currentY++; + SaveStringIntoMatrix(currentY, windowStartXPosition, "".PadRight(windowSize), BLACK_COLOR, ROAD_COLOR); + currentY++; + SaveStringIntoMatrix(currentY, windowStartXPosition, "".PadRight(windowSize), BLACK_COLOR, ROAD_COLOR); + currentY++; + + + if (i < _lanesCount - 1) + { + //lane dividers + int currentX = windowStartXPosition - lanesOffset; + + for (int j = 0; j < (windowSize / 11) + 1; j++) + { + SaveStringIntoMatrix(currentY, currentX, "".PadRight(6), BLACK_COLOR, WHITE_COLOR, windowStartXPosition, (windowStartXPosition + windowSize - 1)); + currentX += 6; + SaveStringIntoMatrix(currentY, currentX, "".PadRight(5), BLACK_COLOR, ROAD_COLOR, windowStartXPosition, (windowStartXPosition + windowSize - 1)); + currentX += 5; + } + + currentY++; + } + } + + + //highway divider + SaveStringIntoMatrix(currentY, windowStartXPosition, "".PadRight(windowSize), YELLOW_COLOR, YELLOW_COLOR); + currentY++; + + SaveStringIntoMatrix(currentY, windowStartXPosition, "".PadRight(windowSize), ROAD_COLOR, ROAD_COLOR); + currentY++; + + //grass + SaveStringIntoMatrix(currentY, windowStartXPosition, "".PadRight(windowSize), GREEN_COLOR, GREEN_COLOR); + currentY++; + + _windowSize[windowId] = windowSize; + + + //draw cars + + if (_windowCars.ContainsKey(windowId)) + { + Parallel.For(1, _lanesCount + 1, (laneId, state) => + { + int currentFreeLanePosition = windowStartXPosition; + foreach (var car in _windowCars[windowId].Where(c => c.Value.Lane.Equals(laneId)).OrderBy(c => c.Value.PositionOnCameraImage)) + { + + int carXPosition = car.Value.PositionOnCameraImage + currentFreeLanePosition; + int carYPosition = 10 + (car.Value.Lane - 1) * 4; + + SaveStringIntoMatrix(carYPosition, carXPosition, "┌──────────┐", BLACK_COLOR, car.Value.BackgroundColor, _windowStartX[windowId], (_windowStartX[windowId] + _windowSize[windowId] - 1)); + SaveStringIntoMatrix(carYPosition + 1, carXPosition, $"| {car.Value.LicenseNumber} |", BLACK_COLOR, car.Value.BackgroundColor, _windowStartX[windowId], (_windowStartX[windowId] + _windowSize[windowId] - 1)); + SaveStringIntoMatrix(carYPosition + 2, carXPosition, "└──────────┘", BLACK_COLOR, car.Value.BackgroundColor, _windowStartX[windowId], (_windowStartX[windowId] + _windowSize[windowId] - 1)); + + currentFreeLanePosition += 20; + } + }); + } + + return windowSize; + } + + public void AddCarToBeAnimatedInWindow(int windowId, int laneId, string licenseNumber, string foregroundColor, string backgroundColor) + { + //if(!_windowsStartX.ContainsKey(windowId)) + // return; + if (!_windowCars.ContainsKey(windowId)) + { + _windowCars.TryAdd(windowId, new ConcurrentDictionary()); + } + + _windowCars[windowId].TryAdd(licenseNumber, new CarToBeRendered() + { + Lane = laneId, + LicenseNumber = licenseNumber, + PositionOnCameraImage = 0, + BackgroundColor = backgroundColor, + + }); + } + + public void RedrawScene(int sceneWidth, int sceneHeight, int lanesCount, int lanes1Offset = 0, int lanes2Offset = 0) + { + + if (sceneWidth < 70) + { + Console.Clear(); + Console.WriteLine("Please resize the window to 70 cols width at minimum."); + return; + } + + if (sceneHeight < 22) + { + Console.Clear(); + Console.WriteLine("Please resize the window to 22 lines height at minimum."); + return; + } + + _sceneWidth = sceneWidth; + _sceneHeight = sceneHeight; + + string tempText; + int startXPosition; + + for (int y = 0; y < sceneHeight; y++) + { + sceneMatrix[y] = new Dictionary(); + } + + + // header + tempText = " __________________________________________________________________ "; + SaveStringIntoMatrix(0, (sceneWidth / 2) - (tempText.Length / 2), tempText, GRAY_COLOR, DEFAULT_BACKGROUND); + + tempText = "| |"; + SaveStringIntoMatrix(1, (sceneWidth / 2) - (tempText.Length / 2), tempText, GRAY_COLOR, DEFAULT_BACKGROUND); + + tempText = "| What The Hack - Traffic Control with Dapr - Traffic simulator |"; + SaveStringIntoMatrix(2, (sceneWidth / 2) - (tempText.Length / 2), tempText, GRAY_COLOR, DEFAULT_BACKGROUND); + + tempText = "|__________________________________________________________________|"; + SaveStringIntoMatrix(3, (sceneWidth / 2) - (tempText.Length / 2), tempText, GRAY_COLOR, DEFAULT_BACKGROUND); + + + // window #0 + int windowSize = RedrawSubWindow(0, 1, lanes1Offset); + + + // window #1 + RedrawSubWindow(1, (sceneWidth - windowSize - 1), lanes2Offset); + + + // windows separator + for (int my = 6; my < sceneHeight; my++) + { + startXPosition = SaveStringIntoMatrix(my, (sceneWidth / 2) - 1, " ", GRAY_COLOR, GRAY_COLOR); + } + + + // Final scene render + string scene = ""; + for (int y = 0; y < sceneHeight; y++) + { + + for (int x = 0; x < sceneWidth; x++) + { + if (sceneMatrix.ContainsKey(y) && sceneMatrix[y].ContainsKey(x)) + { + scene += sceneMatrix[y][x]; + } + else + { + if (isDebugMatrixHighlighted) + { + scene += " ".PastelBg("#FFFF00"); + } + else + { + scene += " "; + } + } + } + + scene += "\n"; + + } + + Console.Clear(); + Console.Write(scene); + + } + + + + public void MoveAllCarsForWindow(int windowId) + { + if (!_windowCars.ContainsKey(windowId)) { + return; + } + + Parallel.For(1, _lanesCount + 1, (laneId, state) => + { + foreach (var car in _windowCars[windowId].Where(c => c.Value.Lane.Equals(laneId)).OrderBy(c => c.Value.PositionOnCameraImage)) + { + car.Value.PositionOnCameraImage += 1; + + if (car.Value.PositionOnCameraImage > 30) + { + CarToBeRendered carToBeDeleted; + _windowCars[windowId].Remove(car.Key, out carToBeDeleted); + + } + } + }); + } + + } + + + public class CarToBeRendered + { + public int Lane { get; set; } + public string LicenseNumber { get; set; } + public int PositionOnCameraImage { get; set; } + public string BackgroundColor { get; set; } + } +} \ No newline at end of file From 88136183393add347a5b07c72d4a59208b3224b7 Mon Sep 17 00:00:00 2001 From: vjirovsky-msft Date: Mon, 14 Nov 2022 16:58:53 +0100 Subject: [PATCH 2/2] updated challenge-00 + README (.NET version) --- 047-TrafficControlWithDapr/README.md | 1 + 047-TrafficControlWithDapr/Student/Challenge-00.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/047-TrafficControlWithDapr/README.md b/047-TrafficControlWithDapr/README.md index a0c7012f33..0a6bac2546 100644 --- a/047-TrafficControlWithDapr/README.md +++ b/047-TrafficControlWithDapr/README.md @@ -71,3 +71,4 @@ When the car passes an exit-camera, another photo and timestamp are registered. - Rob Vettor - Edwin van Wijk - Chandrasekar B +- Vaclav Jirovsky diff --git a/047-TrafficControlWithDapr/Student/Challenge-00.md b/047-TrafficControlWithDapr/Student/Challenge-00.md index b48aab77ca..6cdab85605 100644 --- a/047-TrafficControlWithDapr/Student/Challenge-00.md +++ b/047-TrafficControlWithDapr/Student/Challenge-00.md @@ -18,7 +18,7 @@ Your coach will provide you with a `Resources.zip` package file that contains th Install all the prerequisites listed below and make sure they're working correctly: - Git ([download](https://git-scm.com/)) - - .NET 5 SDK ([download](https://dotnet.microsoft.com/download/dotnet/5.0)) + - .NET 5 SDK ([download](https://dotnet.microsoft.com/download/dotnet/5.0)), .NET 6 SDK ([download](https://dotnet.microsoft.com/download/dotnet/6.0)) - Visual Studio Code ([download](https://code.visualstudio.com/download)) with the following extensions installed: - [C#](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp) - [REST Client](https://marketplace.visualstudio.com/items?itemName=humao.rest-client)