public ControlTowerActor()
        {
            // CosmosDb methods
            var cdb = new CosmosDB();
            // get info about the flight
            var icao = Context.ActorOf <ICAOLookupActor>();

            // register actor type as a sharded entity
            region = ClusterSharding.Get(Context.System).Start(
                typeName: "FlightActor",
                entityProps: Props.Create <FlightActor>(),
                settings: ClusterShardingSettings.Create(Context.System),
                messageExtractor: new MessageExtractor());

            // get a set of data readings
            Receive <DeviceReading>(r =>
            {
                foreach (var a in r.aircraft.Where(z => !string.IsNullOrWhiteSpace(z.flight)))
                {
                    if (!initedActors.ContainsKey(a.flight))
                    {
                        initedActors.Add(a.flight, DateTime.Now);
                        var cos = Context.ActorOf(CosmosSaveActor.Props(cdb));
                        region.Tell(new ShardEnvelope(shardId: "1", entityId: a.flight, message: new FlightActor.FlightActorInit(cos, a.flight, icao)));
                    }
                    // create message for flight actor
                    var req = new FlightActor.FlightDataRequest()
                    {
                        deviceId   = r.deviceId,
                        flightData = a,
                        now        = r.now
                    };

                    // send message to entity through shard region
                    region.Tell(new ShardEnvelope(shardId: "1", entityId: a.flight, message: req));
                    initedActors[a.flight] = DateTime.Now;
                }
            });
        }
        public SubCoordinatorActor()
        {
            // CosmosDb methods
            var cdb = new CosmosDB();
            // get info about the flight
            var icao = Context.ActorOf <ICAOLookupActor>();

            // flight code => Actor
            Dictionary <string, IActorRef> flightActors = new Dictionary <string, IActorRef>();
            // flight code => last message processed
            Dictionary <string, DateTime> flightExpiry = new Dictionary <string, DateTime>();

            // clear out old actors
            Context.System.Scheduler.ScheduleTellRepeatedly(TimeSpan.FromMinutes(15), TimeSpan.FromMinutes(15), Self, new ExpireActors(), Self);

            // group of readings
            Receive <DeviceReading>(r =>
            {
                // foreach plane in current reading -- ignoring those w/o flight code
                foreach (var dr in r.aircraft.Where(z => !string.IsNullOrWhiteSpace(z.flight)))
                {
                    // create message for flight actor
                    var req = new FlightActor.FlightDataRequest()
                    {
                        deviceId   = r.deviceId,
                        flightData = dr,
                        now        = r.now
                    };

                    // if not started up, then do so now
                    if (!flightActors.ContainsKey(dr.flight))
                    {
                        var cos = Context.ActorOf(CosmosSaveActor.Props(cdb));
                        // flightActors.Add(dr.flight, Context.ActorOf(FlightActor.Props(cos, dr.flight, icao)));

                        flightActors.Add(dr.flight, Context.ActorOf(FlightActor.Props()));
                        flightActors[dr.flight].Tell(new FlightActor.FlightActorInit(cos, dr.flight, icao));
                        flightExpiry.Add(dr.flight, DateTime.Now);
                    }

                    // send message
                    flightActors[dr.flight].Tell(req);
                    // update timestamp
                    flightExpiry[dr.flight] = DateTime.Now;
                }
            });

            // clear out actors that haven't recently processed a message
            Receive <ExpireActors>(r =>
            {
                // get idle processor list
                var toCleanup = flightExpiry
                                .Where(z => z.Value.AddHours(1) < DateTime.Now)
                                .Select(z => z.Key).ToList();
                foreach (var t in toCleanup)
                {
                    // shut down the actor
                    var actor = flightActors[t];
                    actor.GracefulStop(TimeSpan.FromSeconds(10));
                    // clear out from lists
                    flightActors.Remove(t);
                    flightExpiry.Remove(t);
                }
            });
        }