![]() |
The PiWeb-Calculated-Characteristics defines the business logic how calculated characteristics are evaluated by the quality data management system ZEISS PiWeb. |
---|
Calculated characteristics in PiWeb are characteristics without their own measured values. Instead, their values are calculated by formulas. These formulas allow referencing values of other characteristics that can be measured regularly or calculated as well. Using calculated characteristics makes it possible to provide pre-calculated values for reporting without having to write system expressions over and over again.
This repository defines the core business logic of parsing formula expressions and calculating results. It is exactly the same logic used by PiWeb application.
The PiWeb Calculated Characteristics library is available via NuGet.
Get it at NuGet.org.
PM> Install-Package Zeiss.PiWeb.CalculatedCharacteristics
Or compile the library by yourself. Requirements:
- Microsoft .NET Standard 2.1 or .NET 6
node | |
---|---|
expression | expression '+' term | expression '-' term | term |
term | term '*' factor | term '/' factor | factor |
factor | [+-] ( NUMBER | function | '"' IDENT '"' | '(' expression ')' | '{' path '}' ) |
function | IDENT '(' [argumentlist ] ')' |
argumentlist | expression | expression '[,;]' argumentlist |
path | pathsegment | pathsegment '/' path | pathsegment '(' KEY ')' |
pathsegment | IDENT | '"' IDENT '"' |
NUMBER | any floating point value (with . as decimal separator) |
IDENT | any sequence of characters |
KEY | any integer in range [0,65535] |
Characteristics are always enclosed in curly brackets. By default, the path is specified relative to the parent origin of the formula.
Part and characteristic names that contain following characters ( ) { } " \ /
can be escaped. Escaping is possible by
- Set the escape character
\
in front of the character to escape - Enclose the name by
"
. Escaping"
and\
within the escaped name can be done with the escape character\
.
The following list shows some examples defining characteristic paths:
{abc}
-> References to characteristic abc of the same parent part or characteristic{abc(20)}
-> References to characteristic attribute with key 20 of characteristic abc{abc\(20\)}
-> References to characteristic abc(20){"abc(20)"}
-> References to characteristic abc(20){abc \"20\"}
-> References to characteristic abc"20"{"abc \"20\""}
-> References to characteristic abc"20"{"abc \"20\""(20)}
-> References to characteristic attribute with key 20 of characteristic abc"20"{../xyz/abc}
-> References to characteristic abc of part or characteristic xyz{../xyz\/abc}
-> Reference to characteristic xyz/abc{../"xyz/abc"}
-> Reference to characteristic xyz/abc
The following sample code shows an easy way of using the API. The data normally comes from the PiWeb-API. Working without remote access the sample code generates the data manually.
namespace Sample;
using System;
using System.Collections.Generic;
using System.Linq;
using Zeiss.PiWeb.Api.Definitions;
using Zeiss.PiWeb.Api.Core;
using Zeiss.PiWeb.Api.Rest.Dtos.Data;
using Zeiss.PiWeb.CalculatedCharacteristics;
internal class Program
{
private const string Formula1 = "1 + 2";
private const string Formula2 = "{char1} + {Char3}";
private static void Main(string[] args)
{
/***** setup dummy inspection plan *****/
// create the inspection plan items (normally this comes from the PiWeb-Server via REST.API)
var part = new InspectionPlanPartDto
{
Path = PathInformation.Combine(PathInformation.Root, PathElement.Part("Part")),
Uuid = Guid.NewGuid()
};
var char1 = new InspectionPlanPartDto
{
Path = PathInformation.Combine(part.Path, PathElement.Char("Char1")),
Uuid = Guid.NewGuid(),
Attributes = [new(WellKnownKeys.Characteristic.LogicalOperationString, Formula1)]
};
var char2 = new InspectionPlanPartDto
{
Path = PathInformation.Combine(part.Path, PathElement.Char("Char2")),
Uuid = Guid.NewGuid(),
Attributes = [new(WellKnownKeys.Characteristic.LogicalOperationString, Formula2)]
};
var char3 = new InspectionPlanPartDto
{
Path = PathInformation.Combine(part.Path, PathElement.Char("Char3")),
Uuid = Guid.NewGuid()
};
// we need to describe how inspection plan items are related
var structureMap = new Dictionary<PathInformation, (InspectionPlanDtoBase Entity, PathInformation[] Children)>
{
{ part.Path, (part, [char1.Path, char2.Path, char3.Path]) },
{ char1.Path, (char1, []) },
{ char2.Path, (char2, []) },
{ char3.Path, (char3, []) }
};
/***** Setup some measurement data *****/
var measurementsData = new Dictionary<PathInformation, double?>
{
{ char3.Path, 5 }
};
// define how we get the value for an characteristic
double? GetMeasurementValueForPath(PathInformation path)
{
return measurementsData.TryGetValue(path, out var value) ? value : null;
}
/***** create factory to create calculators *****/
// define how children of an inspection plan item are provided
IEnumerable<PathInformation> ChildPathsHandler(PathInformation parent)
{
return structureMap.TryGetValue(parent, out var item) ? item.Children : [];
}
// defines how to get the attribute value for an inspection plan item from path
object? GetValueFromAttribute(PathInformation path, ushort key)
{
if (!structureMap.TryGetValue(path, out var item))
return null;
return item.Entity.Attributes.FirstOrDefault(a => a.Key == key).Value;
}
// create the calculators (once created we can reuse it)
var factory = new AttributeBasedMathInterpreterFactory(GetValueFromAttribute, ChildPathsHandler);
var char1Calculator = factory.GetCharacteristicCalculator(char1.Path);
var char2Calculator = factory.GetCharacteristicCalculator(char2.Path);
// should output "Result of Char1: 3"
Console.Write("Result of Char1: ");
Console.WriteLine(char1Calculator.GetResult(
GetMeasurementValueForPath,
(path, key, _) => GetValueFromAttribute(path, key)));
// should output "Result of Char2: 8"
Console.Write("Result of Char2: ");
Console.WriteLine(char2Calculator.GetResult(
GetMeasurementValueForPath,
(path, key, _) => GetValueFromAttribute(path, key)));
}
}
The type AttributeBasedMathInterpreterFactory
reads the formula from the predefined characteristic attribute. If another formula source is required, inherit from SimpleMathInterpreterFactory
and override method GetFormula
.
This repository makes use of resuable workflows from ZEISS-PiWeb/github-actions. Read the documentation (especially about automated semantic versioning) before committing any changes.