## Symbolic Representation of the Optimization Problem

The optimization problem can be symbolically represented as follows:

```json
{
    'sym_variables': [
        ('x1', 'chocolate ice cream cones'),
        ('x2', 'vanilla ice cream cones')
    ],
    'objective_function': '2*x1 + 1.5*x2',
    'constraints': [
        'x1 >= 30',
        'x1 <= 50',
        'x2 >= 20',
        'x2 <= 60',
        'x1 + x2 <= 70'
    ]
}
```

## Gurobi Code

To solve this optimization problem using Gurobi, we can use the following Python code:

```python
import gurobi

# Create a new Gurobi model
model = gurobi.Model()

# Define the variables
x1 = model.addVar(lb=0, ub=70, name="chocolate_ice_cream_cones", vtype=gurobi.GRB.INTEGER)
x2 = model.addVar(lb=0, ub=70, name="vanilla_ice_cream_cones", vtype=gurobi.GRB.INTEGER)

# Set the objective function
model.setObjective(2*x1 + 1.5*x2, gurobi.GRB.MAXIMIZE)

# Add constraints
model.addConstr(x1 >= 30, name="min_chocolate")
model.addConstr(x1 <= 50, name="max_chocolate")
model.addConstr(x2 >= 20, name="min_vanilla")
model.addConstr(x2 <= 60, name="max_vanilla")
model.addConstr(x1 + x2 <= 70, name="total_cones")

# Optimize the model
model.optimize()

# Print the solution
if model.status == gurobi.GRB.OPTIMAL:
    print("Optimal solution found.")
    print(f"Chocolate ice cream cones: {x1.varValue}")
    print(f"Vanilla ice cream cones: {x2.varValue}")
    print(f"Maximum profit: {model.objVal}")
else:
    print("No optimal solution found.")
```

This code defines the variables, sets the objective function, adds the constraints, and optimizes the model using Gurobi. The solution is then printed out, including the number of chocolate and vanilla ice cream cones to sell and the maximum profit.