Montura Consulting   Research & Development
Component Relationship
Instant Flowchart

Instant Flowcharts are exclusive to Repository Relationship Programming.

  • Step-oriented error detect logic is a big advantage during software development. Even the bare-bones basic information is enough to show exactly where the error/problem occurred.
  • Error detect logic is tied together, application wide. This becomes indispensable when debugging in production. Error-detect logic pinpoints the error stops the application immediately, without generating the cascade of errors like Base/SAS.
  • Detailed flowchart information can be stored in a dataset and compared against production results. Both information streams can be compared with PROC COMPARE, providing instant feedback on system status and expected response.
  • Content may vary from session to session, showing every program in the application run. The scope of information can customized to cover the entire application, a single feature, or a single subroutine.

After loading the application into memory, a standard routine pulls identity information from each instance. A bare-minimum implementation would show the 4-level name and execution sequence of each SAS object that is used in the application. .The physical location of the SAS catalog, a detailed description of the application purpose, usage notes, and a description for each component can also be presented.

RRP applications are designed so that some flowchart information can be stored in SAS catalogs. Information stored in a SAS catalog can be extracted with SQL. Machine readable SDLC documentation is invaluable when SAS programs are maintained by a series of programmers over a period of time.


Displayed in the SAS Log.

EPA Groundwater Compliance Flowchart
Path: c:\commenvct\padwis
Catalog: GWCOMPLIANCE

The following programs execute in the following sequence.
Required Programs: 29
Optional Programs: 0

Sequence: 01 / 100, Program: COMPGUI.GWCOMPLIANCE.DATES.CLASS
Sequence: 02 / 200, Program: COMPGUI.GWPOPULATION.FOURLOG.CLASS
Sequence: 03 / 200, Program: COMPGUI.GWPOPULATION.TRMTPROC.CLASS
Sequence: 04 / 201, Program: COMPGUI.GWPOPULATION.EPTP.CLASS
Sequence: 05 / 201, Program: COMPGUI.GWPOPULATION.SOURCETP.CLASS
Sequence: 06 / 202, Program: COMPGUI.GWPOPULATION.UNTREATEDGROUND.CLASS
Sequence: 07 / 300, Program: COMPGUI.GWPOPULATION.POPULATION4LOG.CLASS
Sequence: 08 / 301, Program: COMPGUI.GWPOPULATION.RTM.CLASS
Sequence: 09 / 302, Program: COMPGUI.GWPOPULATION.RTMFOURLOGCOMPUTED.CLASS
Sequence: 10 / 400, Program: COMPGUI.GWCOMPLIANCE.RTMSEASON.CLASS
Sequence: 11 / 401, Program: COMPGUI.GWCOMPLIANCE.RTMSAMPLE.CLASS
Sequence: 12 / 450, Program: COMPGUI.GWCOMPLIANCE.CALENDAR4LOGDAYS.CLASS
Sequence: 13 / 450, Program: COMPGUI.GWCOMPLIANCE.CALENDARCONTAMWINDOW.CLASS
Sequence: 14 / 450, Program: COMPGUI.GWCOMPLIANCE.CALENDAREPWINDOW.CLASS
Sequence: 15 / 450, Program: COMPGUI.GWCOMPLIANCE.CALENDAROFFSEASONDAYS.CLASS
Sequence: 16 / 475, Program: COMPGUI.GWCOMPLIANCE.CALENDARSHUTDOWNABSENT.CLASS
Sequence: 17 / 500, Program: COMPGUI.GWCOMPLIANCE.CALENDARREQUIREDDAYS.CLASS
Sequence: 18 / 550, Program: COMPGUI.GWCOMPLIANCE.SAMPLEACTIVE.CLASS
Sequence: 19 / 700, Program: COMPGUI.GWCOMPLIANCE.SAMPLEABSENTDETECT.CLASS
Sequence: 20 / 701, Program: COMPGUI.GWCOMPLIANCE.SAMPLEABSENTSUMMARY.CLASS
Sequence: 21 / 702, Program: COMPGUI.GWCOMPLIANCE.VIOLATIONSAMPLEABSENT.CLASS
Sequence: 22 / 725, Program: COMPGUI.GWCOMPLIANCE.CALENDARREQUIREDXTRA.CLASS
Sequence: 23 / 750, Program: COMPGUI.GWCOMPLIANCE.BELOWLIMITDETECT.CLASS
Sequence: 24 / 751, Program: COMPGUI.GWCOMPLIANCE.BELOWLIMITMATRIX.CLASS
Sequence: 25 / 752, Program: COMPGUI.GWCOMPLIANCE.BELOWLIMITROLLUP.CLASS
Sequence: 26 / 753, Program: COMPGUI.GWCOMPLIANCE.VIOLATIONTIMELIMIT.CLASS
Sequence: 27 / 800, Program: COMPGUI.GWCOMPLIANCE.VIFC.CLASS
Sequence: 28 / 900, Program: COMPGUI.GWCOMPLIANCE.VIFCSTATUSLOW.CLASS
Sequence: 29 / 901, Program: COMPGUI.GWCOMPLIANCE.VIFCSTATUSABSENT.CLASS

FIGURE #1

With additional information, for the programmer can answer why each program completed successfully, failed, or was skipped over without executing. Objects provide a structure where SAS programmers can select a point in the process where it makes sense to save data vector information, flag settings, and parameters values to a SAS dataset for auditing.


Repository Relationship

A repository is a SAS catalog that contains SAS objects that fulfill a specific purpose, function, or logical step.

Repository content is determined based on how the application must execute after the instances are in memory and ready to execute. Every object in the repository is instantiated and loaded into memory as a collective. In contrast, old programming design treats a repository as a library where each object must be individually selected or referenced by physical name to included.

A collective is designed as a series of physically independent instances (programs). Each collective may be replicated any number of times for parallel processing / multiple scenario response.

Each instance is related to others in the same repository in many different ways that include:

  • Sequence of position in the execution cue (the most common).
  • A singleton that can be invoked by name, but only after instantiation (perfect for functions with version identifiers).

Each repository of instances relates to the application through the Data Relationship to obtain the following:

  • Data - SAS dataset, Oracle table, etc.
  • Parameters set by any other instance, anywhere in the application.
  • Flag values set by any other instance, anywhere in the application.
  • External metadata values.

Features that are unique to Repository Relationship Programming:

Each repository of instances and be repetitively included for parallel threads within the same application - the application does not need to be specifically designed for parallel threads.

Each repository of instances can be selectively removed, without crashing the application.

 

SAS Object Application Example - one application, four repository.

  • Groundwater - contains the main driver.
  • groundwater100 - determine sample frequency for each public water system.
  • groundwater200 - examine data to determine when a water system operator fails to sample for a specified contaminant.
  • groundwater300 - issue one violation per failure unless the operator was issued a waiver.


Figure #2


Repository Object Instantiation

The term "object" means a SAS source code that has been coded and compiled, resulting in the creation of second file of the same name with a suffix of ".class".

The term "instance" means that a SAS object has been loaded into memory and turned into an executable. The object itself is NOT executable. Additional CPU and operating bytecode must be inserted during instantiation process or the instruction set within the object will be meaningless to the CPU and operating system.

Relational objects are physically independent before instantiation.

For beginners, the fast and easy way to get started is use a class-level property with a hardcoded number that signals each position in a serial relationship. In the example below, the programmer has objects stored in two SAS catalogs. A class level property named RUNSEQUENCE is hardcoded with the sequence each should execute. See Figure #1 above to see the numerical order of each program side-by-side with the sequence number coded by the programmer.

Order of Operations

  1. PROC SQL creates a list of every SAS object in two SAS catalogs.
  2. INSTANCE turns each object into an executable instance.
  3. INSERTO stores the instance identifier.
  4. SORTLIST sorts every instance identifier in the application to get correct execution sequence, based on the RUNSEQUENCE property.
public list component / (sendEvent='N'); 
runInterface: method;      
dcl num dset;
dcl char name1 name2 name3 name4;
dcl object thisProgram;

submit continue sql;
create table work.temp as
select *
from sashelp.vcatalg
where libname='EPA' and
memname in('GWCOMPLIANCE','GWPOPULATION') and
objtype='CLASS';
quit;
endsubmit;

dset=open('work.temp', 'i');
do while (fetch(dset)=0);
name1=getvarc(dset, varnum(dset, 'libname'));
name2=getvarc(dset, varnum(dset, 'memname'));
name3=getvarc(dset, varnum(dset, 'objname'));
name4=getvarc(dset, varnum(dset, 'objtype'));

thisProgram=instance(loadclass(name1||'.'||name2||'.'||name3||'.'||name4));
inserto(component, thisProgram, -1, put(thisProgram.runSequence, z4.));
end;
close(dset);
delete('work.temp');
sortlist(component, 'name');
endmethod;

FIGURE #2


Repository Instance Execution

This execution stage is traditionally the most complex portion of the programming effort. Some parts of the application become so complex with minutia that some code is impossible to understand without data flow diagrams, flowcharts, and language debuggers. A bad situation is made even worse with bad programming technique: spaghetti code, GOTO statements, and blocks of code that are no longer used but remain because no one remembers why it was coded in the first place.

"If the devil is in the details, clean code keeps the devil under control."

 

Step #1: Remove any Potential for Main Driver Mess

Limit the main driver to a single purpose. - execute each instance, one after another, in sequence. Any decision to execute on a conditional basis does not belong in this block of logic. There only possible error that can occur in the main driver, it occurs when the programmer fails to create a method named runInterface in one of the repository programs.

The Repository Relationship Programming specification uses the name runInterface as the naming for convention for "main driver" logic in every object, including SAS/Frame widgets.

public list component / (sendEvent='N');
public list gMessage  / (sendEvent='N');
runInterface: method;                                                
dcl num xObject;
dcl object thisObject;

do xObject=1 to listlen(component) while (listlen(gMessage)=0);
thisObject=getitemo(component, xObject);
thisObject.runInterface();
end;
endmethod;
 

Step #2: Identify Error Location

Use one or more global channels (i.e. gMessage) to evaluate "if" the application should terminate in response to errors, missing data, or unexpected data combination that is detected in any repository instance. Think of this as the power to know every possible error location, in terms of repository, program, and method.

  • The automatic variable DESCRIPTION is the four-level qualifier of the SAS object.
  • The automatic variable _METHOD_ is the name of the method.

Two data elements pinpoint the exact error location, regardless of how many lines of code are used in the SAS application.

interface1: method / (description='Samples for EP that must MUST monitor for contaminents');
submit continue sql;
create table activeIndex as
select a.compkey,
a.epkey,
a.logdemo,
a.windowdate format=mmddyy10.,
b.sampdate format=mmddyy10.,
b.samptype,
b.sampid
from calendarContamWindow a
left join sampleSystem b
on a.compkey=b.sfkey and
a.windowdate=b.sampdate
where a.compkey in(select distinct compkey
from rtmFourlogSystem)
order by compkey, windowdate, samptype;
quit;
endsubmit;

if symgetn('sqlrc') then
insertc(gMessage, description||' '||_method_, -1);
endmethod;

The following code is common to all SAS objects.

The error message created by INTERFACE1 feeds the global message channel in the "local" main driver of the repository instance. If an error is detected in INTERFACE1 then CALENDARSHUTDOWNABSENT stops execution immediately, regardless of how many steps remain. Because gMesssage is also connected to the application-level main driver, the entire application stops immediately - without "crashing" or "bombing".

class calendarshutdownabsent;                                          
public num runSequence / (initialValue=475);
public list gMessage / (sendEvent='N', autocreate='N');
public list interface / (initialValue={
'interface1',
'interface2',
'interface3',
'interface4',
'interface5',
'interface6',
'interface7',
'interface8',
'interface9'
});

_init: method / (state='o');
_super();
gMessage =getniteml(envlist('G'), 'message');
endmethod;

runInterface: method;
dcl num xMethod;
do xMethod=1 to listlen(interface) while (listlen(gMessage)=0);
call send(_self_, getitemc(interface, xMethod));
end;
endmethod;

 


Deprecated Coding Practices

  1. Method parameters
  2. Named and positional parameters
  3. Passing macro variables between instances
  4. Parent-child hierarchy between instances

Relationship: Limitations & Exclusions

  1. Any number of instances may participate in a relationship.
  2. Instances may join, and subsequently leave, a relationship at any time.
  3. A relationship may be implemented across any number of physically separate software applications
a