Skip to content

Commit

Permalink
refactor car clients
Browse files Browse the repository at this point in the history
  • Loading branch information
Grimmgockel committed May 22, 2023
1 parent acbbca2 commit 362eb9a
Show file tree
Hide file tree
Showing 14 changed files with 591 additions and 146 deletions.
15 changes: 13 additions & 2 deletions src/Solution1/ConsoleApp1/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,29 @@
var physicalWorld = new PhysicalWorld(graph, parkerCount, cruiserCount);

// sim meta data
var simDataTask = await SimDataClient.Create(mqttClientFactory, physicalWorld, simDataPublishInterval);
//var simDataTask = await SimDataClient.Create(mqttClientFactory, physicalWorld, simDataPublishInterval);

ParkingGuidanceSystem pgs = new ParkingGuidanceSystem(physicalWorld, new NearestParkingStrategy(), false);
// init cruisers
/*
var cruiserClients = Enumerable.Range(0, cruiserCount)
.Select(i => CruiserClient.Create(mqttClientFactory, i, physicalWorld, false));
var cruisers = await Task.WhenAll(cruiserClients);
*/
var cruiserClients = Enumerable.Range(0, cruiserCount)
.Select(i => CarClient.Create(mqttClientFactory, new CruiserClientBehaviour(), physicalWorld, pgs, i, false));
var cruisers = await Task.WhenAll(cruiserClients);

var parkerClients = Enumerable.Range(0, parkerCount)
.Select(i => CarClient.Create(mqttClientFactory, new RandomParkerClientBehaviour(), physicalWorld, pgs, i, true));
var parkers = await Task.WhenAll(parkerClients);

// init parkers
ParkingGuidanceSystem pgs = new ParkingGuidanceSystem(physicalWorld, new NearestParkingStrategy(), false);
/*
var parkerClients = Enumerable.Range(0, parkerCount)
.Select(i => ParkerClient.Create(mqttClientFactory, i, physicalWorld, pgs, true, true));
var parkers = await Task.WhenAll(parkerClients);
*/

// cancel with 'q'
while (Console.ReadKey().Key != ConsoleKey.Q)
Expand Down
209 changes: 86 additions & 123 deletions src/Solution1/ConsoleApp1/clients/CarClient.cs
Original file line number Diff line number Diff line change
@@ -1,124 +1,87 @@
using ConsoleApp1.sim;
using ConsoleApp1.sim.graph;
using ConsoleApp1.util;
using MQTTnet.Client;
using QuickGraph.Algorithms;

namespace ConsoleApp1.clients;

public abstract class CarClient : BaseClient
{
protected int Id { get; }
protected PhysicalWorld PhysicalWorld { get; }
protected StreetPosition Position { get; set; }
protected IEnumerable<StreetEdge> Path { get; set; }
protected StreetNode Destination { get; set; }

protected CarClientStatus Status { get; set; }

protected bool Logging { get; set; }

public override string ToString() => $"[CAR\t{Id},\t{Status}\t]";

protected abstract void HandleDestinationReached();
protected abstract Task HandleNodeReached();
protected abstract void TurnOnNextStreetEdge();
protected abstract void UpdateDestination();

protected CarClient(IMqttClient mqttClient, PhysicalWorld physicalWorld, int id, bool logging) : base(mqttClient)
{
Id = id;
PhysicalWorld = physicalWorld;
Logging = logging;
Path = Enumerable.Empty<StreetEdge>();

// init position
Position = StreetPosition.WithRandomDistance(physicalWorld.StreetEdges.RandomElement());
Position.StreetEdge.IncrementCarCount();

// get initial dest
UpdateDestination();
}

protected void HandlePathingFailed()
{
RespawnAtRandom();
}

protected void RespawnAtRandom()
{
Position.StreetEdge.DecrementCarCount();
Position = StreetPosition.WithRandomDistance(PhysicalWorld.StreetEdges.RandomElement());
Position.StreetEdge.IncrementCarCount();
UpdateDestination();
}

protected async Task HandleDriving()
{
if (DestinationReached()) // car reached destination after driving
{
HandleDestinationReached();
}
else
{
await DriveAccordingToPath();
}
}


private bool DestinationReached()
{
return !Path.Any() && Destination == Position.StreetEdge.Target &&
Position.DistanceFromSource >= Position.StreetEdge.Length;
}

protected async Task DriveAccordingToPath()
{
if (Position.DistanceFromSource < Position.StreetEdge.Length) // driving on street
{
UpdatePosition();
}
else
{
await HandleNodeReached();
}
}

private void UpdatePosition()
{
double speed = Position.StreetEdge.CurrentMaxSpeed();
Position = new StreetPosition(Position.StreetEdge, Position.DistanceFromSource + MathUtil.KmhToMs(speed));

if (Logging)
{
Console.WriteLine($"{this}\ttick | {Position.ToString()} | dest: {Destination.Id} | car count: {Position.StreetEdge.CarCount} | driving at {speed:F2}kmh/{Position.StreetEdge.SpeedLimit:F2}kmh");
}

}

protected void UpdatePath()
{
var shortestPaths = PhysicalWorld.Graph.ShortestPathsDijkstra(
edge => 100 - edge.SpeedLimit,
Position.StreetEdge.Source);

if (shortestPaths.Invoke(Destination, out var path))
{
Path = path.ToList();
if (Logging)
{
Console.WriteLine($"{this}\tpath: {string.Join(',', Path.Select(p => p.StreetName))}");
}
}
else
{
if (Logging)
{
Console.WriteLine($"{this}\tno path found [destination: {Destination.Id}, position: {Position}, node: {Position.StreetEdge.Source.Id}]");
}
Path = Enumerable.Empty<StreetEdge>();
Status = CarClientStatus.PathingFailed;
}
}

using ConsoleApp1.pgs;
using ConsoleApp1.sim;
using ConsoleApp1.sim.graph;
using ConsoleApp1.util;
using MQTTnet.Client;

namespace ConsoleApp1.clients;

public class CarClient: BaseClient
{
protected CarClient(IMqttClient mqttClient, ICarClientBehaviour behaviour, PhysicalWorld world, ParkingGuidanceSystem pgs, int id, bool logging) : base(mqttClient)
{
Behaviour = behaviour;
var kpiManager = new KpiManager(mqttClient, Car);
Car = new MockCar(id, world, kpiManager, logging);
Pgs = pgs;

// get initial dest
Car.Status = CarStatus.Driving;
Car.KpiManager.Reset();
Behaviour.UpdateDestination(Car);
}

public ParkingGuidanceSystem Pgs { get; set; }

private ICarClientBehaviour Behaviour { get; set; }

public MockCar Car { get; set; }

/*
* Creation through factory
*/
public static async Task<CarClient> Create(MqttClientFactory clientFactory, ICarClientBehaviour behaviour,
PhysicalWorld physicalWorld, ParkingGuidanceSystem pgs, int id, bool logging)
{
var client = await clientFactory.CreateClient(builder => builder.WithTopicFilter("tickgen/tick"));
return new CarClient(client, behaviour, physicalWorld, pgs, id, logging);
}

/**
* Main state machine
*/
protected override async Task MqttClientOnApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs arg)
{
switch (Car.Status)
{
case CarStatus.PathingFailed:
Car.Status = CarStatus.Driving;
Car.RespawnAtRandom();
Behaviour.UpdateDestination(Car);
break;

case CarStatus.Driving:
if (Car.DestinationReached())
{
Car.Status = CarStatus.Parking;
}
else
{
Behaviour.DriveAlongPath(Car);
}
break;

case CarStatus.Parking:
Car.KpiManager.TicksSpentParking++;
Behaviour.SeekParkingSpot(Car);
new CruiserClientBehaviour().DriveAlongPath(Car);
if (Behaviour.AttemptLocalParking(Car))
{
Car.Status = CarStatus.Parked;
}
break;

case CarStatus.Parked:
if (!Behaviour.StayParked(Car))
{
Car.Status = CarStatus.Driving;
Behaviour.UpdateDestination(Car);
}
break;

default:
throw new InvalidOperationException($"{this}\tInvalid status: {Car.Status}");
}
}

}
123 changes: 123 additions & 0 deletions src/Solution1/ConsoleApp1/clients/CarClientDeprecated.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
using ConsoleApp1.sim;
using ConsoleApp1.sim.graph;
using ConsoleApp1.util;
using MQTTnet.Client;
using QuickGraph.Algorithms;

namespace ConsoleApp1.clients;

public abstract class CarClientDeprecated : BaseClient
{
protected int Id { get; }
protected PhysicalWorld PhysicalWorld { get; }
protected StreetPosition Position { get; set; }
protected IEnumerable<StreetEdge> Path { get; set; }
protected StreetNode Destination { get; set; }

protected CarStatus Status { get; set; }

protected bool Logging { get; set; }

public override string ToString() => $"[CAR\t{Id},\t{Status}\t]";

protected abstract void HandleDestinationReached();
protected abstract Task HandleNodeReached();
protected abstract void TurnOnNextStreetEdge();
protected abstract void UpdateDestination();

protected CarClientDeprecated(IMqttClient mqttClient, PhysicalWorld physicalWorld, int id, bool logging) : base(mqttClient)
{
Id = id;
PhysicalWorld = physicalWorld;
Logging = logging;
Path = Enumerable.Empty<StreetEdge>();

// init position
Position = StreetPosition.WithRandomDistance(physicalWorld.StreetEdges.RandomElement());
Position.StreetEdge.IncrementCarCount();

// get initial dest
UpdateDestination();
}

protected void HandlePathingFailed()
{
RespawnAtRandom();
}

protected void RespawnAtRandom()
{
Position.StreetEdge.DecrementCarCount();
Position = StreetPosition.WithRandomDistance(PhysicalWorld.StreetEdges.RandomElement());
Position.StreetEdge.IncrementCarCount();
UpdateDestination();
}

protected async Task HandleDriving()
{
if (DestinationReached()) // car reached destination after driving
{
HandleDestinationReached();
}
else
{
await DriveAccordingToPath();
}
}


private bool DestinationReached()
{
return !Path.Any() && Destination == Position.StreetEdge.Target &&
Position.DistanceFromSource >= Position.StreetEdge.Length;
}

protected async Task DriveAccordingToPath()
{
if (Position.DistanceFromSource < Position.StreetEdge.Length) // driving on street
{
UpdatePosition();
}
else
{
await HandleNodeReached();
}
}

private void UpdatePosition()
{
double speed = Position.StreetEdge.CurrentMaxSpeed();
Position = new StreetPosition(Position.StreetEdge, Position.DistanceFromSource + MathUtil.KmhToMs(speed));

if (Logging)
{
Console.WriteLine($"{this}\ttick | {Position.ToString()} | dest: {Destination.Id} | car count: {Position.StreetEdge.CarCount} | driving at {speed:F2}kmh/{Position.StreetEdge.SpeedLimit:F2}kmh");
}
}

protected void UpdatePath()
{
var shortestPaths = PhysicalWorld.Graph.ShortestPathsDijkstra(
edge => 100 - edge.SpeedLimit,
Position.StreetEdge.Source);

if (shortestPaths.Invoke(Destination, out var path))
{
Path = path.ToList();
if (Logging)
{
Console.WriteLine($"{this}\tpath: {string.Join(',', Path.Select(p => p.StreetName))}");
}
}
else
{
if (Logging)
{
Console.WriteLine($"{this}\tno path found [destination: {Destination.Id}, position: {Position}, node: {Position.StreetEdge.Source.Id}]");
}
Path = Enumerable.Empty<StreetEdge>();
Status = CarStatus.PathingFailed;
}
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace ConsoleApp1.clients;

public enum CarClientStatus
public enum CarStatus
{
Driving,
Parking,
Expand Down
Loading

0 comments on commit 362eb9a

Please sign in to comment.