# flow-classes.psm1
<#
The engine that runs an individual Workflow.
History:
EC2619 - original from V3 code with major restructuring
#>
<##.mod-doc
flow-classes.psm1
================
_PSEC version 0.0.9_
#### Classes rational ####
This module contains the class definitions for the classes used in the workflow system.
Unlike Java, Powershell cannot handle circular references so this contortion was resorted to. Namesly:
1. Define all classes in 'classes' module that is included.
2. Extend each class with ....Exec class that has logic for each and rely on fact Powershell can find methods at runtime.
#>
<#.std-parms
.parm $cfg Loaded config file
.parm $task Task reference pointer
.parm $par Parent panel (container)
.parm $flow Flow reference pointer
.parm $lab Label reference pointer
#>
using namespace System.Collections;
using module .\gui-classes.psm1
using module .\gui-utils.psm1
set-strictmode -version 2.0
enum TaskType {
Java
Manual
Script
NodeJS
CfgErr
}
<#.md
.desc
A subclass of the Form class used to implement the Workflow GUI
.details
This subclass contains a few more properties used to manage the Workflow GUI
#>
class WflForm : Form {
#---- properties ----
[hashtable] $tasks;
[object[]] $taskList;
[Task] $cycleController; # cycleController - one per form
[hashtable] $cycle; # current settings of cycleController
[Task] $activeTask; # EC8C20 - global pointer
[Flow] $flow; # Related @@Flow
[string] $psecBase; # Where PSEC is running from
[string] $baseLocn; # Where base workflow is located
[int] $helpW = 1000; # default width of help. Override in setup
[int] $helpH = 670; # default height of help. Override in setup
#---- constructors ----
WflForm([System.Object]$peer,[string]$title,[int32]$wid,[int32]$hgt) :base($peer,$title,$wid,$hgt) {
$this.tasks = @{}
$this.taskList = @()
$this.cycle = @{}
}
}
<#.md
.desc
The main Flow object used in the GUI Workflow application described in @@link.wfl-impl
.details
This is extended by FlowExec so that custom objects can be added to a particular workflow.
#>
#>
class Flow { # Container object
#---- properties ----
[Form] $wflForm; #Related @@Form
[string] $wflName; #public name
[string] $cfgPath; #static CFG path
[hashtable] $dynHT; #current values, likely stowed
[string] $dynPath; #dynamic CFG path
[string] $flowPath; #base path to flow task dirs
[string] $libPath; #base path to lib flow task dirs
[PSCustomObject]$libSetup; #lib setup execution script
[PSCustomObject]$flowSetup; #flow setup execution script
[TaskFlowStruc] $tfs; #Global @@Task @@Flow structure
[ArrayList] $tasks; # List of Tasks
[int16] $majorStepSeq;
[int16] $minorStepSeq;
[task] $priorTask;
[task] $minorPriorTask;
#---- constructors ----
Flow([string]$wflName,[TaskFlowStruc]$tfs){
$this.wflName = $wflName;
$this.tfs = $tfs;
$this.tasks = New-Object ArrayList;
}
#---- methods ----
<#.md
.desc
return vital parts of *Flow* object.
#>
[String]ToString() {
$out = 'Flow.{0},[t={1},cfg={2}]' -f $this.wflName,$this.tasks.count,$this.cfgPath
return $out
}
}
<#.md
.desc
This structure (or its extension) is passed to all @@Task script files as the *tfs* parameter.
.details
It is used to contain and pass information between tasks and to provide custom information to
the tasks.
The $globMap is used to contain named structures that may be passed between tasks. The developer is
advised to come up with a meaningful naming strategy so the use of the structures can easily be found with
an editor.
The tfs extension or the class definitions for the $globMap need to be defined in the x-setup.psm where
x is the flow name.
#>
class TaskFlowStruc { # base class that can be extended in setup to provide workflow specific values and methods
[hashtable]$pathFuncs; #: Array of paths
[hashtable]$pathStrs; #: Converted array of paths for optimization
[hashtable]$globMap; #: Array of globals depending on application
TaskFlowStruc() {
$this.pathFuncs = @{};
$this.pathStrs = @{};
$this.globMap = @{};
}
}
<#.md
.desc
combine @@Elem to the the @@Task and @@Flow with a processing function that handles the action.
.details
Used by action processors
#>
#>
class ActionSet {
#---- properties ----
[Flow]$flow;
[Task]$task;
[ScriptBlock]$func;
#---- constructors ----
ActionSet([Flow]$flow,[Task]$task,[ScriptBlock]$func) {
$this.flow = $flow;
$this.task = $task;
$this.func = $func;
}
[void] actionMethod([Elem]$elem,[Object]$ctx) {
hlog("action method invoked func=$($null -ne $this.func)")
if ($null -ne $this.func) {
[void](& $this.func $this $elem $ctx)
}
}
}
<#.md
.desc
Implemention context for a Manual type Task.
.details
Used by manual step processors
#>
class ManStep { #Manual Step
#---- properties ----
[task] $task;
[string] $name;
[int16] $line;
[boolean] $bIgnore; #do not reflect change to Brief
[elem] $chkBox;
#---- methods ----
[String]ToString() {
$out = "ManStep.{0}[{1},{2}]" -f $this.ix,$this.name,$this.bIgnore
return $out
}
}
<#.md
.desc
Exit for a run completion.
.details
This allows for processing to be added when a @@Task ends (such as starting the Excel processor).
#>
class RunDone { #Manual Step
#---- properties ----
[Task] $task;
[string] $outstr;
[hashtable] $parmsHT;
#---- constructors ----
RunDone([Task]$task) {
$this.task = $task;
$this.outstr = "";
$this.parmsHT = @{};
}
#---- methods ----
[String]ToString() {
$out = "RunDone.{0}[{1},{2}]" -f $this.task.type,$this.task.name
return $out
}
}
<#.md
.desc
Implemention context for a @@Task viewing.
.details
This is a placeholder class that allows for a subclass to be defined without
using circular references in multiple modules (which breaks Powershell).
#>
class ViewTask {
#---- properties ----
[Flow] $flow; #Related @@Flow
[Task] $taskPtr #Related @@Task
[Panel] $menuPan #Related left side @@Panel
[Panel] $statPan #Related right side @@Panel
[Div] $anchors #Anchors we might need
#---- constructors ----
ViewTask([Flow]$flow,[Task]$task,[Panel]$menuPan,[Panel]$statPan,[Div]$anchors) {
$this.flow = $flow;
$this.taskPtr = $task;
$this.menuPan = $menuPan;
$this.statPan = $statPan;
$this.anchors = $anchors;
$task.viewTask = $this;
}
}
<#.md
.desc
Main Workflow context
.details
This is the main object of each Workflow step. It also is used to register @@Elem objects
so they can be found by name.
There are many copies of the same @@Panel pattern. A naming scheme would become overly complex.
The altenative, which is what is used, is to register names against the Task and have a lookup mechanism.
The Task has many pointers to other objects.
The Task type property differentiates between the type of task inplemented.
.Notes
The Task is used by both the @@Config and the @@workflow. Consequently phase 1 of the creation of the Task object
populates the linkage. Phase 2 populates the @@GUI components.
#>
class Task {
#---- properties ----
[int16] $seq; # seq of execution
[string] $name; # task name
[string] $desc; # from config. Error desc if configuration error
[string] $title; # from config
[TaskType] $type; # from config
[hashtable] $taskCfg; #task CFG nodez`z`
[string] $stepLabel; # Menu step label
[string] $execlocn; # Exec configuration and status files
[string] $execdef; # Exec definition file pointer
[string] $execOvrDef; # EC1B13: Exec override definition file pointer for lib rtn
[string] $statlocn; # Exec Brief file path
[string] $loglocn; # Exec logfile save locn
[string] $datalocn; # Exec results (data) outputs
[string] $helplocn; # location of help files
[string] $optlocn; # location of saved options settings
[string] $liblocn; # EC0517 Lib location of libary task
[string] $libBase; # EC0517 base Lib location
[hashtable] $methods; # task defined methods
[hashtable] $vbls; # task defined variables (strings)
[PSCustomObject] $custObj; # container for above;
[PSCustomObject] $overObj; # override object
[scriptBlock] $init;
[scriptBlock] $exec;
[scriptBlock] $done;
[scriptBlock] $fail;
[boolean] $bLibTask; # Runs from library EC0516
[Form] $form; # Owning form
[Flow] $flow; # Owning flow
[TaskFlowStruc] $tfs; # Easy access to flow global parms
[ViewTask] $viewTask; # Placeholder class for subclass implementation
[Div] $menuDiv; # where menu is
[Label] $statElem;
[boolean] $bBroken; # errors detected that prohibit running at all
[boolean] $bRunFail; # Run did not complete sucessfully
[boolean] $bRunGood; # Run did complete sucessfully
[boolean] $bRunStale; # Last run is stale
[boolean] $bRunLocked; # Step cannot be run due to dependencies
[boolean] $bRunLapsed; # Dependent step older that step dependent on
#[boolean] $bRunNext; # Step s/b run next or is independant
[boolean] $bIsCurrent; # Step s/b run next or is independant
#[boolean] $bRunnable; # Can run task
#[boolean] $bRunLater; # Dependent or previous not bRunGood
[boolean] $bWhenShow; # true if whenFail active and s/b shown
[boolean] $bShowPath; # EC1B13 - showe Jave class path
[int64] $runTS; # timestamp of last good run or 0
[int32] $defLogWid = 600; # Log width. Can be changed by def- script
[int32] $defLogHgt = 500; # Log Height. Can be changed by def- script
[TabPanel] $tabPan;
[hashtable] $hooks; #elem find mechanism
[hashtable] $htOpts; #EC9C10 - allow params task access to option settings
[Object[]] $options; #dynamic options
[Task[]] $dependsOn; #Prior Tasks this one depends on
[boolean] $bDevpExpose; #Expose WhenFail task for developer
[Task[]] $whenFail; #Only active on failure of X. X immediately preceeds X or prev has same whenFail
[Task] $priorTask; #Prior Tasks (sequential, skips whenFail)
[collections.IDictionary] $brief; #last good brief or null
[int64] $briefTS; #write TS or 0 (unix epoch)
[ManStep[]] $manSteps; #manual steps control block
[scriptBlock] $parseBriefObject;
[Object] $commObj; # EC8C18 - exec started Comm Obj (eg Excel)
[elem] $autoStartFlag; # - auto start flag
[hashtable] $startAppOpts; # - auto start name
[dialog] $prevLogDlg; # EC9B29 - close dlg on run
[div] $hdrDiv; # EC9B29 - hdr panel for task
[div] $bodyDiv; # EC9B29 - body panel for task
[string] $origWinSize; # EC9B29 - detect resize logic needed
#---- constructors ----
Task([string]$name,[string]$strType,[string]$title,[string]$desc) {
$this.methods = [ordered]@{}
$this.vbls = [ordered]@{}
switch($strType) {
"java" {$this.type = [TaskType]::Java}
"manual" {$this.type = [TaskType]::Manual}
"script" {$this.type = [TaskType]::Script}
"nodejs" {$this.type = [TaskType]::NodeJS}
"cfg-err" {$this.type = [TaskType]::CfgErr}
default {throw "$strType task type not implemented"}
}
$this.name = $name
$this.desc = $desc
$this.title = $title
if ($this.desc -eq "") {$this.desc = $this.title}
$this.hooks = @{}
}
#---- methods ----
# create a find-by-name hooking scheme.
[void]hook([elem]$elem) {
if ($this.hooks[$elem.name] -ne $null) {throw "attempt to hook duplicate elem $($elem.name)"}
$this.hooks[$elem.name] = $elem;
#hlog("hooked $elem to $this");
}
[Elem]find([string]$name) {
if ($this.hooks[$name] -eq $null) {throw "attempt to find elem $name failed"}
return $this.hooks[$name];
}
[Elem]optFind([string]$name) {
return $this.hooks[$name];
}
[string]toString() {
return "Task.$($this.name)[$($this.type),$($this.seq)]";
}
}
PSEC is an extensive set of Powershell enhancements that, along with other acknowledged works, is deployed into the public domain subject to the limitations detailed below.
Ackowledgements
Pandoc, the system used to produce this documentation.
More detailed information is found by clicking on the link that triggered this popup