public void Setup(List <IMyTerminalBlock> ship)
    {
        // First build map of defaults
        var defaultItemStocksMap = new Dictionary <string, ItemStock>();

        for (int i = 0; i < defaultItemStocks.Length; i++)
        {
            var itemStock = defaultItemStocks[i];
            defaultItemStocksMap.Add(itemStock.SubtypeName, itemStock);
        }

        // Get assemblers
        var assemblers = ZACommons.GetBlocksOfType <IMyAssembler>(ship);
        var candidates = new LinkedList <IMyAssembler>();

        // If anything has already been appropriately named, remove it from our map
        for (var e = assemblers.GetEnumerator(); e.MoveNext();)
        {
            var       assembler = (IMyAssembler)e.Current;
            ItemStock target;
            if (ItemStock.TryParse(assembler.CustomName, out target))
            {
                defaultItemStocksMap.Remove(target.SubtypeName);
            }
            else
            {
                // Otherwise add assembler as candidate for renaming
                candidates.AddLast(assembler);
            }
        }

        for (var e = defaultItemStocksMap.Values.GetEnumerator(); e.MoveNext() && candidates.First != null;)
        {
            var itemStock = e.Current;

            // Get first candidate
            var candidate = candidates.First.Value;
            candidates.RemoveFirst();

            // Rename appropriately
            StringBuilder builder = new StringBuilder();
            builder.Append(candidate.CustomName);
            builder.Append(' ');
            builder.Append(DelimiterStart);
            builder.Append(itemStock.SubtypeName);
            builder.Append(DelimiterAmount);
            builder.Append(itemStock.Amount);
            builder.Append(DelimiterEnd);
            candidate.SetCustomName(builder);
        }
    }
    public void Run(ZACommons commons, EventDriver eventDriver)
    {
        if (CurrentState == STATE_INACTIVE)
        {
            return;
        }

        var ship = LIMIT_PRODUCTION_MANAGER_SAME_GRID ? commons.Blocks : commons.AllBlocks;

        var allowedSubtypes  = new HashSet <string>();
        var assemblerTargets = new Dictionary <string, AssemblerTarget>();

        var assemblers = ZACommons.GetBlocksOfType <IMyAssembler>(ship);

        for (var e = assemblers.GetEnumerator(); e.MoveNext();)
        {
            var       assembler = (IMyAssembler)e.Current;
            ItemStock target;
            if (ItemStock.TryParse(assembler.CustomName, out target))
            {
                var subtype = target.SubtypeName;

                // When we take inventory, filter to just these types
                allowedSubtypes.Add(subtype);

                AssemblerTarget assemblerTarget;
                if (!assemblerTargets.TryGetValue(subtype, out assemblerTarget))
                {
                    assemblerTarget = new AssemblerTarget(target.Amount);
                    assemblerTargets.Add(subtype, assemblerTarget);
                }

                // Remember this assembler for this subtype
                assemblerTarget.Assemblers.Add(assembler);

                // Adjust target amount, if necessary
                assemblerTarget.Amount = Math.Max(assemblerTarget.Amount, target.Amount);
            }
        }

        if (CurrentState == STATE_ACTIVE)
        {
            // Get current stocks
            var stocks = EnumerateItems(ship, allowedSubtypes);

            // Now we just enable/disable based on who's low
            for (var e = assemblerTargets.GetEnumerator(); e.MoveNext();)
            {
                var subtype = e.Current.Key;
                var target  = e.Current.Value;
                VRage.MyFixedPoint currentStock;
                if (!stocks.TryGetValue(subtype, out currentStock))
                {
                    currentStock = (VRage.MyFixedPoint) 0.0f;
                }

                // Enable or disable based on current stock
                target.EnableAssemblers((float)currentStock < target.Amount);
            }
        }
        else if (CurrentState == STATE_INACTIVATING)
        {
            // Shut down all known assemblers
            for (var e = assemblerTargets.Values.GetEnumerator(); e.MoveNext();)
            {
                e.Current.EnableAssemblers(false);
            }
            SetState(commons, STATE_INACTIVE);
        }

        eventDriver.Schedule(RunDelay, Run);
    }