AutoCalculation.g

AutoCalculation.g — automatically calculates formulas based on data points.

Code

[Note]

Please refer to Section 3.1, “How to Run a Script” for more information on using scripts.

/* 
 * Automatically calculate formulas based on data points.  Create data points where
 * necessary to store the results back to the DataHub's data set.
 *
 * To use:
 *  - create a file, like c:/tmp/calculations.txt
 *  - in the file, create any number of calculations of the form
 *      $default:test = $DataPid:PID1.Mv * 10;
 *      $default:test2 = $DataPid:PID1.Mv / 10;
 *  - change the fileName member in the AutoCalculation class below to refer to your file
 *  - run the script
 *
 * Any symbol in the calculation of the form $domain:name is assumed to be a DataHub point,
 * and will be created if necessary.  The point on the left hand side of the equal sign will
 * be automatically updated whenever any point on the right hand side of the equal sign
 * changes.
 *
 * You can include complex Gamma expressions, like:
 *   $default:output = progn {
 *      local a = $DataPid:PID1.Mv;
 *      local b = $DataPid:PID1.Pv;
 *      if (b != 0)
 *              a / b;
 *      else
 *              a;
 *      };
 *
 * You can define functions within this file, though it is better to put functions into a
 * separate script file:
 *
 *      function average(a,b)
 *      {
 *          a + b / 2;
 *      }
 *
 *      $default:output = average($DataPid:PID1.Mv, $DataPid:PID1.Pv);
 */

require ("Application");

class AutoCalculation Application
{
    fileName = "C:/tmp/calculations.txt";
    fp;
}

class Computation
{
    app;        // the Application instance defining this computation
    output;     // a symbol
    inputs;     // a list of symbols
    code;       // the code to execute
}

method Computation.constructor(app, expression)
{
    
    .app = app;
    .code = expression;
    .output = .findOutput(expression);
    .inputs = .findInputs(expression);
    
    if (.output)
    {
        datahub_command(format("(create %s 1)", stringc(.output)), 1);
        with input in .inputs do
        {
            datahub_command(format("(create %s 1)", stringc(input)), 1);
            
            .app.OnChange(input, `(@self).compute());
        }
    }
    .compute();
}

method Computation.compute()
{
    try
    {
        eval(.code);
    }
    catch
    {
        princ ("Assignment to ", .output, " failed: ", _last_error_, "\n");
    }
}

method Computation.findOutput (expr)
{
    if (car(expr) == #setq)
        cadr(expr);
    else
        nil;
}

method Computation.findInputsRecursive (expr)
{
    local   inputs, partial;
    if (list_p(expr))
    {
        with part in expr do
        {
            partial = .findInputsRecursive(part);
            inputs = nappend(inputs, partial);
        }
    }
    else if (symbol_p(expr) && strchr(string(expr), ":") != -1)
    {
        inputs = cons(expr, inputs);
    }
    inputs;
}

method Computation.findInputs (expr)
{
    .findInputsRecursive(cddr(expr));
}

method AutoCalculation.readFile()
{
    if ((.fp = open(.fileName, "r", t)) != nil)
    {
        //create some local variables 
        local line;
        
        //loop until we have read the entire file.
        while((line = read(.fp)) != _eof_)
        {
            local comp = new Computation(self, line);
        }
        
        //once we have read the entire file we need to close it.
        close(.fp);
    }
    else
    {
        princ ("AutoCalculation: Could not open file: ",
	       .fileName, ": ", strerror(errno()), "\n");
    }
}

/* Write the 'main line' of the program here. */
method AutoCalculation.constructor ()
{
    .readFile();
}

/* Any code to be run when the program gets shut down. */
method AutoCalculation.destructor ()
{
}

/* Start the program by instantiating the class. */
ApplicationSingleton (AutoCalculation);