
Introspection means extracting semantic (meaningful) information from the code. For example: getting variable names, type names, default values, comments etc. This is useful when building a UI to present the code to the user. Sounds familiar? This is what the SCL editor does.
To do the introspection we need a language parser. In fact this is the same parser that is used when running the code. We just use it differently.
The parser is encapsulated by the SCL script engine. This is the component that implements the active scripting interfaces along with some custom interfaces from us.
It is based on the Component Object Model (COM). Process Simulate API is based on .Net. This is why the scripting engine API has never been made public. Still most of it is available in the Tecnomatix.Engineering.Scripting.SCL.dll which is distributed along with Process Simulate. It contains the COM interface definitions needed for the interop calls to the native scripting components. Why most and not all of it? This has to do with the compatibility of the active scripting API with .Net. Sometimes interop calls are simply not possible. They fail or cause crashes for no obvious reason.
One of our interfaces (ISCLScriptAssist) is of particular interest for the purpose of the introspection use case. As the name suggests it provides assistance when dealing with SCL scripts. What kind of assistance is this? Namely: semantic checks, code completion, code introspection. Don't think of something complex. These are all pretty simple to use.
Now let's focus on code introspection.
ISCLScriptAssist::GetScriptInfo method is the entry point. You provide the script text on input and it returns an object implementing ISCLScriptInfo interface. ISCLScriptInfo::Qualifier returns the script type (FB/FC/UDT). ISCLScriptInfo::BlockName returns the name of the block. ISCLScriptInfo::Version returns the version of the block. ISCLScriptInfo::Variables return the list of all variables declared inside the block. ISCLScriptInfo::Attributes returns the list of all system attributes of the block. ISCLScriptInfo::Types returns the list of all types defined in the script. ISCLScriptInfo::References returns the names of all script blocks (FC/FB) referenced by the block. Simple!
Here is a .Net script viewer snippet that lists all I/O variables in a SCL block along with their ids:
using System;
using System.IO;
using System.Windows.Forms;
using Tecnomatix.Engineering;
using Tecnomatix.Engineering.Scripting.SCL;
public class MainScript
{
const string script = "FUNCTION_BLOCK \"MAIN\" VAR_INPUT in {ID='956F7874-B4C3-4A7F-9F66-088C8483BCD5'}:LREAL; END_VAR VAR_OUTPUT out {ID='45AA75B5-807D-44A3-A781-26AA655CAF23'}:LREAL; END_VAR BEGIN #out:=#in ** 2; END_FUNCTION_BLOCK";
public static void MainWithOutput(ref StringWriter output)
{
// create a SCL script engine instance
Guid clsid = new Guid("4E3797BD-1035-4B54-A4EB-D772803452A2");
var type = Type.GetTypeFromCLSID(clsid, false);
var assist = Activator.CreateInstance(type) as ISCLScriptAssist;
var info = assist.GetScriptInfo(script);
foreach (var varInfo in info.Variables)
{
var propInfo = varInfo as ISCLPropertyInfo;
if (propInfo.Qualifier == SclPropertyQualifier.Input ||
propInfo.Qualifier == SclPropertyQualifier.Output ||
propInfo.Qualifier == SclPropertyQualifier.InputOutput)
{
output.WriteLine("NAME:" + propInfo.Name);
var id = propInfo.GetAttribute("ID");
output.WriteLine("ID" + ":" + id);
}
}
}
}
Variables are represented by the ISCLPropertyInfo interface. So are the UDT fields. Types are represented by the ISCLTypeInfo interface. Block attributes (besides the version) are still not available in the script info!
As you may already notice - the expectation is that the script text contains only one FB/FC/UDT. This is why we have one script per .scl file in the SCL Vault.
The script info is stored in a thread local cache to optimize parsing. So if you don't change the code you'll get it pretty fast.
It is essential for:
SCL editor
Signal I/O exchange
Calling sub scripts
SCL debugger
Comments