コード例 #1
0
        private void RegisterBarracks(EntityId barrackId)
        {
            var newBarracks = new Improbable.Collections.List <EntityId>(hqInfo.Data.barracks);

            newBarracks.Add(barrackId);
            hqInfo.Send(new HQInfo.Update().SetBarracks(newBarracks));
        }
コード例 #2
0
ファイル: GridGlobalLayer.cs プロジェクト: dtbinh/drone-sim
    private void OnEnable()
    {
        bitmap = gameObject.GetComponent <Bitmap>();
        zones  = GlobalLayerWriter.Data.zones;

        //GlobalLayerWriter.ZonesUpdated.Add(HandleZonesUpdate);
    }
コード例 #3
0
        private void OnRegisterBarracks(Improbable.Entity.Component.ResponseHandle <HQInfo.Commands.RegisterBarracks, RegisterBarracksRequest, Nothing> request)
        {
            var newBarracks = new Improbable.Collections.List <EntityId>(hqInfo.Data.barracks);

            newBarracks.Add(request.Request.entityId);
            hqInfo.Send(new HQInfo.Update().SetBarracks(newBarracks));
            request.Respond(new Nothing());
        }
コード例 #4
0
        private Nothing OnRegisterBarracks(RegisterBarracksRequest request, ICommandCallerInfo callerinfo)
        {
            var newBarracks = new Improbable.Collections.List <EntityId>(hqInfo.Data.barracks);

            newBarracks.Add(request.entityId);
            hqInfo.Send(new HQInfo.Update().SetBarracks(newBarracks));
            return(new Nothing());
        }
コード例 #5
0
    public static Improbable.Controller.NoFlyZone CreateCustomNoFlyZone(Improbable.Collections.List <Vector3f> vertices)
    {
        Improbable.Controller.NoFlyZone nfz = new Improbable.Controller.NoFlyZone();
        nfz.vertices = vertices;
        NoFlyZone.setBoundingBoxCoordinates(ref nfz);

        return(nfz);
    }
コード例 #6
0
ファイル: LondonSnapshot.cs プロジェクト: dtbinh/drone-sim
        private static int ShowNoFlyZones(Improbable.Collections.List <Improbable.Controller.NoFlyZone> noFlyZones, Dictionary <EntityId, Entity> snapshotEntities, int currentEntityId)
        {
            foreach (Improbable.Controller.NoFlyZone noFlyZone in noFlyZones)
            {
                currentEntityId = ShowNoFlyZones(noFlyZone, snapshotEntities, currentEntityId);
            }

            return(currentEntityId);
        }
コード例 #7
0
ファイル: LondonSnapshot.cs プロジェクト: dtbinh/drone-sim
 private static Improbable.Collections.List <DroneInfo> PopulateControllerSlots()
 {
     Improbable.Collections.List <DroneInfo> droneSlots = new Improbable.Collections.List <DroneInfo>((int)SimulationSettings.MaxDroneCountPerController);
     while (droneSlots.Count < droneSlots.Capacity)
     {
         droneSlots.Add(GenerateDroneInfo());
     }
     return(droneSlots);
 }
コード例 #8
0
        private void OnUnregisterBarracks(Improbable.Entity.Component.ResponseHandle <HQInfo.Commands.UnregisterBarracks, UnregisterBarracksRequest, Nothing> request)
        {
            var barracks = new Improbable.Collections.List <EntityId>(hqInfo.Data.barracks);

            if (barracks.Contains(request.Request.entityId))
            {
                barracks.Remove(request.Request.entityId);
            }
            hqInfo.Send(new HQInfo.Update().SetBarracks(barracks));
            request.Respond(new Nothing());
        }
コード例 #9
0
ファイル: NFZScript.cs プロジェクト: dtbinh/drone-sim
    public Improbable.Controller.NoFlyZone GetNoFlyZone()
    {
        Improbable.Collections.List <Vector3f> positions = new Improbable.Collections.List <Vector3f>();

        for (int i = 0; i < nfzNodes.Length; i++)
        {
            positions.Add(nfzNodes[i].transform.position.ToSpatialVector3f());
        }

        return(NFZ_Templates.CreateCustomNoFlyZone(positions));
    }
コード例 #10
0
        private Nothing OnUnregisterBarracks(UnregisterBarracksRequest request, ICommandCallerInfo callerinfo)
        {
            var barracks = new Improbable.Collections.List <EntityId>(hqInfo.Data.barracks);

            if (barracks.Contains(request.entityId))
            {
                barracks.Remove(request.entityId);
            }
            hqInfo.Send(new HQInfo.Update().SetBarracks(barracks));
            return(new Nothing());
        }
コード例 #11
0
    public static TimeValueFunction GenerateTypeB(DeliveryType deliveryType)
    {
        Improbable.Collections.List <bool> steps = new Improbable.Collections.List <bool>(SimulationSettings.TVFSteps);
        int numSteps = 2;

        for (int i = 0; i < steps.Capacity; i++)
        {
            steps.Add(i == 4 || i == 9);
        }
        return(new TimeValueFunction(steps, numSteps, deliveryType));
    }
コード例 #12
0
    public static bool hasCollidedWithAny(Improbable.Collections.List <Improbable.Controller.NoFlyZone> zones, Vector3f point)
    {
        foreach (Improbable.Controller.NoFlyZone zone in zones)
        {
            if (isInPolygon(zone, point))
            {
                return(true);
            }
        }

        return(false);
    }
コード例 #13
0
        public static WorkerRequirementSet GetWorkerRequirementSet(Type workerType, params Type[] workerTypes)
        {
            var workerAttributes = new Improbable.Collections.List <WorkerAttributeSet>();

            workerAttributes.Add(WorkerTypeToAttributeSet[workerType]);
            foreach (var nextType in workerTypes)
            {
                workerAttributes.Add(WorkerTypeToAttributeSet[nextType]);
            }

            return(new WorkerRequirementSet(workerAttributes));
        }
コード例 #14
0
ファイル: GridGlobalLayer.cs プロジェクト: dtbinh/drone-sim
    public Improbable.Collections.List <Improbable.Vector3f> generatePointToPointPlan(Improbable.Vector3f p1, Improbable.Vector3f p2)
    {
        if (isPointInNoFlyZone(p2))
        {
            Debug.LogError("next target is in a NFZ");
            return(null); // A plan can not be found
        }

        int[] coord1 = bitmap.findGridCoordinatesOfPoint(p1);
        if (coord1 == null)
        {
            Debug.LogError("coord1 fail");
            return(null);
        }

        int[] coord2 = bitmap.findGridCoordinatesOfPoint(p2);
        if (coord1 == null)
        {
            Debug.LogError("coord2 fail");
            return(null);
        }

        GridLocation l1 = new GridLocation(coord1[0], coord1[1]);
        GridLocation l2 = new GridLocation(coord2[0], coord2[1]);

        ASearch = new ThetaStarSearch(true); // Use AStarSearch or ThetaStarSearch here.

        float droneHeight        = UnityEngine.Random.Range(SimulationSettings.MinimumDroneHeight, SimulationSettings.MaximumDroneHeight);
        List <GridLocation> locs = ASearch.run(bitmap, l1, l2);

        if (locs == null)
        {                 // The case that a path could not be found.
            Debug.LogError("search fail");
            return(null); // Just return empty list.
        }

        //Debug.LogWarning("NUM VERTICES IN PATH: " + locs.Count);

        Improbable.Collections.List <Improbable.Vector3f> result = new Improbable.Collections.List <Improbable.Vector3f>();

        result.Add(p1);
        foreach (GridLocation l in locs)
        {
            Improbable.Vector3f convertedLocation = convertLocation(l);
            Improbable.Vector3f location          = new Improbable.Vector3f(convertedLocation.x, droneHeight, convertedLocation.z);

            result.Add(location);
        }
        result.Add(p2);

        return(result);
    }
コード例 #15
0
    void Scheduler.UpdateDeliveryRequestQueue()
    {
        Improbable.Collections.List <QueueEntry> queueList = new Improbable.Collections.List <QueueEntry>();
        foreach (QueueEntry entry in requestQueue)
        {
            queueList.Add(entry);
        }

        DeliveryHandlerWriter.Send(new DeliveryHandler.Update()
                                   .SetRequestQueue(queueList)
                                   .SetPotential(potential)
                                   .SetRejections(rejections)
                                   .SetRejectedValue(rejecValue));
    }
コード例 #16
0
    private void OnEnable()
    {
        deliveriesMap = ControllerWriter.Data.deliveriesMap;
        droneSlots    = ControllerWriter.Data.droneSlots;

        completedDeliveries = MetricsWriter.Data.completedDeliveries;
        completedRoundTrips = MetricsWriter.Data.completedRoundTrips;
        collisionsReported  = MetricsWriter.Data.collisionsReported;

        failedLaunches   = MetricsWriter.Data.failedLaunches;
        failedDeliveries = MetricsWriter.Data.failedDeliveries;
        failedReturns    = MetricsWriter.Data.failedReturns;
        unknownRequests  = MetricsWriter.Data.unknownRequests;

        revenue   = MetricsWriter.Data.revenue;
        costs     = MetricsWriter.Data.costs;
        penalties = MetricsWriter.Data.penalties;

        avgWaitTime     = MetricsWriter.Data.avgWaitTime;
        launches        = MetricsWriter.Data.launches;
        avgDeliveryTime = MetricsWriter.Data.avgDeliveryTime;

        for (int i = 0; i < droneSlots.Count; i++)
        {
            if (droneSlots[i].occupied)
            {
                usedSlots++;
            }
        }

        departuresPoint = transform.position.ToCoordinates() + SimulationSettings.ControllerDepartureOffset;
        arrivalsPoint   = transform.position.ToCoordinates() + SimulationSettings.ControllerArrivalOffset;

        ControllerWriter.CommandReceiver.OnRequestNewTarget.RegisterAsyncResponse(HandleTargetRequest);
        ControllerWriter.CommandReceiver.OnCollision.RegisterAsyncResponse(HandleCollision);
        ControllerWriter.CommandReceiver.OnUnlinkDrone.RegisterAsyncResponse(HandleUnlinkRequest);

        globalLayer = gameObject.GetComponent <GridGlobalLayer>();

        /* SCHEDULER CHOICE */
        scheduler = gameObject.GetComponent <FirstComeFirstServeScheduler>();
        //scheduler = gameObject.GetComponent<LeastLostValueScheduler>();
        //scheduler = gameObject.GetComponent<ShortestJobFirstScheduler>();

        UnityEngine.Random.InitState((int)gameObject.EntityId().Id);
        InvokeRepeating("ControllerTick", UnityEngine.Random.Range(0, SimulationSettings.RequestHandlerInterval), SimulationSettings.RequestHandlerInterval);
        InvokeRepeating("PrintMetrics", 0, SimulationSettings.ControllerMetricsInterval);
        InvokeRepeating("DroneMapPrune", UnityEngine.Random.Range(0, SimulationSettings.DroneMapPruningInterval), SimulationSettings.DroneMapPruningInterval);
    }
コード例 #17
0
        static ModularData GetModularComponent(DocumentSnapshot[] moduleSnapshots)
        {
            var installList = new Improbable.Collections.List <int>(moduleSnapshots.Length);

            for (int i = 0; i < moduleSnapshots.Length; i++)
            {
                moduleSnapshots[i].TryGetValue <bool>("equip", out var installed);

                if (installed)
                {
                    installList.Add(i);
                }
            }

            return(new ModularData(installList, ShipType.Starter));
        }
コード例 #18
0
ファイル: LondonSnapshot.cs プロジェクト: dtbinh/drone-sim
        private static void ProfilingSnapshot()
        {
            float maxX = 400;
            float maxZ = 400;

            var snapshotEntities = new Dictionary <EntityId, Entity>();
            var currentEntityId  = 2; //reserve id 1 for the scheduler

            Improbable.Collections.List <Improbable.Controller.NoFlyZone> noFlyZones = new Improbable.Collections.List <Improbable.Controller.NoFlyZone>();
            Improbable.Collections.List <ControllerInfo> controllers = new Improbable.Collections.List <ControllerInfo>();

            // CONTROLLERS
            int         firstController = currentEntityId;
            EntityId    controllerId    = new EntityId(currentEntityId++);
            Coordinates controllerPos   = new Coordinates(100, 0, 100);

            controllers.Add(new ControllerInfo(controllerId, controllerPos.ToSpatialVector3f()));
            snapshotEntities.Add(
                controllerId,
                EntityTemplateFactory.CreateControllerTemplate(
                    controllerPos,
                    new Vector3f(-maxX, 0, maxZ),
                    new Vector3f(maxX, 0, -maxZ),
                    noFlyZones,
                    PopulateControllerSlots()
                    ));
            int lastController = currentEntityId;
            // controller placement complete

            // ORDER GENERATOR
            // find and place order generator
            OrderGeneratorBehaviour orderGenerator = FindObjectOfType <OrderGeneratorBehaviour>();

            snapshotEntities.Add(
                SimulationSettings.OrderGeneratorEntityId,
                EntityTemplateFactory.CreateSchedulerTemplate(
                    new Vector3(0, 0, 0),
                    firstController,
                    lastController,
                    noFlyZones,
                    controllers
                    )
                );
            // end scheduler placement

            SnapshotMenu.SaveSnapshot(snapshotEntities, "profiling");
        }
コード例 #19
0
ファイル: GridGlobalLayer.cs プロジェクト: dtbinh/drone-sim
    public Improbable.Collections.List <Improbable.Vector3f> generatePlan(List <Improbable.Vector3f> waypoints)
    {
        // If can not plan for all the waypoints,
        // this will return null to indicate that the route is unachievable.

        Improbable.Collections.List <Improbable.Vector3f> result = new Improbable.Collections.List <Improbable.Vector3f>();
        for (int i = 1; i < waypoints.Count; i++)
        {
            Improbable.Collections.List <Improbable.Vector3f> planSection = generatePointToPointPlan(waypoints[i - 1], waypoints[i]);
            if (planSection == null || planSection.Count < 2)
            {
                return(null); // Return null to indicate a plan is unachievable.
            }
            result.AddRange(planSection);
        }
        return(result);
    }
コード例 #20
0
        static async Task <ModuleResources> GetModuleResources(DocumentReference moduleRef)
        {
            var resourceQuery = await moduleRef.Collection(ResourceCollection).GetSnapshotAsync();

            var resourceSnapshots = resourceQuery.Documents;

            var list = new Improbable.Collections.List <ResourceInfo>(resourceQuery.Count);

            for (int i = 0; i < resourceQuery.Count; i++)
            {
                var resourceSnapshot = resourceSnapshots[i];
                var resource         = resourceSnapshot.ConvertTo <Resource>();

                list.Add(new ResourceInfo(resourceSnapshot.Id, resource.Type, resource.Quantity));
            }

            return(new ModuleResources(list));
        }
コード例 #21
0
    private TimeValueFunction GenerateRandomTVF()
    {
        Improbable.Collections.List <bool> steps = new Improbable.Collections.List <bool>(SimulationSettings.TVFSteps);
        int numSteps = 0;

        for (int i = 0; i < steps.Capacity; i++)
        {
            if (UnityEngine.Random.Range(0f, 1f) < 0.5f)
            {
                steps.Add(true);
                ++numSteps;
            }
            else
            {
                steps.Add(false);
            }
        }

        return(new TimeValueFunction(steps, numSteps, GenerateDeliveryType()));
    }
コード例 #22
0
        const string workerType   = "life"; //Layer name not workerType?

        static int Main(string[] arguments)
        {
            Console.WriteLine(String.Format("Generating snapshot file {0}", snapshotPath));
            Assembly.Load("GeneratedCode");

            // https://docs.improbable.io/reference/13.3/csharpsdk/using/snapshots

            // Construct a SnapshotOutputStream to write a snapshot to a file at the string path.
            SnapshotOutputStream sos = new SnapshotOutputStream(snapshotPath);

            var id             = 0;
            var maxRowCount    = 50; //dimensionsInWorldUnits z is 100
            var maxColumnCount = 50; //dimensionsInWorldUnits x is 100

            // TODO: Update to account for center being at (0.0)

            //Create a grid of entities
            for (double y = 0; y < maxRowCount; y++)        //Starts at 0
            {
                for (double x = 0; x < maxColumnCount; x++) //starts at 0
                {
                    id++;                                   //starts at 1
                    var entityId = new EntityId(id);
                    Improbable.Collections.List <EntityId> nList = getNeighbors(x, maxColumnCount, y, maxRowCount, id);
                    bool currIsAlive = RandomBool();
                    bool prevIsAlive = RandomBool();
                    var  entity      = createEntity(workerType, x, y, currIsAlive, 0, prevIsAlive, 0, nList);
                    var  error       = sos.WriteEntity(entityId, entity);
                    if (error.HasValue)
                    {
                        throw new System.SystemException("error saving: " + error.Value);
                    }
                }
            }

            // Writes the end of snapshot header and releases the resources of the SnapshotOutputStream.
            sos.Dispose();

            return(0);
        }
コード例 #23
0
    void HandleDeliveryRequest(QueueEntry entry)
    {
        DeliveryRequest request = entry.request;

        DeliveryInfo deliveryInfo;
        Vector2      random;

        deliveryInfo.timestamp         = entry.timestamp;
        deliveryInfo.packageInfo       = request.packageInfo;
        deliveryInfo.timeValueFunction = request.timeValueFunction;

        deliveryInfo.slot = GetNextSlot();
        if (deliveryInfo.slot < 0)
        {
            Debug.LogError("Something's gone terribly wrong with the slot mechanics.");
        }

        random = UnityEngine.Random.insideUnitCircle * SimulationSettings.DronePadRadius;
        Vector3f departurePoint = departuresPoint.ToSpatialVector3f() + new Vector3f(random.x, 0, random.y);

        random = UnityEngine.Random.insideUnitCircle * SimulationSettings.DronePadRadius;
        Vector3f arrivalPoint = arrivalsPoint.ToSpatialVector3f() + new Vector3f(random.x, 0, random.y);

        //Debug.LogWarning("point to point plan");
        //for new flight plan
        deliveryInfo.nextWaypoint = 1;
        deliveryInfo.returning    = false;
        Improbable.Collections.List <Improbable.Vector3f> plan = globalLayer.generatePointToPointPlan(
            departurePoint,
            request.destination);

        //Debug.LogWarning("null check");
        if (plan == null || plan.Count < 2)
        {
            // let scheduler know that this job can't be done
            DroneDeploymentFailure();
            return;
        }

        deliveryInfo.waypoints = plan;

        //0th index only useful as last point in return journey
        //so make sure 0th index == last location in journey == arrivalsPoint
        deliveryInfo.waypoints[0] = arrivalPoint;
        deliveryInfo.latestCheckinTime
            = Time.time
              + (SimulationSettings.DroneETAConstant
                 * Vector3.Distance(
                     departurePoint.ToUnityVector(),
                     deliveryInfo.waypoints[1].ToUnityVector())
                 / SimulationSettings.MaxDroneSpeed);

        //create drone
        //if successful, add to droneMap
        //if failure, tell scheduler job couldn't be done
        var droneTemplate = EntityTemplateFactory.CreateDroneTemplate(
            departurePoint.ToCoordinates(),
            deliveryInfo.waypoints[deliveryInfo.nextWaypoint],
            gameObject.EntityId(),
            request.packageInfo.weight + PayloadGenerator.GetPackagingWeight(request.packageInfo.type),
            SimulationSettings.MaxDroneSpeed);

        SpatialOS.Commands.CreateEntity(PositionWriter, droneTemplate)
        .OnSuccess((response) => DroneDeploymentSuccess(response.CreatedEntityId, deliveryInfo))
        .OnFailure((response) => DroneDeploymentFailure());
    }
コード例 #24
0
        private static Improbable.Collections.List <EntityId> getNeighbors(double x, int maxColumnCount, double y, int maxRowCount, int id)
        {
            Improbable.Collections.List <EntityId> neighbors = new Improbable.Collections.List <EntityId>();

            int  maxX = maxColumnCount - 1;
            int  maxY = maxRowCount - 1;
            long nId;

            //Top-Left
            if ((x > 0) & (y < maxY))
            {
                nId = (long)((y + 1) * maxColumnCount + ((x + 1) - 1));
                neighbors.Add(new EntityId(nId));
            }

            //Top
            if (y < maxY)
            {
                nId = (long)((y + 1) * maxColumnCount + (x + 1));
                neighbors.Add(new EntityId(nId));
            }

            //Top-Right
            if ((y < maxY) & (x < maxX))
            {
                nId = (long)((y + 1) * maxColumnCount + (x + 2));
                neighbors.Add(new EntityId(nId));
            }

            //Right
            if (x < maxX)
            {
                neighbors.Add(new EntityId(id + 1));
            }

            //Bottom-Right
            if ((x < maxX) & (y > 0))
            {
                nId = (long)((y - 1) * maxColumnCount + (x + 1 + 1));
                neighbors.Add(new EntityId(nId));
            }

            //Bottom
            if (y > 0)
            {
                nId = (long)((y - 1) * maxColumnCount + (x + 1));
                neighbors.Add(new EntityId(nId));
            }

            //Bottom-Left
            if ((y > 0) & (x > 0))
            {
                nId = (long)((y - 1) * maxColumnCount + ((x + 1) - 1));
                neighbors.Add(new EntityId(nId));
            }

            //Left
            if (x > 0)
            {
                neighbors.Add(new EntityId(id - 1));
            }

            return(neighbors);
        }
コード例 #25
0
        private static Entity createEntity(string workerType, double X, double Y, bool currentAlive, UInt64 currentSequenceId, bool previousAlive, UInt64 previousSequenceId, Improbable.Collections.List <EntityId> neighborsList)
        {
            var entity = new Entity();
            //const string entityType = "Cell";
            string entityType = "Cell";

            if (currentAlive)
            {
                entityType = "Cell_Alive";
            }
            else
            {
                entityType = "Cell_Dead";
            }

            // Defines worker attribute requirements for workers that can read a component.
            // workers with an attribute of "client" OR workerType will have read access
            var readRequirementSet = new WorkerRequirementSet(
                new Improbable.Collections.List <WorkerAttributeSet>
            {
                new WorkerAttributeSet(new Improbable.Collections.List <string> {
                    workerType
                })
                //new WorkerAttributeSet(new Improbable.Collections.List<string> {"client"}),
            });

            // Defines worker attribute requirements for workers that can write to a component.
            // workers with an attribute of workerType will have write access
            var workerWriteRequirementSet = new WorkerRequirementSet(
                new Improbable.Collections.List <WorkerAttributeSet>
            {
                new WorkerAttributeSet(new Improbable.Collections.List <string> {
                    workerType
                }),
            });

            var writeAcl = new Improbable.Collections.Map <uint, WorkerRequirementSet>
            {
                { EntityAcl.ComponentId, workerWriteRequirementSet },
                { Position.ComponentId, workerWriteRequirementSet },
                { Metadata.ComponentId, workerWriteRequirementSet },
                { Life.ComponentId, workerWriteRequirementSet }
            };

            entity.Add(new EntityAcl.Data(readRequirementSet, writeAcl));
            // Needed for the entity to be persisted in snapshots.
            entity.Add(new Persistence.Data());
            entity.Add(new Metadata.Data(entityType)); //Can use metadata to do color setting for visualization in inspector
            entity.Add(new Position.Data(new Coordinates(X, 0, Y)));
            entity.Add(new Life.Data(currentAlive, currentSequenceId, previousAlive, previousSequenceId));
            entity.Add(new Neighbors.Data(neighborsList));
            return(entity);
        }
コード例 #26
0
ファイル: LondonSnapshot.cs プロジェクト: dtbinh/drone-sim
        private static void LondonLarge()
        {
            float maxX = SimulationSettings.maxX; //routable width is 31500m
            float maxZ = SimulationSettings.maxZ; //routable height is 14000m

            var snapshotEntities = new Dictionary <EntityId, Entity>();
            var currentEntityId  = 2; //reserve id 1 for the scheduler

            Improbable.Collections.List <Improbable.Controller.NoFlyZone> noFlyZones = new Improbable.Collections.List <Improbable.Controller.NoFlyZone>();
            Improbable.Collections.List <ControllerInfo> controllers = new Improbable.Collections.List <ControllerInfo>();

            // NO FLY ZONES
            // start creating no fly zones from the editor
            NFZScript[] noFlyZoneScripts = FindObjectsOfType <NFZScript>();
            foreach (NFZScript noFlyZoneScript in noFlyZoneScripts)
            {
                noFlyZones.Add(noFlyZoneScript.GetNoFlyZone());
            }
            // end creation of no fly zones

            // CONTROLLERS
            // start placing controllers
            int firstController = currentEntityId;

            ControllerBehaviour[] controllerScripts = FindObjectsOfType <ControllerBehaviour>();
            foreach (ControllerBehaviour controllerScript in controllerScripts)
            {
                EntityId    controllerId  = new EntityId(currentEntityId++);
                Coordinates controllerPos = controllerScript.gameObject.transform.position.ToCoordinates();

                controllers.Add(new ControllerInfo(controllerId, controllerPos.ToSpatialVector3f()));

                snapshotEntities.Add(
                    controllerId,
                    EntityTemplateFactory.CreateControllerTemplate(
                        controllerPos,
                        new Vector3f(-maxX, 0, maxZ),
                        new Vector3f(maxX, 0, -maxZ),
                        noFlyZones,
                        PopulateControllerSlots()
                        ));
            }
            int lastController = currentEntityId;
            // controller placement complete

            // make nfz nodes show up on the inspector map
            //currentEntityId = ShowNoFlyZones(noFlyZones, snapshotEntities, currentEntityId);

            // ORDER GENERATOR
            // find and place order generator
            OrderGeneratorBehaviour orderGenerator = FindObjectOfType <OrderGeneratorBehaviour>();

            snapshotEntities.Add(
                SimulationSettings.OrderGeneratorEntityId,
                EntityTemplateFactory.CreateSchedulerTemplate(
                    orderGenerator.gameObject.transform.position,
                    firstController,
                    lastController,
                    noFlyZones,
                    controllers
                    )
                );
            // end scheduler placement

            SnapshotMenu.SaveSnapshot(snapshotEntities, "london_large");
        }