Skip to content

Instantly share code, notes, and snippets.

@tomliversidge
Created May 31, 2016 07:26
Show Gist options
  • Save tomliversidge/e421e3c11156068ca1869b03487f9649 to your computer and use it in GitHub Desktop.
Save tomliversidge/e421e3c11156068ca1869b03487f9649 to your computer and use it in GitHub Desktop.
Tracker Manager with generic candidates
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