nope/modules/mod-Net-Analyzer/registry/beckhoff.parser.ts
2020-11-05 18:01:47 +01:00

961 lines
28 KiB
TypeScript

/**
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-03-10 14:50:13
* @modify date 2020-06-03 14:04:26
* @desc [description]
*/
import * as handlebars from "handlebars";
import { AbstractParser } from "./default.parser";
import {
IParsedLogic,
IParseableTransitionDescription,
IParseableLogic,
IVariableDescription,
} from "../types/interface";
import {
deepClone,
rsetattr
} from "../../ZISS-TypeScript-Library/src/Object-Methods";
import { replaceAll, padString } from "../../ZISS-TypeScript-Library/src/String-Methods";
import { ILogicNetwork } from "../../ZISS-Network/type/ILogicNetwork";
import { IJsonSchema } from "../../ZISS-TypeScript-Library/src/JSON";
import { IArcExpression } from "../../ZISS-Petri-Net/types/IArcAnnotation";
import { IParseableInstance } from '../types/plc.additional.interfaces';
import { LOGIC_TEMPLATES } from './beckhoff.petri-net.templates';
import { v4 as uuidv4 } from 'uuid';
import { IEC_66113_convertValues, IEC_66113_ConvertType } from '../helpers/IEC66113_helpers';
import { dynamicSort } from '../../ZISS-TypeScript-Library/src/List-Methods';
interface IFunctionAnalyzerHelper {
pathDone: string,
doneRequired: boolean,
pathError: string,
errorRequired: boolean,
}
export interface IParseOptions {
nameOfPlc: string,
nameOfMainFb: string,
nameOfStatesType: string,
nameOfTransitionType: string,
nameOfUsedTransitionsVariable: string,
maxRecursion: number,
maxIterations: number,
additionalDelay: number,
}
export const defaultParseOptions: IParseOptions = {
nameOfPlc: 'plc-0001',
nameOfMainFb: 'FB_MAIN',
nameOfStatesType: 'STATES_DUT',
maxRecursion: 15,
maxIterations: 20,
nameOfTransitionType: 'TRANSITION_PARAMETERS_DUT',
nameOfUsedTransitionsVariable: 'TRANSTIONS_DUT',
additionalDelay: 20
};
export class BeckhoffParser extends AbstractParser {
protected _context: IParsedLogic;
protected _parsableContext: IParseableLogic;
public variableDefinition(context: IParsedLogic): string {
throw new Error("Method not implemented.");
}
/**
* Function which will generate Parameters for the desired Function
* @param task The Taskobject.
*/
protected _generateParameters(task: IParseableTransitionDescription) {
// Only Adapt a Copy
let ret = deepClone(task.params);
const pair = new Map<string, string>();
// Adapt the Parameters.
for (const updateDefinition of task.dynamicParams) {
pair.set(
updateDefinition.usedVariable,
'"' + updateDefinition.usedVariable + '"'
);
rsetattr(
ret,
updateDefinition.parameterPointer,
updateDefinition.usedVariable
);
}
let obj = JSON.stringify(ret, undefined, 4);
// Create The Text which will be used for the Parameters
for (const [key, value] of pair.entries()) {
replaceAll(obj, value, key);
}
return obj;
}
// Array, containing all Elements which should be replaced.
protected _replace: {
regex: RegExp;
replacer: string;
}[] = [
{
regex: /(?<=[ \]\)])or(?=[ \[\(])/g,
replacer: "OR"
},
{
regex: /(?<=[ \]\)])and(?=[ \[\(])/g,
replacer: "AND"
},
{
regex: /(?<=[ \]\)])!=(?=[ \[\(])/g,
replacer: "<>"
},
{
// (?<=[ \]\)])
regex: /not(?=[ \[\(])/g,
replacer: "NOT"
},
{
regex: /(?<=[ \]\)])\|(?=[ \[\(])/g,
replacer: "OR"
},
{
regex: /(?<=[ \]\)])\&(?=[ \[\(])/g,
replacer: "AND"
},
{
regex: /(?<=[ \]\)])true(?=[ \[\(])/g,
replacer: "TRUE"
},
{
regex: /(?<=[ \]\)])false(?=[ \[\(])/g,
replacer: "FALSE"
}
];
/**
* Function, which will be used to adapt the Path.
* @param value
*/
protected _adaptGuard(
guard: string,
simplifiedGuard: string,
OriginalScope: {
[index: string]: {
source: string;
schema: IJsonSchema;
};
},
simplifiedScope: {
[index: string]: {
source: string;
schema: IJsonSchema;
};
},
mappingOriginalToSimplified: Map<string, string>,
mappingSimplifiedToOriginal: Map<string, string>,
mappingSimplifiedToAdapted: Map<string, string>,
mappingAdaptedToSimplified: Map<string, string>,
mappingVariableToRoot: Map<string, string>
) {
let adaptedGuard = simplifiedGuard;
if (adaptedGuard === 'true'){
return 'TRUE';
} else if (adaptedGuard === 'false'){
return 'FALSE';
}
// Firstly get rid off the Elements (Chars, ot replace in the Equation)
for (const data of this._replace) {
adaptedGuard = adaptedGuard.replace(data.regex, data.replacer);
}
// Adapt the used Names
for (const [simple, adapted] of mappingSimplifiedToAdapted) {
adaptedGuard = replaceAll(adaptedGuard, simple, adapted);
}
return adaptedGuard;
}
/**
* Function, which will be used to adapt a Variable Name.
* @param value The original Variable Name
*/
protected _adaptVariableName(
variable: string,
simplifiedName: string,
path: string
) {
return simplifiedName;
}
public functionBlocks: {
[index: string]: {
// Defaultname (Type Name e.g. TON)
name: string,
// Variables used on the PLC Side.
plcInputs: {
name: string,
type: string
}[],
plcOutputs: {
name: string,
type: string
}[],
// Custom Flags for activating / deactivating the Block
start: string,
stop: string,
// Code Snippets for testing errors and success
error: string,
done: string,
// Mapping between the PLC inputs and outputs and system internal inputs / outputs.
inputMapping: {
// Index = Path of the Parameter, value = PLC-Input
[index: string]: string,
}
outputMapping: {
// Index = Path of the Parameter, value = PLC-Input
[index: string]: string,
},
// Optional Function to generate a custom Name.
generateInstanceName?: (
transition: IParseableTransitionDescription,
context: IParsedLogic,
handlebars: any,
name: string,
) => string
// Optional Template, which should be used for the Generation of the Data, during triggering
triggerTemplate?: string,
// Optional Template, which should be used for the Generation of the Code during the Main-Process
callTemplate?: string,
// Flag indicating, whe
requiresInstance: boolean,
deactivateOnDone: boolean
}
} = {
'timer.wait': {
name: 'TON',
done: '{{name}}{{{selector}}}Q',
error: 'FALSE',
inputMapping: {
value: '{{name}}{{{selector}}}PT := INT_TO_TIME({{value}});'
},
outputMapping: {},
plcInputs: [
{
name: 'IN',
type: 'BOOL'
},
{
name: 'PT',
type: 'TIME'
}
],
plcOutputs: [
{
name: 'Q',
type: 'BOOL'
},
{
name: 'ET',
type: 'TIME'
}
],
start: '{{name}}{{{selector}}}IN := TRUE;',
stop: '{{name}}{{{selector}}}IN := FALSE;',
requiresInstance: true,
deactivateOnDone: false
}
}
protected _defaultInstanceName(
transition: IParseableTransitionDescription,
context: IParsedLogic,
handlebars: any,
name: string,
) {
return 'FB_' + transition.id + '_' + name;
}
/**
* Helperfunction, which will generate a Context for a Function.
*
* @protected
* @param {IParseableTransitionDescription} transition
* @returns
* @memberof BeckhoffParser
*/
protected _getFunctionHelperContext<T>(transition: IParseableTransitionDescription) {
const _ret: IFunctionAnalyzerHelper = {
// Path to the Done Variable, if available.
pathDone: this._context.mappingSimplifiedToAdapted.get(
this._context.mapPathToSimplifiedName.get(
transition.id + ".function.output.done"
)
),
// Flag, indicating, whether a Done is requested
doneRequired: this._context.mapPathToSimplifiedName.has(
transition.id + ".function.output.done"
),
// Path to the Variable in which the corresponding error could be found.
pathError: this._context.mappingSimplifiedToAdapted.get(
this._context.mapPathToSimplifiedName.get(
transition.id + ".function.output.error"
)
),
// Flag, indicating, whether the Error of this Function is Checked or not.
errorRequired: this._context.mapPathToSimplifiedName.has(
transition.id + ".function.output.error"
)
}
return _ret as any as T & IFunctionAnalyzerHelper;
}
protected _timeAdaption(placeId: string) {
const _this = this;
// Get the Place matched to the ID
const place = this._context.places.filter(p => p.id === placeId)[0];
// Extract all involved Transitions.
const transitions: IParseableTransitionDescription[] = [
... place.consumedBy.map(transId => _this._parsableContext.transitions.filter(trans => (trans.id === transId))[0]),
... place.producedBy.map(transId => _this._parsableContext.transitions.filter(trans => (trans.id === transId))[0]),
... place.requiredBy.map(transId => _this._parsableContext.transitions.filter(trans => (trans.id === transId))[0]),
... place.avoidedBy.map(transId => _this._parsableContext.transitions.filter(trans => (trans.id === transId))[0])
];
const _context = {
transitions
}
// Create a Template to Test Execute the Function
const _template = `
{{#each transitions}}
{{methodName}}_ADAPT_TIMES(iCurrentTime);
{{/each}}
`
return handlebars.compile(_template)(_context);
}
/**
* List containing Functions. This can be used to create a custom code generation-Function
* per Function. Therefore the function will be linked by the index.
*
* @memberof BeckhoffParser
*/
public functions: {
[index: string]: {
generateFunctionTriggerCode: (
transition: IParseableTransitionDescription,
context: IParsedLogic,
handlebars: any
) => string,
generateInstanceStructure?: (
transition: IParseableTransitionDescription,
context: IParsedLogic,
handlebars: any
) => IParseableInstance
}
} = {
"plc.set": {
generateFunctionTriggerCode: (transition, context, handlebars) => {
// TODO: Update the Setters and Getters
// Mark the Task as Done.
// Test Object. Is used to update the Code
const _context = this._getFunctionHelperContext<{
entries: any[]
}>(transition);
_context.entries = [];
// Push all Ports to the Entries, which will be set.
for (const port in transition.params) {
_context.entries.push({
port: 'GVL_IOS.' + port,
value: IEC_66113_convertValues(transition.params[port])
});
}
// Although push all "dynamic" Variables to the Entries.
for (const param of transition.dynamicParams) {
_context.entries.push({
port: param.parameterPointer,
value: this._adaptVariableName("", param.usedVariable, "")
});
}
// Use a Template, which will add
const template = `
{{#if doneRequired}}
{{pathDone}} := FALSE;
{{/if}}
{{#if errorRequired}}
{{pathError}} := FALSE;
{{/if}}
{{#each entries}}
{{port}} := {{value}};
{{/each}}
{{#if doneRequired}}
{{pathDone}} := TRUE;
{{/if}}
{{#if errorRequired}}
{{pathError}} := FALSE;
{{/if}}
`;
return handlebars.compile(template)(_context);
},
},
"math.calculate": {
generateFunctionTriggerCode: (transition, context, handlebars) => {
return "calc";
},
}
};
/**
* Function, which will be used to generate Function Code.
* @param taskData
*/
protected _getFunctionTriggerCode(
_transitionDescription: IParseableTransitionDescription
) {
const transition = deepClone(_transitionDescription);
if (transition.functionName && this.functions[transition.functionName]) {
const _func = this.functions[transition.functionName].generateFunctionTriggerCode;
return _func(transition, this._context, handlebars);
} else if (this.functionBlocks && this.functionBlocks[transition.functionName]) {
const _func = this.functionBlocks[transition.functionName];
// Generate the Name.
const _name = _func.generateInstanceName ? _func.generateInstanceName(transition, this._context, handlebars, _func.name) : this._defaultInstanceName(transition, this._context, handlebars, _func.name);
// Generate a Helper Context Element.
const _context = this._getFunctionHelperContext<{
entries: any[],
doneGetter: string,
errorGetter: string,
start: string,
startReset: string,
stop: string,
stopReset: string,
}>(transition);
_context.entries = [];
// Push all Ports to the Entries, which will be set.
for (const param in transition.params) {
if (_func.inputMapping[param]) {
_context.entries.push({
mapping: handlebars.compile(_func.inputMapping[param])({
value: transition.params[param],
name: _name,
selector: '_'
})
});
}
}
// Add all Dynamic Values to the Test.
for (const param of transition.dynamicParams) {
if (_func.inputMapping[param.parameterPointer]) {
_context.entries.push({
mapping: handlebars.compile(_func.inputMapping[param.parameterPointer])({
value: param.usedVariable,
name: _name,
selector: '_'
})
});
}
}
// Extend the Context.
_context.doneGetter = handlebars.compile(_func.done)({
name: _name,
selector: '.'
});
_context.errorGetter = handlebars.compile(_func.done)({
name: _name,
selector: '.'
});
_context.start = _func.start ? handlebars.compile(_func.start)({
name: _name,
selector: '_'
}) : '';
_context.startReset = _func.start ? handlebars.compile(_func.start)({
name: _name,
selector: '_'
}) : '';
_context.stop = _func.stop ? handlebars.compile(_func.stop)({
name: _name,
selector: '_'
}) : '';
_context.stopReset = _func.stop ? handlebars.compile(_func.stop)({
name: _name,
selector: '_'
}) : '';
// Use a Template, which will add
const template = _func.triggerTemplate || `
{{#if doneRequired}}
// Mark execution as Active
{{{pathDone}}} := FALSE;
{{/if}}
{{#if errorRequired}}
// Reset the Error
{{{pathError}}} := FALSE;
{{/if}}
// Stop the Block.
{{{stopReset}}}
updateInstances();
// Activate the Block
{{{start}}}
// Parameter assignment
{{#each entries}}
{{{mapping}}}
{{/each}}
updateInstances();
{{#if doneRequired}}
// Update the State of the Execution of the Block
{{{pathDone}}} := {{{doneGetter}}};
{{#if deactivateOnDone}}
// Deactivate the Function-Block:
IF {{{pathDone}}} THEN
{{{stop}}}
END_IF;
{{/if}}
{{/if}}
{{#if errorRequired}}
// Update the Error Flag
{{{pathError}}} := {{{errorGetter}}};
{{/if}}
`;
return handlebars.compile(template)(_context);
}
return "// Unkown Function. ==> Using remote Broker";
}
/**
* Function, which will be used to generate Function Code.
* @param taskData
*/
protected _getFunctionCallCode(
_transitionDescription: IParseableTransitionDescription
): IParseableInstance | null {
const transition = deepClone(_transitionDescription);
if (transition.functionName && this.functions[transition.functionName] && this.functions[transition.functionName].generateInstanceStructure) {
const _func = this.functions[transition.functionName].generateInstanceStructure;
return _func(transition, this._context, handlebars);
} else if (this.functionBlocks && this.functionBlocks[transition.functionName] && this.functionBlocks[transition.functionName].requiresInstance) {
const _func = this.functionBlocks[transition.functionName];
// Generate the Name.
const _name = _func.generateInstanceName ? _func.generateInstanceName(transition, this._context, handlebars, _func.name) : this._defaultInstanceName(transition, this._context, handlebars, _func.name);
// Generate a Helper Context Element.
const _context = this._getFunctionHelperContext<{
inputs: {
instanceVar: string,
accessor: string,
}[],
outputs: {
instanceVar: string,
accessor: string,
}[],
hasInputs: boolean,
hasOutputs: boolean,
stop: string,
doneGetter: string
}>(transition);
_context.hasInputs = _func.plcInputs ? _func.plcInputs.length > 0 : false;
_context.hasOutputs = _func.plcOutputs ? _func.plcOutputs.length > 0 : false;
_context.inputs = [];
_context.outputs = [];
// Extend the Context.
_context.doneGetter = handlebars.compile(_func.done)({
name: _name,
selector: '_'
});
_context.stop = _func.stop ? handlebars.compile(_func.stop)({
name: _name,
selector: '_'
}) : '';
for (const input of _func.plcInputs) {
_context.inputs.push({
accessor: _name + '_' + input.name,
instanceVar: _name + '.' + input.name
})
}
for (const output of _func.plcOutputs) {
_context.outputs.push({
accessor: _name + '_' + output.name,
instanceVar: _name + '.' + output.name
})
}
// Use a Template, which will add
const template = _func.callTemplate || (`
{{#if hasInputs}}
// Set the Inputs
{{#each inputs}}
{{instanceVar}} := {{accessor}};
{{/each}}
{{/if}}
{{#if hasOutputs}}
// Set the Outputs provided by the FB
{{#each outputs}}
{{accessor}} := {{instanceVar}};
{{/each}}
{{/if}}
{{#if doneRequired}}
// Update the State of the Execution of the Block
{{{pathDone}}} := {{{doneGetter}}};
{{#if deactivateOnDone}}
// Deactivate the Function-Block:
IF {{{pathDone}}} THEN
{{{stop}}}
END_IF;
{{/if}}
{{/if}}
{{#if errorRequired}}
// Update the Error Flag
{{{pathError}}} := {{{errorGetter}}};
{{/if}}
`);
const _ret: IParseableInstance = {
fBName: _func.name,
callCode: handlebars.compile(template)(_context),
inputs: _func.plcInputs.map(item => {
return {
name: _name + '_' + item.name,
type: item.type
}
}),
outputs: _func.plcOutputs.map(item => {
return {
name: _name + '_' + item.name,
type: item.type
}
}),
instanceName: _name,
transitionName: transition.id,
}
return _ret;
}
return null
}
protected _convertInput(definition: {
// Amount of Tokens that will be consumed (Result must be greater the minTokens)
consumed: IArcExpression;
placeId: string;
type:
| "expression"
| "inhibitor"
| "multiexpression"
| "test"
| "value"
| "variable";
minTokens: number;
}) {
return {
placeId: definition.placeId,
minTokens: definition.minTokens,
tokensToRemove: 1
};
}
protected _convertOutput(definition: {
// Amount of Tokens that will be produced
produced: IArcExpression;
placeId: string;
type:
| "expression"
| "inhibitor"
| "multiexpression"
| "test"
| "value"
| "variable";
maxTokens: number;
}): {
// Amount of Tokens that will be produced
placeId: string;
maxTokens: number;
tokensToAdd: number;
} {
return {
placeId: definition.placeId,
maxTokens: definition.maxTokens,
tokensToAdd: 1
};
}
protected _getAccessorOfGuardVariable(data: IVariableDescription){
// The Input of the Path, based on a Variable is defined as
// PLC-Name.[input/output].[module].[name]
// plc_0003.input.GVL_IOS.bInput24VMotorenIO
const splitted = data.path.split('.').slice(2);
return splitted.join('.');
}
protected _templates: {
[index: string]: string;
} = LOGIC_TEMPLATES;
/**
* Function, which will generate the MainLoop.
* @param network
*/
mainLoop(network: ILogicNetwork): string {
this.linkTemplates(network);
const render = handlebars.compile(this._templates.mainTemplate);
return render(this._parsableContext);
}
public linkTemplates(network: ILogicNetwork, ...args) {
super.linkTemplates();
// Reference to this object.
const _this = this;
// Register Functions
handlebars.registerHelper("getFunctionTriggerCode", (taskData) =>
_this._getFunctionTriggerCode(taskData)
);
// Register Functions
handlebars.registerHelper("uuid", () => '{' + uuidv4() + '}');
handlebars.registerHelper("getAccessorOfGuardVariable", (data) =>
_this._getAccessorOfGuardVariable(data)
);
handlebars.registerHelper("timeAdaption", (placeId) =>
_this._timeAdaption(placeId)
);
// Generate the Context
this._context = this.parseLogic(network);
this._parsableContext = this.getParseableLogic(network);
}
protected _parseLogic: IParsedLogic
/**
* Helper Function, to Generate the desired Logic.
* @param _network
*/
parseLogic(_network: ILogicNetwork) {
if (this._network != _network) {
const ret = super.parseLogic(_network);
for (const _var of ret.subscribedVariables){
_var.type = IEC_66113_ConvertType(_var.type) as any;
}
for (const _var of ret.variables){
_var.type = IEC_66113_ConvertType(_var.type) as any;
}
this._parseLogic = ret;
}
return this._parseLogic;
}
/**
* Function to create Project related Files.
*
* @param {ILogicNetwork} network The Network containing the Logic
* @param {IParseOptions} [options=defaultParseOptions] Additional Options for Parser
* @returns
* @memberof BeckhoffParser
*/
public createProjectFiles(network: ILogicNetwork, options: IParseOptions = defaultParseOptions) {
// Reference to the Own Object.
const _this = this;
// Internally the Templates
this.linkTemplates(network);
const codeFiles: {path: string, code: string}[] = [
{
path: 'POUs/'+options.nameOfMainFb + '.TcPOU',
code: this.generateMainFB(network,options),
},
{
path: 'DUTs/'+options.nameOfStatesType + '.TcDUT',
code: this.generateStatesType(network,options),
},
{
path: 'DUTs/'+options.nameOfTransitionType + '.TcDUT',
code: this.generateTransitionType(network,options),
},
{
path: 'DUTs/'+options.nameOfUsedTransitionsVariable + '.TcDUT',
code: this.generateTransitionsVariable(network,options),
}
];
// Array, cotaining all Files. These will be packed into a ZIP-File Later on.
const files: {path: string, code: string}[] = [
... codeFiles,
{
path: 'PlcTask.TcTTO',
code: handlebars.compile(this._templates.plcTask)(this.getParseableLogic(network))
},
{
path: 'Testcode.plcproj',
code: handlebars.compile(this._templates.plcProject)({codeFiles: codeFiles.map(item => {
return {
path: replaceAll(item.path, '/', '\\')
}
})})
}
]
return files;
}
protected _network: ILogicNetwork;
protected _options: IParseOptions
protected _getParseableLogic: IParseableLogic;
public getParseableLogic(network: ILogicNetwork, options: IParseOptions = defaultParseOptions){
if (this._network != network && this._options != options) {
this._network = network;
this._options = options;
const ret = super.getParseableLogic(network);
// Name of the Function Name.
(ret as any).options = options;
(ret as any).instances = [];
const _this = this;
const _adapt = (trans, length) => {
// Adding a Vaild Method Name.
(trans as any).methodName = 'T' + padString(trans.idx,length,true) + '_' +trans.id;
(trans as any).hasInputs = trans.consumed.length > 0;
(trans as any).hasOutputs = trans.produced.length > 0;
(trans as any).testOutputs = trans.produced.filter(place => place.maxTokens > 0).map(item => Object.assign(deepClone(options), item));
(trans as any).hasTestOutputs = (trans as any).testOutputs.length > 0;
(trans as any).hasTriggers = trans.triggers.length > 0;
(trans as any).hasLocks = trans.locks.length > 0;
(trans as any).hasReleases = trans.releases.length > 0;
// Transmit the Options
(trans as any).options = options;
for (const name of ['avoided', 'produced', 'testOutputs', 'consumed', 'required', 'triggers']){
trans[name] = trans[name].map(item => Object.assign({options},item));
}
// Test if Instance Code must be generated
if (trans.functionName && (
(_this.functions && _this.functions[trans.functionName] && _this.functions[trans.functionName].generateInstanceStructure) ||
(_this.functionBlocks && _this.functionBlocks[trans.functionName] && _this.functionBlocks[trans.functionName].requiresInstance)
)
) {
// Instance Code must be generated
(ret as any).instances.push(_this._getFunctionCallCode(trans));
}
}
for (const [idx,trans] of ret.transitions.entries()){
_adapt(trans,ret.transitions.length);
}
ret.transitionsSortedByPriority = ret.transitions.sort(dynamicSort('priority'))
for (const [idx,trans] of ret.actions.entries()){
_adapt(trans,ret.transitions.length);
}
// Transmit the Options
for (const name of ['places', 'initialMarking', 'subscribedVariables', 'transitions', 'variables']){
ret[name] = ret[name].map(item => Object.assign({options},item));
}
this._getParseableLogic = ret;
}
return this._getParseableLogic;
}
public generateMainFB(network: ILogicNetwork, options: IParseOptions = defaultParseOptions): string {
this.linkTemplates(network);
return handlebars.compile(this._templates.mainTemplate)(this.getParseableLogic(network, options));
}
public generateStatesType(network: ILogicNetwork, options: IParseOptions = defaultParseOptions): string {
this.linkTemplates(network);
return handlebars.compile(this._templates.statesStructTemplate)(this.getParseableLogic(network, options));
}
public generateTransitionsVariable(network: ILogicNetwork, options: IParseOptions = defaultParseOptions): string {
this.linkTemplates(network);
return handlebars.compile(this._templates.transitionVariableTemplate)(this.getParseableLogic(network, options));
}
public generateTransitionType(network: ILogicNetwork, options: IParseOptions = defaultParseOptions): string {
this.linkTemplates(network);
return handlebars.compile(this._templates.transitionStructTemplate)(this.getParseableLogic(network, options));
}
}