Created
May 31, 2016 07:26
-
-
Save tomliversidge/e421e3c11156068ca1869b03487f9649 to your computer and use it in GitHub Desktop.
Tracker Manager with generic candidates
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Net; | |
using System.Net.Configuration; | |
using System.Runtime.CompilerServices; | |
using System.Runtime.InteropServices; | |
using System.Security.Cryptography; | |
using System.Text; | |
using System.Threading.Tasks; | |
using Akka.Actor; | |
namespace AkkaTrackerManager | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
var system = ActorSystem.Create("tracker"); | |
var tm = system.ActorOf<TrackerManager>(); | |
Task.Run(async () => | |
{ | |
tm.Tell(new GPSSignal()); | |
tm.Tell(new GPSSignal()); | |
// alow some time for .NET Fiddle to flush | |
await Task.Delay(500); | |
}).Wait(); | |
Console.ReadLine(); | |
} | |
} | |
public class TrackerManager : ReceiveActor | |
{ | |
private IActorRef _gpsTracker; | |
public TrackerManager() | |
{ | |
_gpsTracker = Context.ActorOf(Props.Create<GPSTracker>(), "gpstracker"); | |
Become(Unknown); | |
} | |
private void Unknown() | |
{ | |
Receive<GPSSignal>(msg => | |
{ | |
Console.WriteLine("Forwarding gps to gps tracker"); | |
_gpsTracker.Tell(msg); | |
}); | |
Receive<Candidate<Entry>>(candidate => | |
{ | |
Console.WriteLine("Received entry candidate"); | |
if (Accept(candidate.Content)) | |
{ | |
Console.WriteLine("Accepted entry candidate"); | |
Sender.Tell(candidate.Accept()); | |
Become(Somewhere); // or some other state | |
} | |
else | |
{ | |
Console.WriteLine("Rejected entry candidate"); | |
Sender.Tell(candidate.Reject()); | |
} | |
}); | |
} | |
private void Somewhere() | |
{ | |
Receive<GPSSignal>(msg => | |
{ | |
_gpsTracker.Tell(msg); | |
}); | |
Receive<Candidate<Exit>>(candidate => | |
{ | |
if (Accept(candidate.Content)) | |
{ | |
Sender.Tell(candidate.Accept()); | |
Become(Unknown); // or some other state | |
} | |
else | |
{ | |
Sender.Tell(candidate.Reject()); | |
} | |
}); | |
} | |
private bool Accept(Exit msg) | |
{ | |
// some logic to determine whether or not to accept the candidate | |
return false; | |
} | |
private bool Accept(Entry msg) | |
{ | |
// some logic to determine whether or not to accept the candidate | |
return true; | |
} | |
} | |
public class GPSTracker : ReceiveActor, IWithUnboundedStash | |
{ | |
public IStash Stash { get; set; } | |
public GPSTracker() | |
{ | |
Become(Unknown); | |
} | |
private void Unknown() | |
{ | |
Receive<GPSSignal>(msg => | |
{ | |
// pretend GPS enters somewhere | |
// Sender would be TrackerManager | |
var candidate = new Candidate<Entry>(new Entry(), Somewhere); | |
Sender.Tell(candidate); | |
Console.WriteLine("Awaiting entry confirmation"); | |
BecomeStacked(AwaitingDecision); | |
}); | |
} | |
private void Somewhere() | |
{ | |
Receive<GPSSignal>(msg => | |
{ | |
// pretend GPS exits somewhere | |
// Sender would be TrackerManager | |
Console.WriteLine("Received GPS in somewhere state"); | |
var candidate = new CandidateBuilder<Exit>() | |
.From(new Exit()) | |
.IfAcceptedBecome(Unknown) | |
.Build(); | |
Sender.Tell(candidate); | |
Console.WriteLine("Exiting, awaiting confirmation"); | |
BecomeStacked(AwaitingDecision); | |
}); | |
} | |
private void AwaitingDecision() | |
{ | |
Receive<GPSSignal>(msg => | |
{ | |
Stash.Stash(); // store until we receive confirmation | |
}); | |
Receive<Decision> (decision => | |
{ | |
Console.WriteLine($"GPS Tracker - Confirmation received, candidate accepted: {decision.Accepted}"); | |
Stash.UnstashAll(); | |
if (decision.Accepted) | |
{ | |
Become(decision.NewBehaviour); | |
} | |
else | |
{ | |
UnbecomeStacked(); // rollback | |
} | |
}); | |
} | |
} | |
public class Candidate<T> | |
{ | |
public T Content { get; } | |
private readonly Action _ifAccepted; | |
public Candidate(T content, Action ifAccepted) | |
{ | |
Content = content; | |
_ifAccepted = ifAccepted; | |
} | |
public Decision Accept() | |
{ | |
return Decision.Accept(_ifAccepted); | |
} | |
public Decision Reject() | |
{ | |
return Decision.Reject(); | |
} | |
} | |
public class CandidateBuilder<T> | |
{ | |
private T _content; | |
private Action _action; | |
public CandidateBuilder<T> From(T content) | |
{ | |
_content = content; | |
return this; | |
} | |
public CandidateBuilder<T> IfAcceptedBecome(Action action) | |
{ | |
_action = action; | |
return this; | |
} | |
public Candidate<T> Build() | |
{ | |
return new Candidate<T>(_content, _action); | |
} | |
} | |
public class Decision | |
{ | |
public static Decision Accept(Action result) => new Decision(true, result); | |
public static Decision Reject() => new Decision(false); | |
private Decision(bool accepted, Action newBehaviour = null) | |
{ | |
Accepted = accepted; | |
NewBehaviour = newBehaviour; | |
} | |
public Action NewBehaviour { get; } | |
public bool Accepted { get; } | |
} | |
internal class GPSSignal{} | |
internal class Entry{} | |
internal class Exit { } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment