Calculation Code Examples

This page provides calculation code examples

1. How to add new record to Alarm Log Row table ?

// Automatically generated scaffolding for the calculation code
//
// Code name: no code name defined, put code name on the input box above this code editor
// List of variables that you can use based on parameters that you have added:
// CalcEquipment_Pump P1

using System.Linq;
using System;

namespace ABB.Vtrin.CalcEngine
{
	public class CalcClass_PumpAlarmLogRow : CalcInstance
	{
		// put your constructor here
		
		public void Calculate(CalcEquipment_Pump P1)
		{
			// put your calculation code here
			
			WriteLine(P1.RPM.CurrentValue.Value.ToString());
			WriteLine(P1.PathInstance.ToString());
			
			var equipmentInstnace = P1.PathInstance.ToString();
			
			var instnaceID = Driver.Classes["Path"].Instances.GetInstanceByName(equipmentInstnace).Id;
			
			var propID = GetPropertyId(P1.EquipmentId.ToString(),"RPM");
			
			WriteLine("Pump Instance ID is : "+instnaceID.ToString());
			
			WriteLine("Pump RPM ID is : "+propID.ToString());
		   
			if(P1.PRESSURE.CurrentValue.Value>10){
			   
		   var pathClass = Driver.Classes["AlarmLogRow"];
		      if(pathClass!=null){
		         var newInstance = pathClass.Instances.Add();
		         newInstance.SetRawPropertyValue("EventTime", DateTime.UtcNow);//this is GUID of M1 motor instance
		         newInstance.SetRawPropertyValue("Instance", instnaceID);//this is GUID of M1 motor instance
		         newInstance.SetRawPropertyValue("Property",propID);
		         pathClass.Instances.CommitChanges([newInstance]);
		      }
			}   
		}
			   
		private System.Guid GetPropertyId(string equipmenttypeid, string propertyname)
      {
         var equs = Driver.Classes["Equipment"];
         if (equs == null)
            throw new System.ApplicationException("Class 'Equipment' not available");

         var infos = Driver.Classes["EquipmentPropertyInfo"];
         if (infos == null)
            throw new System.ApplicationException("Class 'EquipmentPropertyInfo' not available");

         var equ = equs.Instances.GetInstanceById(equipmenttypeid);
         if (equ == null)
            throw new System.ApplicationException($"Equipment type '{equipmenttypeid}' not found");

         // Search from equipment and base equipments
         for (; ; )
         {
            var def = infos.Instances.GetInstanceSet("Equipment=? and DisplayName=?", equ, propertyname);
            if (def.Length != 0)
            {
               return (System.Guid)def[0].Id;
            }
            else
            {
               var baseequ = equ["Base"];
               if (baseequ == null)
                  throw new System.ApplicationException($"Property name '{propertyname}' not found");
               equ = (ABB.Vtrin.cDbClassInstance)baseequ;
            }
         }
      }
 
}
}

2. How to read history data from equipment property and write to another property ?

// Automatically generated scaffolding for the calculation code
// Code name: no code name defined, put code name on the input box above this code editor
// List of variables that you can use based on parameters that you have added:
// CalcEquipment_MOTOR motor

using System.Linq;
using System;
using System.Collections.Generic;

namespace ABB.Vtrin.CalcEngine
{
	public class CalcClass_ReadAndWriteArrayDataType_1000 : CalcInstance
	{
		// put your constructor here
		
		public void Calculate(CalcEquipment_MOTOR motor)
		{
			// put your calculation code here
			DateTime startTime = DateTime.Now.AddMinutes(-5);
			DateTime endTime = DateTime.Now;
			
			var prop1 = motor.PROP1.FetchGraphData("", startTime, endTime,"StreamHistory");
			WriteLine("Graph Data Points Count :: " + prop1.Count);
			
			for(int i =0;i<prop1.Count;i++){
			   var xValue = Convert.ToDateTime(prop1.GetXValue(i));
			   var yValue = prop1.GetYValue(i);
			   motor.PROP_2.WriteValue(value, xValue.AddMilliseconds(i));
			}
		}
	}
}

3. Write data to OPC Event table based on a condition

// Automatically generated scaffolding for the calculation code
// Code name: no code name defined, put code name on the input box above this code editor
// List of variables that you can use based on parameters that you have added:

using System.Linq;
using System;
using System.Collections.Generic;

namespace ABB.Vtrin.CalcEngine
{
	public class CalcClass_WriteDataToOPCEventTable : CalcInstance
	{
		// put your constructor here
		
		public void Calculate(CalcEquipment_MOTOR motor)
		{
      // put your calculation code here
      if(motor.RPM.CurrentValue.Value >= 1500){
            var pathClass = driver.Classes["OpcEvent"];
            var newInstance = pathClass.Instances.Add();
            newInstance.SetRawPropertyValue("EventTime", DateTime.UtcNow);
            newInstance.SetRawPropertyValue("Source", "Automation Test");
            newInstance.SetRawPropertyValue("Message", uniqueName);
        pathClass.Instances.CommitChanges(new[] { newInstance });
}		
		}
	}
}

4. Read data from third part APIs using calculation.

This example reads data from a third party API and parses the data and writes to a variable.

// Automatically generated scaffolding for the calculation code
//
// Code name: no code name defined, put code name on the input box above this code editor
// List of variables that you can use based on parameters that you have added:
// CalcVariable<double> temperature

using System;
using System.Globalization;
using System.Linq;
using System.Xml;

namespace ABB.Vtrin.CalcEngine
{
	public class CalcClass_LatestTemperatureFetcher : CalcInstance
	{
		// put your constructor here
		
		public void Calculate(CalcVariable<double> temperature)
		{
			// put your calculation code here
			
			// Load to XmlDocument object
			using (var xmlReader = new XmlTextReader("http://opendata.fmi.fi/wfs?service=WFS&version=2.0.0&request=getFeature&storedquery_id=fmi::observations::weather::simple&place=helsinki")
			{
				DtdProcessing = DtdProcessing.Ignore
			})
			{
				var xmlDocument = new XmlDocument();
				xmlDocument.Load(xmlReader);
				var namespaceManager = new XmlNamespaceManager(xmlDocument.NameTable);
				namespaceManager.AddNamespace("wfs", "http://www.opengis.net/wfs/2.0");
				namespaceManager.AddNamespace("BsWfs", "http://xml.fmi.fi/schema/wfs/2.0");

				// Get all <BsWfsElement>s that contain <ParameterName> with content t2m = air temperature
				// t2m param is air temperature: http://opendata.fmi.fi/meta?observableProperty=observation&param=t2m&language=eng
				var xmlNodes = xmlDocument.SelectNodes("//BsWfs:BsWfsElement[BsWfs:ParameterName and BsWfs:ParameterName[text()='t2m']]", namespaceManager);
				if (xmlNodes is null || xmlNodes.Count == 0)
				{
					throw new Exception("Unable to get <BsWfsElement>s from xml.");
				}

				// Order <BsWfsElement>s by <BsWfs:Time> value and
				// get <BsWfs:ParameterValue> and <BsWfs:Time> element value
				(DateTime date, double temperature)? latestMeasurement = xmlNodes
					.OfType<XmlElement>()
					.Select(e => (
						date: e.SelectSingleNode("BsWfs:Time", namespaceManager)?.InnerText,
						temperature: e.SelectSingleNode("BsWfs:ParameterValue", namespaceManager)?.InnerText)
					)
					.Where(v => !string.IsNullOrWhiteSpace(v.date) && !string.IsNullOrWhiteSpace(v.temperature))
					.Select(v => (
						date: DateTime.Parse(v.date, CultureInfo.InvariantCulture).ToUniversalTime(),
						temperature: double.Parse(v.temperature, NumberStyles.Number, CultureInfo.InvariantCulture)
					))
					.OrderByDescending(v => v.date)
					.FirstOrDefault();

				if (!latestMeasurement.HasValue)
				{
					throw new Exception($"Unable to parse <BsWfs:Time> and <BsWfs:ParameterValue> elements from {xmlNodes.Count} <BsWfsElement> elements.");
				}

				//temperature = latestMeasurement.Value.temperature;
				//date = latestMeasurement.Value.date;
				

				temperature.SetCurrentValue(latestMeasurement.Value.temperature, latestMeasurement.Value.date);
			}
			
		}
	}
}
// Automatically generated scaffolding for the calculation code
//
// Code name: no code name defined, put code name on the input box above this code editor
// List of variables that you can use based on parameters that you have added:
// CalcVariable<double> ElectricityPrice

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;

namespace ABB.Vtrin.CalcEngine
{
	public class CalcClass_CurrentElectricityPriceFetcher : CalcInstance
	{
		// put your constructor here
		
		public void Calculate(CalcVariable<double> ElectricityPrice)
		{
			// put your calculation code here
			
			using (var webClient = new WebClient())
			{
				// Download JSON
				var jsonString = webClient.DownloadString("https://elspotcontrol.netlify.app/spotprices-v01-FI.json");

				var regexp = new Regex(@"{""time"": ""([^""]+)"", ""price"": (\d+\.\d+)}");

				var matches = regexp.Matches(jsonString);

				var results = new List<(double price, DateTime date)>();
				foreach (var item in matches.OfType<Match>())
				{
					results.Add((
						price: double.Parse(item.Groups[2].Value, NumberStyles.Number, CultureInfo.InvariantCulture) / 10,
						date: DateTime.Parse(item.Groups[1].Value, CultureInfo.InvariantCulture).ToUniversalTime()
					));
				}

				var currentTime = DateTime.UtcNow;
				var (price, date) = results.FirstOrDefault(r => r.date.Date == currentTime.Date && (r.date.Hour == currentTime.Hour || r.date.Hour == currentTime.Hour - 1));

				// currentElectricityPrice = price;
				ElectricityPrice.SetCurrentValue(price, currentTime);
				
			}
			
		}
	}
}

5. Totalizer calculation of Steam Flow example

Lets assumes Steam Flow Data is updated for every 1 second for a variable (Steam_Flow) and being collected for 1 minute. And let's sum the data points for 1 minute duration and write to a new variable 'Steam_Flow_Totalizer'

// Automatically generated scaffolding for the calculation code
//
// Code name: no code name defined, put code name on the input box above this code editor
// List of variables that you can use based on parameters that you have added:
// CalcVariable<double> Temperature, CalcVariable<double> TotalizedTemperature

using System.Linq;
using System;

namespace ABB.Vtrin.CalcEngine
{
	public class CalcClass_calc_1 : CalcInstance
	{
		// put your constructor here
		
		public void Calculate(CalcVariable<double> Steam_Flow, CalcVariable<double> Steam_Flow_Totalizer)
		{
			// put your calculation code here
			
			DateTime time1 = DateTime.Now.AddMinutes(-1);
			DateTime time2 = DateTime.Now.AddMinutes(0);
			
			var sum = Steam_Flow.GetSum(time1,time2,"StreamHistory");
			
			WriteLine("SUM :: "+sum.Value.ToString());
			Steam_Flow_Totalizer.CurrentValue = sum.Value;
		}
	}
}

6. How to fetch Max and Min value of a Tag using calculation

This example helps the user to fetch Max Value and Min Value of a Tag using the calculation.

// Automatically generated scaffolding for the calculation code
//
// Code name: no code name defined, put code name on the input box above this code editor
// List of variables that you can use based on parameters that you have added:
// CalcVariable<double> TAG1

using System.Linq;
using System;

namespace ABB.Vtrin.CalcEngine
{
	public class CalcClass_FetchValueMinAndMaxOfVariable : CalcInstance
	{
		// put your constructor here
		
		public void Calculate(CalcVariable<double> TAG1)
		{
			// put your calculation code here
			var instance = Driver.Classes["Tag"].Instances.GetInstanceSet($"DisplayName = "+TAG1.Name).FirstOrDefault();
			if(instance != null){
			   var min = instance["ValueDisplayMin"];
			   var max = instance["ValueDisplayMax"];
			   if(TAG1.CurrentValue.Value > Convert.ToDouble(min)){
			      WriteLine("Current value of " + TAG1.Name + " is off the Min limit " + min.ToString());
			   }
			}
		}
	}
}