Routes and Material Handling

Each Product route is an ordered process definition. Each consecutive pair requires a directed movement connection. If a Product travels Source → Machine → Sink, configuring only Source → Machine is incomplete.

Define a linear route

bracket = Product(
    "Bracket",
    [receiving, cutting, wip, assembly, shipping],
)
plant.add(bracket)

The same physical connection can serve more than one Product. Add it once, then include the corresponding locations in each Product route.

Select a handling method

Method Required objects Connection pattern Best for teaching
Manual carry Worker with "transport" skill worker=handler Labor travel and contention
Forklift Forklift and qualified driver worker=driver, transporter=lift Coupled labor/equipment availability
AGV AGV only transporter=agv Autonomous shared transport

Manual carrying:

from manufacturing_sim_s import Worker

handler = Worker("Material Handler", (5, 5), speed=4, skills=["transport"])
plant.add(handler)
plant.connect(receiving, cutting, worker=handler)

Forklift movement:

from manufacturing_sim_s import Forklift, Worker

driver = Worker("Forklift Driver", (5, 5), skills=["transport"])
lift = Forklift("Forklift 1", (10, 5), speed=8)
plant.add(driver, lift)
plant.connect(receiving, cutting, worker=driver, transporter=lift)

Autonomous AGV movement:

agv = AGV("AGV 1", (5, 5), speed=6)
plant.add(agv)
plant.connect(receiving, cutting, transporter=agv)

Do not assign a Worker to an AGV connection. A Forklift always requires a Worker. A manually transporting Worker must have the "transport" skill.

Assign a Machine operator

Transport and machine operation are separate assignments:

operator = Worker("Cutter Operator", (28, 20), skills=["cutting"])
cutting = Machine(
    "Cutting",
    (30, 25),
    12,
    10,
    processing_time=55,
    worker=operator,
    required_skill="cutting",
)
plant.add(operator, cutting)

The operator is occupied for the full operation. If the same Worker also handles material, machine work and transport compete for that person.

Add products with different processing times

multi_plant = Plant("Mixed Product Line", 145, 60)
multi_receiving = Source("Receiving", (5, 25), 10, 10, arrivals=75)
multi_cutting = Machine(
    "Cutting",
    (30, 25),
    12,
    10,
    processing_times={"Bracket": 55, "Plate": 85},
)
multi_assembly = Machine("Assembly", (84, 25), 12, 10, processing_time=80)
multi_shipping = Sink("Shipping", (120, 25), 10, 10)
multi_agv = AGV("AGV 1", (5, 5))

bracket = Product(
    "Bracket",
    [multi_receiving, multi_cutting, multi_assembly, multi_shipping],
)
plate = Product("Plate", [multi_receiving, multi_cutting, multi_shipping])
multi_receiving.product_mix = {bracket: 0.7, plate: 0.3}

multi_plant.add(
    multi_receiving, multi_cutting, multi_assembly, multi_shipping,
    multi_agv, bracket, plate,
)
required_edges = {
    (origin, destination)
    for product in (bracket, plate)
    for origin, destination in zip(product.route, product.route[1:])
}
for origin, destination in required_edges:
    multi_plant.connect(origin, destination, transporter=multi_agv)

print(multi_plant.validate())

Every Product that visits a Machine with processing_times needs a matching entry. The edge set avoids adding the shared Receiving-to-Cutting connection twice.

Use Buffers deliberately

A Buffer(capacity=8) provides intentional storage. When full, upstream flow blocks until space opens. Machine input queues are automatic and are not a substitute for a modeled floor-storage location. Add a Buffer to a route only when that storage and its capacity matter to the experiment.

Use Advanced Features for conditional, probabilistic, rework, and scrap routes. Then continue to Run, Analyze, and Redesign.