```json
{
  "sym_variables": [
    ("x1", "number of shipping containers"),
    ("x2", "number of cargo planes")
  ],
  "objective_function": "1000*x1 + 800*x2",
  "constraints": [
    "5000*x1 + 6000*x2 <= 20000",
    "x1 <= x2",
    "x1 >= 0",
    "x2 >= 0"
  ]
}
```

```python
import gurobipy as gp
from gurobipy import GRB

# Create a new model
m = gp.Model("Shipping Optimization")

# Create variables
containers = m.addVar(vtype=GRB.INTEGER, name="containers")
planes = m.addVar(vtype=GRB.INTEGER, name="planes")

# Set objective function
m.setObjective(1000 * containers + 800 * planes, GRB.MAXIMIZE)

# Add constraints
m.addConstr(5000 * containers + 6000 * planes <= 20000, "budget")
m.addConstr(containers <= planes, "shipping_delay")
m.addConstr(containers >=0, "non_neg_containers")
m.addConstr(planes >= 0, "non_neg_planes")


# Optimize model
m.optimize()

# Print results
if m.status == GRB.OPTIMAL:
    print(f"Optimal Solution Found:")
    print(f"Number of Shipping Containers: {containers.x}")
    print(f"Number of Cargo Planes: {planes.x}")
    print(f"Total Products Shipped: {1000 * containers.x + 800 * planes.x}")
elif m.status == GRB.INFEASIBLE:
    print("Model is infeasible.")
else:
    print(f"Optimization terminated with status {m.status}")

```
