Skip to content
Gabe Stocco edited this page Jul 16, 2020 · 20 revisions

Logical Analyzer is a rules processing engine to apply provided rules against arbitrary objects

Walkthrough

This is a quick walkthrough of using Logical Analyzer to build a toll processing system. The sample code is available in the tests

Our Target Object

In this case we are going to be operating on Vehicles. So we create a simple vehicle class with a mix of fields and properties.

class Vehicle
{
    public int Weight;
    public int Axles { get; set; }
    public int Occupants { get; set; }
}

Our Rules

Lets start with a basic rule. Vehicles over 1000 weight need to pay a toll.

new VehicleRule("Normal Car"){
    Cost = 3,
    Severity = 1,
    Target = "Vehicle",
    Clauses = new List<Clause>()
    {
        new Clause("Weight", OPERATION.GT)
        {
            Data = new List<string>()
            {
                "1000"
            }
        }
    }
},

Now this leaves out motorcycles, so lets add a smaller toll for them

new VehicleRule("Motorcycle"){
    Cost = 1,
    Severity = 0,
    Target = "Vehicle",
    Clauses = new List<Clause>()
    {
        new Clause("Weight", OPERATION.LT)
        {
            Data = new List<string>()
            {
                "1001"
            }
        }
    }
}

Trucks cause a lot of wear on roads, so we could add a higher toll based on axles

new VehicleRule("Overweight or long")
{
    Cost = 10,
    Severity = 3,
    Expression = "Weight OR Axles",
    Target = "Vehicle",
    Clauses = new List<Clause>()
    {
        new Clause("Weight", OPERATION.GT)
        {
            Label = "Weight",
            Data = new List<string>()
            {
                "4000"
            }
        },
        new Clause("Axles", OPERATION.GT)
        {
            Label = "Axles",
            Data = new List<string>()
            {
                "2"
            }
        }
    }
}

And finally we want to encourage carpooling so we add a lower toll for carpools.

new VehicleRule("Carpool Car"){
    Cost = 2,
    Severity = 2,
    Target = "Vehicle",
    Expression = "WeightGT1000 AND WeightLT4000 AND OccupantsGT2",
    Clauses = new List<Clause>()
    {
        new Clause("Weight", OPERATION.GT)
        {
            Label = "WeightGT1000",
            Data = new List<string>()
            {
                "1000"
            }
        },
        new Clause("Weight", OPERATION.LT)
        {
            Label = "WeightLT4000",
            Data = new List<string>()
            {
                "4000"
            }
        },
        new Clause("Occupants", OPERATION.GT)
        {
            Label = "OccupantsGT2",
            Data = new List<string>()
            {
                "2"
            }
        },
    }
}

Putting It Together

Now we can take our list of rules and calculate the cost for each vehicle

int GetCost(Vehicle vehicle, Analyzer analyzer, IEnumerable<Rule> rules)
{
    return ((VehicleRule)analyzer.Analyze(rules, vehicle).MaxBy(x => x.Severity).First()).Cost;
}

Let's create a couple vehicles and see how much toll they would pay

var truck = new Vehicle()
{
    Weight = 20000,
    Axles = 5,
    Occupants = 1
};

var car = new Vehicle()
{
    Weight = 3000,
    Axles = 2,
    Occupants = 1
};

var carpool = new Vehicle()
{
    Weight = 3000,
    Axles = 2,
    Occupants = 3
};

var motorcycle = new Vehicle()
{
    Weight = 1000,
    Axles = 2,
    Occupants = 1
};

var analyzer = new Analyzer();

Assert.IsTrue(GetCost(truck, analyzer, rules) == 10);
Assert.IsTrue(GetCost(car, analyzer, rules) == 3);
Assert.IsTrue(GetCost(carpool, analyzer, rules) == 2);
Assert.IsTrue(GetCost(motorcycle, analyzer, rules) == 1);
Clone this wiki locally