11 Jul, 2019

Organizing Automation

It is quite tempting to throw all of your EKL code in a single Knowledgeware object– Action, Rule, KnowledgePattern, or Reaction. While convenient, this practice brings many drawbacks. Instead of describing what these are, I will focus instead on describing some programming principles and the benefits they bring. I learned the value of these principles from working on several automation tools, software development projects, training and teaching, and collaboration with team members. Needless to say that such principles, and more generally software programming patterns, stem from the work of Christopher Alexander. Here are some of the main programming principles I try stick to:

  • Separation of Concerns: code should be distinct; focused on achieving clearly defined independent goals. This leads to developing modular, reusable, and easier to main code.

  • Don’t repeat yourself: code should not be repeated elsewhere in a solution. If a body of code is repeated, then it better be made into its own module (related to the above principle).

  • Avoid early optimization: focus on generating the output needed, then worry about optimization.

  • Least Astonishment: code should be clear, and its signature (method names, class names, etc.) should clearly indicate what it does. Don’t call a method point_generator when all it does is create curves. Of course, the same is true for variables.

  • Design by contracts: before writing code, or at least while writing it, identify its least required inputs, optional ones, and returned outputs.

  • Immutability: input data should remain unchanged. This comes from functional programming, which is similar to math functions. For example, y=f(x). Here, the value of x is consumed by function f, which returns a new value, y. The value of x remain unchanged. This practice makes easier to think about, trace, and debug code.

It is technically easier to implement the above principles when writing code in programming languages such as VBA and C-sharp because they support Interfaces to define contracts and Classes to make Objects. EKL is a scripting language designed to create functions and methods that live inside Knowledgeware features. That said, we have a few facilities offered in CATIA to enable adhering to the above principles.

Knowledge Engineering Specifications (KES)

Knowledgeware features can exist in KES, Shape, Drawing, and Skeleton Representations. There is one caveat, KESs do not store UDFs, PowerCopies, or knowledge Patterns because these are geometric features (or are used to generate geometry)–not something that KES is designed for. This is not an issue because we can store geometric features in Shapes. Since Physical Products can hold multiple Representations (whether Shared or Aggregated), KESs are perfect for high level grouping, reuse, and distribution of automations and Resources Tables.

  • Products can hold multiple Representations even of similar type. For Example: Product holding 3 KESs, 2 Shapes, 4 Drawings, etc. Parts, however, can only hold one of each type.

Relationship And Parameter Sets

We can insert and nest automation Knowledgeware Features in Relationship Sets, which enables yet another level of organization inside Representations. There can be only one Relationship Set root inside a Representation. Once created, we can add and nest as many as needed. The same is true for Parameter Sets. Relationship Sets hold Knowledgeware Objects/, while Parameter Sets hold Parameters– Value Pointer.

Actions As Modules

We can treat Actions as functions that we can in different sequences to produce results. EKL allows calling Actions from other Actions, Rules, Reactions, and KnowledgePatterns through the .Run(...) method. If an Action takes arguments in the UI, we’ll need to pass those in the same order transposing them from Top>>Down (from the Action UI) to Left>>Right ( in code). For example MyAction.Run(firstFromTop, secondFromTop, ... lastFromTop). Actions do not have a return type. However, they can set values of Literal and Feature objects. This is similar to sending objects by reference in other languages–which is an imperative programming style.

Here is an example:

//define the advisor action
let addPoints(AdvisorAction)
//set it to equal other actions (in the tree)
addPoints =...

//declare and initialize a list variable
let myPoints(List)

//call the Action and pass the list. 
//Actions don't have to have inputs.
addPoints.Run(myPoints)

//we expect the list to come back filled with points..
let p(Point)
for p inside myPoints{
    Notify(p.Name)  
}


//lets set the value of a literal
let myVal(Integer)
let setMyValueToZero(AdvisorAction)
setMyValueToZero =...

myVal=10
//myVal is 10
Notify("MyValue is #", myVal)

//let's set the value to zero
setMyValueToZero.Run(myValue)
//now, myVal is 0
Notify("MyValue is #", myVal)



//lets set the value of a feature
let myCircle(Circle)
let makeCircle(AdvisorAction)
makeCircle = ...

let radius=10mm
let x = 0mm
let y =0mm
let myPlane(Plane)
myPlane = ...
makeCircle.Run( myCircle,x, y, z, radius )
//now myCircle has been created.

Daisy-chain Those KnowledgePatterns

Similar to Actions, we can break a KnowledgePattern (KPT) into many KPTs, each focused on generating a smaller set of outputs. Then, we can daisy chain KPTs (within a Representation) so that one KPT produces the drivers for subsequent KPTs. This approach works best if we reference the output generated in the Specification Tree (for example in Geometrical set) as opposed to the output listed under the KPTs. Doing this, allows users to change drivers for subsequent KPTs if needed without breaking the automation. For example, trying to add or remove points from the Geometrical set being referenced by other KPTs. This workflow can be made even more flexible by setting KPTs execution mode to Manual. Execution mode define how KPTs respond to changes to its driving inputs. If set to Automatic, it is advised to set the Priority attribute for KPTs to ensure the correct order of execution is followed. KPTs with lower Priority value will execute first.

Synchronizing EKL And VBScript

Should you need APIs not exposed to EKL, you can easily call dotnet from EKL using VBScript, which can be triggered by the .Run(...) command. That said, integrating VBScripts within Knowledgeware-EKL workflow requires synchronization. That is, when VBScript is followed by other EKL automation.

Synchronization is required because each Language runs on its own thread. Hence, one might start before the previous one is finished. Further, there is no direct method to pass arguments from VBScript to EKL. This is because VBScript are written inside Subs (Subroutines), which are blocks of code that don’t return values. We can solve both limitations (synchronization and sharing of data) using the pattern below:

  • A simpler implementation to consider is to call VBScripts at the end of an automation sequence where no EKL code needs to run afterwards.

  • Since EKL can only read text or Excel files from disk, we’re limited to producing these file formats from VBScript on disk. However, if producing to the Specification Tree, there will be greater area for data handover from VBScript to EKL.

Resource Tables

We can use Resource Tables to organize automation objects as described in the post on Making use of Resource Tables/. Tables help organize, reuse, distribute, and scale automation solutions across teams in an organization.

Custom EKL Functions

We can build functional and imperative methods as custom EKL libraries, which is more effective than using Actions. However, creating custom functions requires an additional KHC license. Take a look at this post to evaluate the benefits for your use case.

Variable UDF Outputs

UDFs are instantiated as Features in the Specification Tree— just like a Curve or a Point object. However, UDFs bring two major advantages: they supported nested, secondary, Outputs; and they can be made intelligent by embedding EKL code. Users often require producing UDFs with varying number of geometric objects, but UDF definitions can only have a fixed number of Outputs. While this looks like a limitation, it actually is far from it. Here is why:

  • First, it ensures the integrity of the UDF definition. For example, if the source definition has two Outputs. Any consumer of UDF features created from that definition will always find the two Outputs.

  • Second, we can easily produce Outputs with a varying number of geometric objects by using an EKL rule to assemble them. The EKL API is assemble(itemsList). Since UDFs will only yield what we designate as Outputs, we can choose what to assemble a single Output as needed.

One might ask: “how can I control properties of a one of the items making up the Join object?”. We can easily do this by a combination of Output Parameters (exposed to end user) and EKL Rules embedded in the UDF. Take a look at this pattern:

Closing Remarks

My inspiration for writing this post came largely from Christopher Alexander’s Pattern Language, and Robert Woodbury’s Elements of Parametric Design. What I propose above are basic ideas I learned from practice. I’ll need to develop them further in order to call them Patterns. Hence, the post is titled “Organizing Automation”.

Tags: , , , , , , , ,

About : Maher Elkhaldi

Maher Elkhaldi is a senior applications engineer at Tesla Motors. He founded the 3DXAutomation blog to help make knowledge of programming CATIA easier to find, and contribute to the open-source community.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.