CalcEngine Plus

User-authored calculations for .NET

Integrating Your Data Stores

To turn a user-authored expression like “voltage/resistance” into a number (we speak of “evaluating an expression”, and getting a “result”) you’re going to have come up with some actual values for “voltage” and “resistance” at some point. Let’s look at some code:


namespace BasicConsoleApp
{
    using CalcEngine;

    class Program
    {
        static void Main(string[] args)
        {
            Parser parser = new CalcEngine.Parser();
            Operand operand = parser.Parse("1000 * voltage / resistance");
            double milliamps = operand.Result;
        }
    }
}

 

The business of examining a string like "1000 * voltage / resistance" and figuring out what tags are required to evaluate it is called “parsing an expression”. That’s what the Parse() method does.

The Parse() method returns an Operand, which has a Result property. Every time you ask for the Result property, the Operand is evaluated and you get the numeric result back. Easy, right?

Not so fast! If you try to run this, it will blow up when it tries to evaluate the result (“The calc engine needs a helper object to provide tag values”). The exception is complaining that it doesn’t know what to do with “voltage” or “resistance”. Which is fair enough; we haven’t yet given the calc engine much help in this respect.  

So we need to tell the calc engine what values to use for “voltage” and “resistance”. Where these values come from is up to you and your application. In the resources or process control world, you might pull these from a process historian. Finance wizards might have these values in a transaction database. Modellers might have the necessary values right there in an in-memory dictionary. Whatever your situation is, you have to give the Parser some way of getting those tag values, whenever it needs them. Here’s how:

Wiring up calculations with real-world tags

In the vast majority of cases, the calc engine needs a helper object to do anything useful. As a developer, you have to write the helper object. Only you know what constitutes a tagname in your data stores, and only you know how to retrieve a value for a tagname.

The good news is, you’re probably already doing this kind of heavy lifting and you know your data stores pretty well. You have to write a class that implements ITagValueService, which only has one method: GetNumericValue. This method returns a numeric value for a given tagname.

Here’s a simple example of an ITagValueService implementation to suit the above:


    class TagValueServer : ITagValueService
    {
        public double GetNumericValue(string tagname, out bool tagFound, out string errorMessage)
        {
            errorMessage = string.Empty;
            tagFound = true;

            // In a real-world application, you would be making calls 
            // to retrieve these tag values from an external system.
            // Here, we'll just synthesize some hard-coded values.
            switch (tagname.ToUpper())
            {
                case "VOLTAGE":
                    return 240;
                case "RESISTANCE":
                    return 1000;
                default:
                    errorMessage = "Tag not found: " + tagname;
                    tagFound = false;
                    return double.NaN;
            }
        }
    }

 

And we need to tell the Parser about this helper class, too. We must pass in our ITagValueService implementation via the constructor, as follows:

 


      Parser parser = new CalcEngine.Parser(new TagValueServer());

 

So let’s make that change, and add a line to print out the result:

 


    class Program
    {
        static void Main(string[] args)
        {
            Parser parser = new CalcEngine.Parser(new TagValueServer());
            Operand operand = parser.Parse("1000 * voltage / resistance");
            double milliamps = operand.Result;

            Console.WriteLine("Our result is {0}mA. \nHit any key to exit", milliamps);
            Console.ReadLine(); // Pause to admire the result.
        }
    }

 

Our result is 240mA (being 1000 * 240 / 1000). In a real app, we’d go on to store that calculated value, or display it somehow.[BasicConsoleApp hyperlink]