cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
JMP is taking Discovery online, April 16 and 18. Register today and join us for interactive sessions featuring popular presentation topics, networking, and discussions with the experts.
Choose Language Hide Translation Bar
Write Limits to a Data Table from a Limits Table
txnelson
Super User

This JMP Add-in takes Spec Limits and any of the 23 different types of Control Limits (i.e. XBar, S, Moving Range, etc.) that are in a separate data table (I call it a Limits Table) and populates the appropriate limits column properties in a JMP Data Table.

As stated above, the “Limits Table” is just a JMP table.  To be a valid Limits Table, it must have a column that contains the names of the columns to be updated, and then a column for each specific limit who’s values are going to be moved into the column properties your table that has your measurement data in.  Below is an example of what a Limits Table might look like.  Note: the names of the columns do not have to have specific names.  As will be seen, the columns to be used for the different parts of the spec and control limits will be mapped in a dialog window.

limits table.PNG

 

The “Limits Table” has 7 columns ad 128 rows.  Each row contains the limits for a specific column in your measurement data table.  In this example, the column named “Column Names” contains the names of the columns in your measurement data table that are to be updated.  All columns in the “Limits Table” will be mapped in the Add-in, as to the role it plays.

The measurement data table below, is the Semiconductor Capability data table from the JMP Sample data tables.  For this example, the Spec Limits that are part of the shipped data table have been removed. 

data table.PNG

To run the Add-in, go to your measurement data table, and click on it to make it the Current “Active” Data Table. Then run the Addin  The window that appears requests the user to select the “Limits Table” to be used for the updating.

LW window 1.PNG

When you select the table to use, the window will be augmented with the selection boxes to map the columns in the “Limits Table” to the role it will play in the moving of the limits into the measurement data table.

LW window 2.PNG

This is where the mapping takes place.  The first item to map, it to identify the column in the “Limits Table” that is to be used as the Identifier of what columns to update.  In our example, the “Column Names” column contains this information and it needs to be placed into the ID Column selection box.

LW window 3.PNG

Next, we map the limit columns into the type of Limit and whether it is the Upper, Target or Lower limit for the specified limit.  .  Spec Limits and Control Limits are handled separately.  There is a radio button that needs to be set to Spec Limits or Control Limits, to match what type of limit you are mapping.  To start with, we will setup the Spec Limits.  In the “Limits “Table” the columns named LSL, Target and USL contain spec limits.  To map them, simply select each one and move it to the appropriate limit selection box.

LW window 4.PNG

The columns XBar LCL, XBar Target and XBar UCL contain control limits.  Specifically they contain XBar control limits.  To map them to the Add=in, one first sets the radio button to “Control Limits”.

LW window 5.PNG

The mapping environment changes slightly.  Added to the display, is a down arrow box, which allows one to set which type of control limit you want to map.

LW window 6.PNG

The type of control limit that is in the “Limits Table” are XBar limits, so that is the control limit type we select.  Then we just map the columns to the limit rolls each of them play

LW window 7.PNG

If there were more types of control limits in the “Limit Table” one would just repeat the steps of selecting the limit type you want to map, and then place the appropriate columns into the selection box that represents the type of limit it is.

When you are done with the mapping, just click on “OK” to execute the Add-in.  When it completes, you can validate that it has been successful by going to the measurement data table and review the limits column properties

LW window 8.PNG

LW window 9.PNG

Comments
Ronti

this .jmpaddin file is not being intalled in my computer.

Anyone with this problem?

 

image.png

 

/Onti

You might check the version compatibility with the Add-in. He might have limited it to 12 or newer. You might want to check with your license admin. I’m pretty sure you have access to version 12 and 13… there were lots of improvements that you might find really handy.

Best,

M
txnelson

@MikeD_Anderson is correct.  The addin requires JMP 12 or later

Great addin! I was just going to write kind of the same thing for a project. 

 

Is is it possible run the addin via JSL? I would like to automate the process of adding spec limits in my application. 

txnelson

Thank you for your comment.  As far as running the addin as part of JSL, you will have to dig into the code and pull out the working part.   The code is structured into functions and EXPRs, that should allow you to easily strip out the code that is called from the dialog box.

jpol

Absolutely brilliant add-in. Great time saver. Especially as the "Show as graph reference lines" is automatically activated.

 

Thanks for sharing.

 

Philip

Thank you for the wonderful script.

Hi txnelson, Thank you for your script. When I run it, it runs without any error. However, when I look at the spec limits, for 'some of the columns' script assignes wrong limit values....I checked the column names and  naming is not the issue....I cannot figureout a reason why it does fine for some columns and some coulmnsn not,,,,,am I missing something? Can you help?

txnelson

Here is a version that I believe will correct the issue.  Try it out and get back to me if it fixes your issue.  I will then post the new Addin on the File Exchange

Names Default To Here( 1 );
Clear Symbols();

/*****************************************************************************/
/*                                                                           */
/* This is the inital piece of code to be run in the script                  */
/* It initializes the environment and opens the initial user inptut dialog   */
/*                                                                           */
/*****************************************************************************/
Main = Expr(
	dt = Current Data Table();
	
	LastColListY = "";

	// The recall values are saved into a namespace called "Recall"
	// and if it currently doesn't exist, set it up
	If( Namespace Exists( "Recall" ) == 0,
		recall = New Namespace(
			"Recall"
		)
	);

	// Get a list of all of the numeric columns in the data table
	AllNumericColumns = dt << get column names( numeric, string );
	AllNumericColumnsCompressed = AllNumericColumns;

	// Uppercase and compress the column names
	For( i = 1, i <= N Items( AllNumericColumns ), i++,
		AllNumericColumnsCompressed[i] = Uppercase( AllNumericColumnsCompressed[i] );
		While( Pat Match( AllNumericColumnsCompressed[i], " ", "" ) > 0, Pat Match( AllNumericColumnsCompressed[i], " ", "" ) );
	);

	// This iist is the list of the names used within JMP Contol Limits and is used by the script
	// to set the type of contol limit being updated
	ControlLimitList = {"XBar", "R", "S", "Individual Measurement", "Moving Range", "Median Moving Range", "P", "NP", "C", "U", "UWMA", "EWMA",
	"CUSUM", "Levey Jennings", "Individual on Group Means", "Individual on Group Std Devs", "Moving Range on Group Means",
	"Moving Range on Group Stds", "Median Moving Range on Group Means", "Median Moving Range on Group Std Devs", "Run Chart", "G", "T"};

	// Variale used to set the length of the column selection list
	nc = N Col( dt );

	lbWidth = 160;

	// Create a list of all of the open tables
	list_tables = {};

	For( i = 1, i <= N Table(), i++,
		Insert Into( list_tables, Data Table( i ) << get name )
	);
	
	// Display the Limits Table Selection window
	nw = New Window( "Populate Limits",
		V List Box(
			Spacer Box( size( 1, 5 ) ),
			H List Box( IconBB = Button Box( "" ), IconTB = Text Box( "Populate Limits Column Property from a separate table" ) ),
			Spacer Box( size( 1, 20 ) ), 
	
			TheHlB = H List Box(
				V List Box(
					Text Box( "Select Limits Table" ),
					Spacer Box( size( 1, 2 ) ),
					Panel Box( "Set Limits in " || Char( dt << get name ) || " with",
						TableLB = List Box( list_tables, n lines( 6 ), width( 220 ) ), 

					)
				)
			),
			Spacer Box( size( 1, 10 ) ),
			H List Box(
				Spacer Box( size( 10, 1 ) ),
				createtableBB = Button Box( "Create Limits Table from Current Active Data Table", createlimitstable )
			),
			Spacer Box( size( 1, 10 ) )
		), 	

		// When the user selection for the limits data table changes, reset the column dialog boxes
		TableLB << on change(
		
			// Delete the button box
			Try( createtableBb << delete );
		
			// Blank out the low,avg and high lists
			resetlists;
			
			// Delete the current column dialog boxes
			Try( clusterdlg << delete );
			
			// If the user has selected a new limits table, reset the window
			If( Try( (tablelb << get selected)[1], "" ) != "",
				dtlimits = Data Table( (tablelb << get selected)[1] );
				Current Data Table( dtlimits );
				
				// Reinput the code for the column dialog boxes....this is 
				// required, because the col list boxes do not lose the data
				// table references without totally deleting the col list box
				// and bringing in new instances
				clusterexpr;
				
				// Add the col dialog boxes to the current display
				theHLB << append( clusterdlg );
				
				// Reset required lists
				AllLimitColList = AllLimitColListCompressed = {};
				Try( AllLimitColList = Column( dtlimits, Column Names ) << get values );
				AllLimitColListCompressed = AllLimitColList;
				
				// Uppercase and remove blanks from the column list
				For( i = 1, i <= N Items( AllLimitColList ), i++,
					AllLimitColListCompressed[i] = Uppercase( AllLimitColListCompressed[i] );
					While( Pat Match( AllLimitColListCompressed[i], " ", "" ) > 0, Pat Match( AllLimitColListCompressed[i], " ", "" ) );
				);
			);
		), 
	
	// Set the icon 
		iconbb << set icon( "Update" )
	);

);

/*****************************************************************************/
/*                                                                           */
/*  Create a Limits Table populating limits from the current data table      */
/*                                                                           */
/*****************************************************************************/
CreateLimitsTable = Expr(
	HasBeenSeen = Repeat( 0, 24 );
	
	dtnew = New Table( "Limits Table", invisible, New Column( "Column Names", character, values( AllNumericColumns ) ) );

// Pass through all of the specified parameters and extract any limits found in the
	// active data table and move them to the new limits data table
	For( allvars = 1, allvars <= N Items( AllNumericColumns ), allvars++,  

		col = Parse( "dt:" || AllNumericColumns[allvars] );

	// Gather in the already set Control and Spec Limits
		controls = col << get property( "Control Limits" );
		specs = col << get property( "Spec Limits" );

	// Process Spec Limits
		
		If( !(Char( specs ) == "Scriptable[]" | Is Empty( specs ) == 1),
			If( HasBeenSeen[24] == 0,
				dtnew << New Column( "LSL" );
				dtnew << New Column( "Target" );
				dtnew << New Column( "USL" );
				HasBeenSeen[24] = 1;
			);
			For( i = 1, i <= N Items( specs ), i++,
				If( Is Missing( Try( Specs["LSL"], . ) ) == 0,
					dtnew:LSL[AllVars] = Specs["LSL"]
				);
				If( Is Missing( Try( Specs["Target"], . ) ) == 0,
					dtnew:Target[AllVars] = Specs["Target"]
				);
				If( Is Missing( Try( Specs["LSL"], . ) ) == 0,
					dtnew:USL[AllVars] = Specs["USL"]
				);
			);
		);
		

	
	//  All of the Control limits have the same structure.  This code extracts all of the different
		//  types of control limits and moves them to the limits table
		If( !(Char( controls ) == "Scriptable[]" | Is Empty( controls ) == 1), 
		
			For( i = 1, i <= N Items( controls ), i++, 

				If( Contains( ControlLimitList, Trim( Word( 1, Char( controls[i] ), "(,)" ) ) ) > 0,
					Control Limit Type = Trim( Word( 1, Char( controls[i] ), "(,)" ) );
					LimitListNumber = Contains( ControlLimitList, Trim( Word( 1, Char( controls[i] ), "(,)" ) ) );
					If( HasBeenSeen[LimitListNumber] == 0,
						dtnew << New Column( ControlLimitList[LimitListNumber] || "LCL" );
						dtnew << New Column( ControlLimitList[LimitListNumber] || "AVG" );
						dtnew << New Column( ControlLimitList[LimitListNumber] || "UCL" );
						HasBeenSeen[LimitListNumber] = 1;
					);
			
					For( ctype = 2, ctype <= 6, ctype = ctype + 2,
						If( Trim( Word( ctype, Char( controls[i] ), "(,)" ) ) == "Avg",
							Column( dtnew, ControlLimitList[LimitListNumber] || "AVG" )[AllVars] = Num(
								Word( ctype + 1, Char( controls[i] ), "(,)" )
							)
						);
						If( Trim( Word( ctype, Char( controls[i] ), "(,)" ) ) == "LCL",
							Column( dtnew, ControlLimitList[LimitListNumber] || "LCL" )[AllVars] = Num(
								Word( ctype + 1, Char( controls[i] ), "(,)" )
							)
						);
						If( Trim( Word( ctype, Char( controls[i] ), "(,)" ) ) == "UCL",
							Column( dtnew, ControlLimitList[LimitListNumber] || "UCL" )[AllVars] = Num(
								Word( ctype + 1, Char( controls[i] ), "(,)" )
							)
						);

					);
				)
			);
	  
		);
	);

	If( N Cols( dtnew ) == 1,
		dtnew << New Column( "LSL" );
		dtnew << New Column( "Target" );
		dtnew << New Column( "USL" );
		dtnew << New Column( "XBarLCL" );
		dtnew << New Column( "XBarAVG" );
		dtnew << New Column( "XBarUCL" );
	);

	Insert Into( List_Tables, "Limits Table" );
	x = List_Tables;
	TableLB << RemoveAll;
	TableLB << append( list_tables );

	dtnew << zoom window;
);


/*****************************************************************************/
/*                                                                           */
/* Blank out all selections and reset to the current limit                   */
/*                                                                           */
/*****************************************************************************/
resetLists = Expr(
	lowList = avgList = highList = {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""};
	If( Try( LimitTypeRB << get, 1 ) == 1,
		limitNum = 24,
		LimitNum = cb << get
	);
);

// Initialize the lists
ResetLists;

/*****************************************************************************/
/*                                                                           */
/* Set the Control Limits for all specified control limits for a single      */
/* row from the limits data table                                            */
/*                                                                           */
/*****************************************************************************/
SetControlLimits = Function( {c, CList, IgnoreMissing, i},
	{c, CList, IgnoreMissing}, 
			
	// Big List pulls in any existing limit control values.  This allows for the addition of
	// replacement of limit values without erasing any limits that currently exist.
	c = AllNumericColumns[Contains( AllnumericColumnsCompressed, AllLimitColListCompressed[i] )];
	biglist = Column( dt, c ) << getproperty( "control limits" );
					
		// Roll across all of the types of contol limits found
	For( cl = 1, cl <= N Items( CList ), cl++,
		little = {__type__( Avg( avexpr ), UCL( ULexpr ), LCL( LLexpr ) )};
		Substitute Into( little, Expr( __type__ ), Parse( controllimitnamesList[CList[cl]] ) );
			
		// Get the new limits to be processed from the limits table
		// If no column has been specified for one of the limits, a
		// missing value is set
		lcl = Try( Column( dtlimits, lowlist[CList[cl]] )[i], . );
		tar = Try( Column( dtlimits, avglist[CList[cl]] )[i], . );
		ucl = Try( Column( dtlimits, highlist[CList[cl]] )[i], . );
			
		// If Control Limits were found interrogate them
		If( Is Empty( biglist ) == 0,
			For( _i_ = N Items( big list ), _i_ >= 1, _i_--, 
			
				If( Uppercase( Word( 1, biglist[_i_], "(" ) ) == Uppercase( controllimitnamesList[CList[cl]] ),
					onelist = Char( biglist[_i_] );
					xlcl = .;
					xtarget = .;
					xucl = .;
					For( limitcnt = 2, limitcnt <= 7, limitcnt = limitcnt + 2,
						alimit = Word( limitcnt, onelist, "(,)" );
						If(
							Contains( alimit, "LCL" ) > 0, xlcl = Num( Word( limitcnt + 1, onelist, "(,)" ) ),
							Contains( alimit, "Avg" ) > 0, xtarget = Num( Word( limitcnt + 1, onelist, "(,)" ) ),
							Contains( alimit, "UCL" ) > 0, xucl = Num( Word( limitcnt + 1, onelist, "(,)" ) )
						);
					);
					Remove From( biglist, _i_ );
		
					dtReport << add Rows( 1 );
					dtReport:Column[N Rows( dtReport )] = c;
					dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
					dtReport:Limit[N Rows( dtReport )] = "Control Limit";
					dtReport:OldNew[N Rows( dtReport )] = "Previous";
					dtReport:Level[N Rows( dtReport )] = "Lower";
					dtReport:Value[N Rows( dtReport )] = xlcl;
					dtReport << add Rows( 1 );
					dtReport:Column[N Rows( dtReport )] = c;
					dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
					dtReport:Level[N Rows( dtReport )] = "Average";
					dtReport:Value[N Rows( dtReport )] = xtarget;
					dtReport:Limit[N Rows( dtReport )] = "Control Limit";
					dtReport:OldNew[N Rows( dtReport )] = "Previous";
					dtReport << add Rows( 1 );
					dtReport:Column[N Rows( dtReport )] = c;
					dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
					dtReport:Level[N Rows( dtReport )] = "Upper";
					dtReport:Value[N Rows( dtReport )] = xucl;
					dtReport:Limit[N Rows( dtReport )] = "Control Limit";
					dtReport:OldNew[N Rows( dtReport )] = "Previous";
					
				);					
					
				// If the limits do not currently exist, but other control limits are there, 
				// insert the new control limits into the large control limit list
				If( Is Missing( lcl ) + Is Missing( tar ) + Is Missing( ucl ) > 0, 
					// If Missing values from the limits table are to signal not to 
					// change the current limit, then process them accoringly
					If( IgnoreMissing == "No",
						If( LowList[CList[cl]] != "",
							Substitute Into( little, Expr( LLexpr ), Eval( lcl ) ),
							Substitute Into( little, Expr( LLexpr ), Eval( xlcl ) );
							lcl = xlcl;
						);
							
						If( AvgList[CList[cl]] != "",
							Substitute Into( little, Expr( avexpr ), Eval( tar ) ),
							Substitute Into( little, Expr( avexpr ), Eval( xtarget ) );
							tar = xtarget;
						);
							
						If( HighList[CList[cl]] != "",
							Substitute Into( little, Expr( ULexpr ), Eval( ucl ) ),
							Substitute Into( little, Expr( ULexpr ), Eval( xucl ) );
							ucl = xucl;
						);
					, 
						
						If( Is Missing( lcl ) == 1 | LowList[CList[cl]] == "",
							Substitute Into( little, Expr( LLexpr ), Eval( xlcl ) );
							lcl = xcl;
						,
							Substitute Into( little, Expr( LLexpr ), Eval( lcl ) )
						);
						If( Is Missing( ucl ) == 1 | HighList[CList[cl]] == "",
							Substitute Into( little, Expr( ULexpr ), Eval( xucl ) );
							ucl = xucl;
						,
							Substitute Into( little, Expr( ULexpr ), Eval( ucl ) )
						);
						If( Is Missing( tar ) == 1 | AvgList[CList[cl]] == "",
							Substitute Into( little, Expr( avexpr ), Eval( xtarget ) );
							tar = xtarget;
						,
							Substitute Into( little, Expr( avexpr ), Eval( tar ) )
						);
					)
				, 
					// Just add the new limits if not concerned about preserving data
					// and replacing limits if missing values have been specified
					If( Is Missing( lcl ) + Is Missing( tar ) + Is Missing( ucl ) < 3,
						If( LowList[CList[cl]] != "",
							Substitute Into( little, Expr( LLexpr ), Eval( lcl ) ),
							Substitute Into( little,
								Expr( LLexpr ),
									Eval( xlcl );
									lcl = xlcl;
							)
						);
							
						If( AvgList[CList[cl]] != "",
							Substitute Into( little, Expr( avexpr ), Eval( tar ) ),
							Substitute Into( little, Expr( avexpr ), Eval( xtarget ) );
							tar = xtarget;
						);
							
						If( HighList[CList[cl]] != "",
							Substitute Into( little, Expr( ULexpr ), Eval( ucl ) ),
							Substitute Into( little, Expr( ULexpr ), Eval( xucl ) );
							ucl = xucl;
						);
					)
	
				);
				// If the big list doesn't exist, create it
				If( Is Empty( biglist ) == 1,
					biglist = {}
				);
				// Add the current limit type to the overall list of control limits
				Insert Into( biglist, little );
				
				dtReport << add Rows( 1 );
				dtReport:Column[N Rows( dtReport )] = c;
				dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
				dtReport:Limit[N Rows( dtReport )] = "Control Limit";
				dtReport:OldNew[N Rows( dtReport )] = "New";
				dtReport:Level[N Rows( dtReport )] = "Lower";
				dtReport:Value[N Rows( dtReport )] = lcl;
				dtReport << add Rows( 1 );
				dtReport:Column[N Rows( dtReport )] = c;
				dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
				dtReport:Level[N Rows( dtReport )] = "Average";
				dtReport:Value[N Rows( dtReport )] = tar;
				dtReport:Limit[N Rows( dtReport )] = "Control Limit";
				dtReport:OldNew[N Rows( dtReport )] = "New";
				dtReport << add Rows( 1 );
				dtReport:Column[N Rows( dtReport )] = c;
				dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
				dtReport:Level[N Rows( dtReport )] = "Upper";
				dtReport:Value[N Rows( dtReport )] = ucl;
				dtReport:Limit[N Rows( dtReport )] = "Control Limit";
				dtReport:OldNew[N Rows( dtReport )] = "New";
			)
		,   
			// No control limits currently exist for the target column, so create the large
			// control limit list by setting it equal to the new input limits
		
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
			dtReport:Limit[N Rows( dtReport )] = "Control Limit";
			dtReport:OldNew[N Rows( dtReport )] = "Previous";
			dtReport:Level[N Rows( dtReport )] = "Lower";
			dtReport:Value[N Rows( dtReport )] = .;
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
			dtReport:Level[N Rows( dtReport )] = "Average";
			dtReport:Value[N Rows( dtReport )] = .;
			dtReport:Limit[N Rows( dtReport )] = "Control Limit";
			dtReport:OldNew[N Rows( dtReport )] = "Previous";
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
			dtReport:Level[N Rows( dtReport )] = "Upper";
			dtReport:Value[N Rows( dtReport )] = .;
			dtReport:Limit[N Rows( dtReport )] = "Control Limit";
			dtReport:OldNew[N Rows( dtReport )] = "Previous";
			
			If( Is Missing( lcl ) + Is Missing( tar ) + Is Missing( ucl ) < 3, 
				
				If( LowList[CList[cl]] != "",
					Substitute Into( little, Expr( LLexpr ), Eval( lcl ) ),
					Substitute Into( little, Expr( LLexpr ), Eval( . ) );
					lcl = .;
				);
						
				If( AvgList[CList[cl]] != "",
					Substitute Into( little, Expr( avexpr ), Eval( tar ) ),
					Substitute Into( little, Expr( avexpr ), Eval( . ) );
					tar = .;
				);
					
				If( HighList[CList[cl]] != "",
					Substitute Into( little, Expr( ULexpr ), Eval( ucl ) ),
					Substitute Into( little, Expr( ULexpr ), Eval( . ) );
					ucl = .;
				);
			);
				
				// If the big list doesn't exist, create it
			If( Is Empty( biglist ) == 1,
				biglist = {}
			);
				// Add the current limit type to the overall list of control limits
			Insert Into( biglist, little );
			
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
			dtReport:Limit[N Rows( dtReport )] = "Control Limit";
			dtReport:OldNew[N Rows( dtReport )] = "New";
			dtReport:Level[N Rows( dtReport )] = "Lower";
			dtReport:Value[N Rows( dtReport )] = lcl;
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
			dtReport:Level[N Rows( dtReport )] = "Average";
			dtReport:Value[N Rows( dtReport )] = tar;
			dtReport:Limit[N Rows( dtReport )] = "Control Limit";
			dtReport:OldNew[N Rows( dtReport )] = "New";
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
			dtReport:Level[N Rows( dtReport )] = "Upper";
			dtReport:Value[N Rows( dtReport )] = ucl;
			dtReport:Limit[N Rows( dtReport )] = "Control Limit";
			dtReport:OldNew[N Rows( dtReport )] = "New";
		)
				
		;
	);
	
	If( Char( biglist ) != "Scriptable[]" & Is Empty( biglist ) == 0 & biglist != {},
		Column( dt, c ) << setproperty( "control limits", Eval( biglist ) )
	);
);

/*****************************************************************************/
/*                                                                           */
/* Set the Spec Limits for all specified control limits for a single         */
/* row from the limits data table                                            */
/*                                                                           */
/*****************************************************************************/
SetSpecLimits = Function( {c, lsl, usl, avg, IgnoreMissing, showlims},
	{c, lsl, usl, avg, IgnoreMissing, showlims}, 
	
	// Get the current spec limits
	Try( speclist = Column( dt, c ) << get property( "spec limits" ), {} );
	
	Try( xlsl = speclist["LSL"], xlsl = . );
	Try( xusl = speclist["USL"], xusl = . );
	Try( xavg = speclist["Target"], xavg = . );
	
	dtReport << add Rows( 1 );
	dtReport:Column[N Rows( dtReport )] = c;
	dtReport:Limit Type[N Rows( dtReport )] = "_";
	dtReport:Limit[N Rows( dtReport )] = "Spec Limit";
	dtReport:OldNew[N Rows( dtReport )] = "Previous";
	dtReport:Level[N Rows( dtReport )] = "Lower";
	dtReport:Value[N Rows( dtReport )] = xlsl;
	dtReport << add Rows( 1 );
	dtReport:Column[N Rows( dtReport )] = c;
	dtReport:Limit Type[N Rows( dtReport )] = "_";
	dtReport:Level[N Rows( dtReport )] = "Average";
	dtReport:Value[N Rows( dtReport )] = xavg;
	dtReport:Limit[N Rows( dtReport )] = "Spec Limit";
	dtReport:OldNew[N Rows( dtReport )] = "Previous";
	dtReport << add Rows( 1 );
	dtReport:Column[N Rows( dtReport )] = c;
	dtReport:Limit Type[N Rows( dtReport )] = "_";
	dtReport:Level[N Rows( dtReport )] = "Upper";
	dtReport:Value[N Rows( dtReport )] = xusl;
	dtReport:Limit[N Rows( dtReport )] = "Spec Limit";
	dtReport:OldNew[N Rows( dtReport )] = "Previous";

	// Process the limits
	If( Is Missing( lsl ) + Is Missing( avg ) + Is Missing( usl ) < 3, 
		// If misssing value input is to be Ignored, that means that
		// old values of the limits are to be moved forward if the new
		// input values are missing values
		If( IgnoreMissing == "Yes",
			If( (Is Missing( xusl ) == 1 & Is Missing( usl ) == 0) | HighList[24] != "",
				xusl = usl
			);
			If( (Is Missing( xlsl ) == 1 & Is Missing( lsl ) == 0) | LowList[24] != "",
				xlsl = lsl
			);
			If( (Is Missing( xavg ) == 1 & Is Missing( avg ) == 0) | AvgList[24] != "",
				xavg = avg
			);
			cmd = Expr(
				Column( dt, c ) << SetProperty(
					"Spec LImits",
					{LSL( Expr( xlsl ) ), USL( Expr( xusl ) ), Target( Expr( xavg ) ), Show Limits( Expr( showlims ) )}
				)
			);
			
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = "_";
			dtReport:Limit[N Rows( dtReport )] = "Spec Limit";
			dtReport:OldNew[N Rows( dtReport )] = "New";
			dtReport:Level[N Rows( dtReport )] = "Lower";
			dtReport:Value[N Rows( dtReport )] = xlsl;
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = "_";
			dtReport:Level[N Rows( dtReport )] = "Average";
			dtReport:Value[N Rows( dtReport )] = xavg;
			dtReport:Limit[N Rows( dtReport )] = "Spec Limit";
			dtReport:OldNew[N Rows( dtReport )] = "New";
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = "_";
			dtReport:Level[N Rows( dtReport )] = "Upper";
			dtReport:Value[N Rows( dtReport )] = xusl;
			dtReport:Limit[N Rows( dtReport )] = "Spec Limit";
			dtReport:OldNew[N Rows( dtReport )] = "New";
			
		, // Else
			If( LowList[24] == "",
				lsl = xlsl
			);
			If( AvgList[24] == "",
				avg = xavg
			);
			If( HighList[24] == "",
				usl = xusl
			);
			cmd = Expr(
				Column( dt, c ) << SetProperty(
					"Spec LImits",
					{LSL( Expr( lsl ) ), USL( Expr( usl ) ), Target( Expr( avg ) ), Show Limits( Expr( showlims ) )}
				)
			);
			
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = "_";
			dtReport:Limit[N Rows( dtReport )] = "Spec Limit";
			dtReport:OldNew[N Rows( dtReport )] = "New";
			dtReport:Level[N Rows( dtReport )] = "Lower";
			dtReport:Value[N Rows( dtReport )] = lsl;
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = "_";
			dtReport:Level[N Rows( dtReport )] = "Average";
			dtReport:Value[N Rows( dtReport )] = avg;
			dtReport:Limit[N Rows( dtReport )] = "Spec Limit";
			dtReport:OldNew[N Rows( dtReport )] = "New";
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = "_";
			dtReport:Level[N Rows( dtReport )] = "Upper";
			dtReport:Value[N Rows( dtReport )] = usl;
			dtReport:Limit[N Rows( dtReport )] = "Spec Limit";
			dtReport:OldNew[N Rows( dtReport )] = "New";
		);
		Eval( Eval Expr( cmd ) );
	);
);

/*****************************************************************************/
/*                                                                           */
/* Run through each of the columns in the limits table and call the functions*/
/* that update the limits, based upon what limits have been specified in the */
/* dialog input screen                                                       */
/*                                                                           */
/*****************************************************************************/
runit = Expr(
	ControlLimitNamesList = {"XBar", "R", "S", "Individual Measurement", "Moving Range", "Median Moving Range", "P", "NP", "C", "U", "UWMA", "EWMA",
	"CUMSUM", "Levey Jennings", "Individual on Group Means", "Individual on Group Std Devs", "Moving Range on Group Means",
	"Moving Range on Group Std Devs", "Median Moving Range on Group Means", "Median Moving Range on Group Std Devs", "Run Chart", "G", "T"}; 
		
	// Create a list of which control limits have been specified
	CList = {};
	For( i = 1, i <= 23, i++,
		If( LowList[i] || avgList[i] || highList[i] != "",
			Insert Into( CList, i )
		)
	); 
		
	// If spec limits have been specified, setup a similar flag to the control limits
	// to use for spec limit processing
	SLList = {};
	If( LowList[24] || avgList[24] || highList[24] != "",
		Insert Into( SLList, 24 )
	); 

	// Get Excluded Rows in the Limits Table so they can be bypassed in processing 
	Excluded Rows = As List( dtLimits << get excluded rows ); 

	// Roll across the rows in the limits table and process the new limits
	For( ii = 1, ii <= N Items( AllLimitColList ), ii++,
		Process = "Yes";
		If( N Items( Excluded Rows ) > 0,
			If( Loc( Excluded Rows, ii ) > 0,
				Process = "No",
				Process = "Yes"
			)
		);

		If( Process == "Yes",
			targetName = AllNumericColumns[Contains( AllnumericColumnsCompressed, AllLimitColListCompressed[ii] )];	
//ii=30			
				// If there are spec limits to be processed, call the setspeclimits function
			If( N Items( SLList ) > 0,
				return = setspeclimits(
					Targetname,
					Try( Column( dtlimits, lowlist[24] )[ii], . ),
					Try( Column( dtlimits, highlist[24] )[ii], . ),
					Try( Column( dtlimits, avglist[24] )[ii], . ),
					IgnoreMissing,
					showLims
				)
			);	
	
	// If Control limit changes have been specified, call the SetControlLimits function
			If( N Items( CList ) > 0,
				return = SetControlLimits( TargetName, CList, IgnoreMissing, ii )
			);
		);
	);
	
	ShowReport;
	
	close(dtLimits, nosave );
	dtLimits = dtOriginalLimits;
	current data table(dt);
);

/*****************************************************************************/
/*                                                                           */
/* Create and display a Summary Report                                       */
/*                                                                           */
/*****************************************************************************/
ShowReport = Expr(

	If( N Rows( dtReport ) > 0, 
	// Calculate Required Scroll Bar Width
		Current Data Table( dtReport );
		Summarize( dtReport, Ltypes = By( :Limit Type ) );
		hsize = N Items( Ltypes ) * 200 + 150;
		
		// Generate a tabluate report for the display
		tab = Tabulate(
			Change Item Label( Statistics( Sum, " " ) ),
			Remove Column Label( Grouping Columns( :Limit Type, :Limit, :OldNew ) ),
			Show Control Panel( 0 ),
			Include missing for grouping columns( 1 ),
			Add Table(
				Column Table( Grouping Columns( :Limit, :Limit Type, :OldNew ), Analysis Columns( :Value ) ),
				Row Table( Grouping Columns( :Column, :Level ) )
			),
			SendToReport( Dispatch( {}, "Tabulate", OutlineBox, {Set Title( "Set Limits Report" )} ) )
		);
		Tab << on Close( Close( dtReport, nosave ) );
		
		// Modify the output so the Tabulate table is in a scroll box of limited size
		Report( tab ) << append( thescroll = Scroll Box( size( hsize, 700 ) ) );
		thescroll << append( Report( Tab )[Box( 1 )] );
		// Delete the non scroll box version of the table
		Report( Tab )[Box( 1 )] << delete;
	
		// If Non Processed columns are found in the data table, list them out
		If( N Items( NotProcessedColumnsList ) > 0,
			rptNotProcessedOB = Outline Box( "Columns Not Processed",
				Border Box( top( 10 ), Left( 10 ), Right( 10 ), bottom( 10 ), sides( 15 ),
					V List Box(
						Spacer Box( size( 1, 10 ) ),
						Text Box( "The following column(s) are in the data table" ),
						Text Box( "but are not being processed because they were)" ),
						Text Box( "not referenced in the limits table." ),
						Spacer Box( size( 1, 10 ) ),
						H List Box( Spacer Box( size( 60, 1 ) ), List Box( NotProcessedColumnsList ) ),
						Spacer Box( size( 1, 10 ) ),
						Text Box( "No limits will be set for the above referenced columns" ),
						Spacer Box( size( 1, 10 ) )
											
					)
				)
			);
			rptNotProcessedOB << close;
		
			Report( Tab ) << Append( rptNotProcessedOB );
		
		);
		
		// If columns were found in the limits table, but not in the data table
		// list them out
		If( N Items( NotFoundList ) > 0, 
			
			rptNotFoundOB = Outline Box( "Columns Not Found",
				Border Box( top( 10 ), Left( 10 ), Right( 10 ), bottom( 10 ), sides( 15 ),
					V List Box(
						Spacer Box( size( 1, 10 ) ),
						Text Box( "The following column(s) names(s) were referenced in the" ),
						Text Box( "Limit Table, but no numeric column of that/those name(s)" ),
						Text Box( "were found in the data table." ),
						Spacer Box( size( 1, 10 ) ),
						H List Box( Spacer Box( size( 60, 1 ) ), List Box( NotFoundList ) ),
						Spacer Box( size( 1, 10 ) ),
						Text Box( "No limits will be set for the above referenced columns" ),
						Spacer Box( size( 1, 10 ) )
											
					)
				)
			);
			rptNotFoundOB << close;
			Report( Tab ) << Append( Spacer Box( size( 1, 10 ) ) );
			Report( Tab ) << Append( rptNotFoundOB );
		
		);
	)
);
	
/*****************************************************************************/
/*                                                                           */
/* This is the display for the column selections where the user matches the  */
/* the limit columns from the limit data table with the columns in the       */
/* data table                                                                */
/*                                                                           */
/*****************************************************************************/	
clusterexpr = Expr(
	all Columns List = dtlimits << get column names;
	Current Data Table( dtlimits );
	clusterDlg = V List Box(
		Border Box( Left( 3 ), top( 2 ),
			V List Box(
				Text Box( "Select Columns to Read the Limits From" ),
				H List Box(
					V List Box(
				
						// Generate the master display box for the columns in the data table
						Panel Box( "Select Columns",
							cldvlb = H List Box(
								Popup Box(
									{"Clear", filterval = "" ; thefilter << set text( "" ) ; clearit ; , 
														
									"Numeric", filterval = "" ; thefilter << set text( "" ) ; clearit ; For( II = 1,
										II <= N Items( All Columns List ), II++,
										If( Column( All Columns List[II] ) << get data type != "Numeric",
											colListData << set selected( II )
										)
									) ; colListData << remove selected ; , 
														
									"Character", filterval = "" ; thefilter << set text( "" ) ; clearit ; For( II = 1,
										II <= N Items( All Columns List ), II++,
										If( Column( All Columns List[II] ) << get data type != "Character",
											colListData << set selected( II )
										)
									) ; colListData << remove selected ; , 
														
									"Name Contains", filterval = "" ; filtervlb << prepend(
										V List Box(
											thefilter = Text Edit Box( "",
												<<set width( 175 ),
												<<set script(
													filterval = Trim( thefilter << get text() );
													clearit;
													// This line seems redundant, but proved to be necessary to
													// insure the correct list of data was evaluated
													All Columns List = colListData << get items;
															
															// If a value is in the filter input box, cycle through all
													// of the columns to find the matches
													If( Is Missing( filterval ) == 0,
														For( II = 1, II <= N Items( All Columns list ), II++, 
																	
															If( Contains( Uppercase( Char( All Columns list[II] ) ), Uppercase( filterval ) ) == 0,        
																// If a match is found select the column because
																// all selected items will be removed at the end,
																// leaving only the non matching items
																colListData << set selected( II )
															)
														)
													);
													colListData << remove selected;
												)
											)
										)
									)},
									Spacer Box( size( 1, 4 ) )
								),
								filtervlb = V List Box( colListData = Col List Box( Data Table( dtlimits ), width( lbWidth ), nLines( 15 ) ) )
							)
						),
						colListData << append( dtlimits << get column names ),    
						
							// At startup, sselect the columns in the selection box for 
						// the columns that are selected in the data table 
						For( II = 1, II <= N Items( All Columns list ), II++,
							If( Column( ii ) << get selected == 1,
								colListData << set selected( II )
							)
						)
					),    
				
					// Setup the selection buttons and the selection boxes
					Panel Box( "Cast Selected Columns into Roles",
						Lineup Box( N Col( 2 ), Spacing( 3 ),
							bb1 = Button Box( "ID Column", colListY << Append( colListData << GetSelected ) ),
							colListY = Col List Box( width( lbWidth - 50 ), nLines( 1 ), min col( 1 ), character, max selected( 1 ) )
						),
						V List Box(
							Lineup Box( N Col( 2 ),
								Text Box( "Type of Limit" ),
								Text Box( " " ),
								LimitTypeRB = Radio Box(
									{"Spec Limit", "Control Limit"},
									Try(
										If( LimitTypeRB << get == 1,
											info << set text( "Spec Limits will be set when you select this option and that is that" );
											cb << delete;
											cbsb << delete;
											vlbcb << append( cbsb = Spacer Box( size( 1, 41 ) ) );
											bbl << set button name( "Lower SL" );
											bba << set button name( "Target" );
											bbU << set button name( "Upper SL" );
											WriteLimitColNames( LimitNum );
											LimitNum = 24;
											ReadLimitColNames( LimitNum );
										,
											Info << set text(
												"Select the Control Limit to be populated and then select the columns that contain those limits"
											);
											Try( cbsb << delete );
											vlbcb << append(
												cbsb = V List Box(
													Spacer Box( size( 1, 3 ) ),
													cb = Combo Box(
														ControlLimitList,
														writeLimitColNames( limitNum );
												
														LimitNum = cb << get;
														ReadLimitColNames( limitNum );
													)
												)
											);
											bbl << set button name( "Lower CL" );
											bba << set button name( "Average" );
											bbU << set button name( "Upper CL" );
											WriteLimitColNames( LimitNum );
											LimitNum = cb << get;
											ReadLimitColNames( LimitNum );
										)
									)
								),
								H List Box(
									//Spacer Box( size( 15, 1 ) ),
									V List Box(
										ReportCB = Check Box( {"Show Summary Rpt"} ),
										ShowCB = Check Box( {"Set Show Specs"} ),
										IgnoreCB = Check Box( {"Ignore Missing"} )
									)
								)
							), 
						
							Info = Text Box( "Spec Limits will be set when you select this option and that is that" ), 
										 
							vlbcb = V List Box( cbsb = Spacer Box( size( 1, 39 ) ) )
						), 
						
						// Setup the Button Boxes and Col List Boxes for the column selections
						lineupvlb = V List Box(
							lub = V List Box(
								Lineup Box( N Col( 2 ), Spacing( 3 ),
									bbL = Button Box( "Lower SL", colLower << Append( colListData << GetSelected ) ),
									colLower = Col List Box( width( lbWidth - 50 ), nLines( 1 ), numeric, max selected( 1 ) ),
									bba = Button Box( "Target", colAverage << Append( colListData << GetSelected ) ),
									colAverage = Col List Box( width( lbWidth - 50 ), nLines( 1 ), numeric, max selected( 1 ) ),
									bbU = Button Box( "Upper SL", colUpper << Append( colListData << GetSelected ) ),
									colUpper = Col List Box( width( lbWidth - 50 ), nLines( 1 ), numeric, max selected( 1 ) ), 

								),
								Spacer Box( size( 1, 12 ) ),
								bb1 << set tip( "Column in the Limits table that matches to the column names in the main data table" );
								info << set width( 200 ) << set wrap( 200 );
							)
						)
					), 	
								
					// Setup the action buttons that control the window processing
					V List Box(
						Panel Box( "Action",
							Lineup Box( N Col( 1 ),
								Button Box( "OK",    
							
									WriteLimitColNames( LimitNum );
									
									lowconcat = highconcat = "";
									For( i = 1, i <= N Items( LowList ), i++,
										lowconcat = lowconcat || LowList[i];
										highconcat = highconcat || HighList[i];
									);
									LimitsChoosen = Length( lowconcat || highconcat ) > 0;
								
									// Check to see if the required data has been input
									If(
										N Items( colListY << GetItems ) == 0, Dialog( "No ID Column Specified", Button( "OK" ) ),          // Else
										LimitsChoosen == 0, Dialog( "No Limit Columns Specified", Button( "OK" ) ),      // Else
									
										RecallList = {lowlist = {}, avglist = {}, highlist = {}};
									
										RecallList["lowlist"] = lowlist;
										RecallList["avglist"] = avglist;
										RecallList["highlist"] = highlist;
									
										// Place the selected values from the screen into the recall list
										Insert Into( RecallList, Parse( "IDColumn = \!"" || Char( (colListY << get items)[1] ) || "\!"" ) );
									
										If( IgnoreCB << get == 1,
											IgnoreMissing = "Yes",
											IgnoreMissing = "No"
										);
										ShowLims = ShowCB << get;
										ShowRpt = ReportCB << get;
									
										Insert Into( RecallList, Parse( "IgnoreMissing = \!"" || IgnoreMissing || "\!"" ) );
									
										Insert Into( RecallList, Parse( "ShowLims = " || Char( showlims ) ) );

										Insert Into( RecallList, Parse( "ShowRpt = " || Char( showRpt ) ) );
										
										// Save recall string to Recall namespace
										Recall:WriteLimits = RecallList;					
									
										nw << CloseWindow;
										
										dtReport = New Table( "Report",
											Private,
											New Column( "Column", Character, "Nominal" ),
											New Column( "Limit Type", Character, "Nominal" ),
											New Column( "Level",
												Character,
												"Nominal",
												Set Property( "Value Ordering", {"Lower", "Average", "Upper"} )
											),
											New Column( "Value", Numeric, "Continuous", Format( "Best", 10 ) ),
											New Column( "Limit",
												Character,
												"Nominal",
												Set Property( "Value Ordering", {"Spec Limit", "Control Limit"} )
											),
											New Column( "OldNew", Character, "Nominal", Set Property( "Value Ordering", {"Previous", "New"} ) )
										);
										dtReport:Column << set property( "Row Order Levels", 1 );
										
										// Create a working copy of the limits table
										dtLimits << Select Rows( NotFoundListRowNumbers );
										dtLimits << invert row selection;
										dtLimitsTemp = dtLimits << subset(invisible, selected rows(1), selected columns(0));
										dtLimits << clear select;
										dtOriginalLimits = dtLimits;
										dtLimits = dtLimitsTemp;
										wait(0);

										// Run the actual code that updates the limits						
										runit;
									);
								), 
								
								Button Box( "Cancel",
									clusterDlg << CloseWindow;
									Names Default To Here( 1 );
								),
								Text Box( " " ), 
								
								Button Box( "Remove",
									colListY << RemoveSelected;
									collower << remove selected;
									colaverage << remove selected;
									colupper << remove selected;
								), 
								
								Button Box( "Recall",
									If( Is List( Recall:WriteLimits ) == 1, 
										
										Eval List( Recall:WriteLimits );
										
										If( Try( (colListY << get items)[1], "" ) != IDcolumn,
											colListY << RemoveAll;
											colListY << append( IDcolumn );
										);
										
										If( IgnoreMissing == "Yes",
											IgnoreCB << set( 1 ),
											IgnoreCB << set( 0 )
										);
										
										ShowCB << set( ShowLims );
										
										ReportCB << set( ShowRpt );
										
										readLimitColNames( LimitNum );
									)
								), 
								
								Button Box( "Help", notImplemented )
							)
						)
					), 

				)
			)
		), 
		
		// Set the default values for the check box options
		IgnoreCB << set( 1 );
		IgnoreMissing = "Yes";
		ShowCB << set( 1 );
		ShowLims = 1;
		ReportCB << set( 1 );
		ShowRpt = 1;
	);

	// Write the names to the Limit Lists ffrom the display window
	WriteLimitColNames = Function( {lNum},
		LowList[LNum] = Try( (colLower << get items)[1], "" );
		AvgList[LNum] = Try( (colAverage << get items)[1], "" );
		HighList[LNum] = Try( (colUpper << get items)[1], "" );
	);

	// Read the limit values from the Limit Lists and populate
	// the display window with the values
	ReadLimitColNames = Function( {lNum},
		{lNum},
		tmplist = lowlist;
		ColLower << remove all;
		lowlist = tmplist;
		tmplist = {};
		Insert Into( tmplist, lowlist[lnum] );
		collower << append( tmplist );
	
		tmplist = avglist;
		ColAverage << remove all;
		avglist = tmplist;
		tmplist = {};
		Insert Into( tmplist, avglist[lnum] );
		colAverage << append( tmplist );
	
		tmplist = Highlist;
		ColUpper << remove all;
		Highlist = tmplist;
		tmplist = {};
		Insert Into( tmplist, Highlist[lnum] );
		ColUpper << append( tmplist );
	);
	
	// When the value for the column that contains the names of the data
	// table columns that will have their limits changed, process the list,
	// comparing it to the data table to check for columns not found
	ColListY << On Change(
		If( Try( (ColListY << get items)[1], "" ) != LastColListY,
			If( Try( N Items( colListY << get items ), 0 ) > 0,
				AllLimitColList = AllLimitColListCompressed = NotProcessedColumnsList = NotProcessedColumnsListCompressed = {};
				LastColListY = (ColListY << get items)[1];
				If( Try( (colListY << get items)[1], "" ) != "",
					AllLimitColList = Column( dtlimits, (colListY << get items)[1] ) << get values;
					AllLimitColListCompressed = AllLimitColList;
					NotFoundList = {};
					NotFoundListRowNumbers = {};
			
					// Uppercase and compress the column names
					For( i = N Items( AllLimitColList ), i > 0, i--,
						AllLimitColListCompressed[i] = Uppercase( AllLimitColListCompressed[i] );
						While( Pat Match( AllLimitColListCompressed[i], " ", "" ) > 0, Pat Match( AllLimitColListCompressed[i], " ", "" ) );
						If( Contains( AllNumericColumnsCompressed, AllLimitColListCompressed[i] ) == 0,
							Insert Into( NotFoundList, AllLimitColList[i] );
							Insert Into( NotFoundListRowNumbers, i );
							AllLimitColListCompressed = Remove( AllLimitColListCompressed, i, 1 );
							AllLimitColList = Remove( AllLimitColList, i, 1 );
						);
					);
			
					// Find all of the columns that are not going to be processed
					NotProcessedColumnsListCompressed = NotProcessedColumnsList = dt << get column Names( string );
		
					For( i = N Items( NotProcessedColumnsList ), i > 0, i--,
						NotProcessedColumnsListCompressed[i] = Uppercase( NotProcessedColumnsListCompressed[i] );
						While( Pat Match( NotProcessedColumnsListCompressed[i], " ", "" ) > 0,
							Pat Match( NotProcessedColumnsListCompressed[i], " ", "" )
						);
						If( Contains( AllLimitColListCompressed, NotProcessedColumnsListCompressed[i] ) > 0, 
							//Insert Into( NotFoundList, AllLimitColList[i] );
							NotProcessedColumnsListCompressed = Remove( NotProcessedColumnsListCompressed, i, 1 );
							NotProcessedColumnsList = Remove( NotProcessedColumnsList, i, 1 );
						);
					);
		
					// If there are columns that do not exist in the data table, notify the user
					If( N Items( NotFoundList ) > 0,
						winLocation = nw << get window position;
						err = New Window( "Columns Not Found",
							Border Box( top( 10 ), Left( 10 ), Right( 10 ), bottom( 10 ), sides( 15 ),
								V List Box(
									Spacer Box( size( 1, 10 ) ),
									Text Box( "The following column(s) names(s) were referenced in the" ),
									Text Box( "Limit Table, but no numeric column of that/those name(s)" ),
									Text Box( "were found in the data table." ),
									Spacer Box( size( 1, 10 ) ),
									H List Box( Spacer Box( size( 60, 1 ) ), List Box( NotFoundList ) ),
									Spacer Box( size( 1, 10 ) ),
									Text Box( "No limits will be set for the above referenced columns" ),
									Spacer Box( size( 1, 10 ) ),
									H List Box(
										continueBB = Button Box( "Continue Anyway", err << close window ),
										stopBB = Button Box( "Stop Script",
											err << close window;
											nw << close window;
											Throw();
										)
									)
								)
							)
						);
						err << move window( winLocation[1] + 100, winLocation[2] + 100 ) << zoom window;
					);
				);
			)
		)
	);
);

/*****************************************************************************/
/*                                                                           */
/* An Expr to run for any function that has not been implemented.  It        */
// displays a dialog window telling the user that the function has not been  */
/* implemented yet                                                           */
/*                                                                           */
/*****************************************************************************/
notImplemented = Expr(
	Dialog( "Feature Not Implemented Yet", Button( "OK" ) )
);

/*****************************************************************************/
/*                                                                           */
/* Code used in the column selection filter to reset the selection box       */
/*                                                                           */
/*****************************************************************************/
clearit = Expr(
	
	For( II = 1, II <= N Items( colListData << get items ), II++,
		colListData << set selected( II )
	);
	colListData << remove selected;
	colListData << append( all columns list );
);

// Call the main expr as the inital piece of script to be run
main;

.

Perfect! this works correctly. Please go ahead make this version of the code for addin...

Vmuthu

@txnelson  This is a fantastic addon, Can we add "Units" for the column from another spec sheet? do you have any enhanced JSL script for that?

Thanks,

Venkat 

txnelson

Since JMP has added the ability to Manage Limits, which takes over the Writing of limits to data table from a limits table, I will not be enhancing the Addin.  However, below is a simple script that should give you a start and a script that will read from a limits data table and add Units to another table's columns

Names Default To Here( 1 );
Clear Symbols();
dt = Open( "$SAMPLE_DATA/big class.jmp" );

dtLimits = New Table( "Limits",
	New Column( "Column Name", Character, values( {"height", "weight"} ) ),
	New Column( "Units", Character, values( {"Inches", "Lbs"} ) )
);


colNamesList = {"height", "weight"};

For( i = 1, i <= N Items( colNamesList ), i++,
	dtLimits << select where( :Column Name == colNamesList[i] );
	If( N Rows( dtLimits << get selected rows ) > 0,
		Eval(
			Substitute(
					Expr(
						Column( dt, colNamesList[i] ) << set property( "Units", __units__ )
					),
				Expr( __units__ ), dtLimits:Units[i]
			)
		)
	);
);

Hi @txnelson,

 

anyway that I can change the USL and LSL lines colour to red (from the default blue) ?

 

 

Thank you,

Ann Ann

txnelson

I am not aware of any preference setting that will change the default color for the Spec Limits reference lines.  What I have done in the past, has been to create my own Reference Lines in the color I want, and not use the JMP default to generate the Limits Lines.

kabir

Thank you, great add-in and scripting!

Have been working on automating some analysis and this works perfect. Capability analysis is not required, and it is very slow for this as 100+ variables. Manage spec limits not importing from a table for some reason and hanging.

 

 

 

Song

Thank you, this add-in very good for me.

I have one question.

How can i make the graph after add-in run first?

txnelson

What graph are you talking about?  The Summary Report?

Song

Graph buider and save image!

I'd like to Graph buider save to image after spec in but the script below generates an image before the spec is applied.

 

Open("C:\Users\A07\Desktop\data\Working data\Jump\spec_limit.jmp");

dt = open();




 Names Default To Here( 1 );
Clear Symbols();

/*****************************************************************************/
/*                                                                           */
/* This is the inital piece of code to be run in the script                  */
/* It initializes the environment and opens the initial user inptut dialog   */
/*                                                                           */
/*****************************************************************************/
Main = Expr(
	dt = Current Data Table();
	
	LastColListY = "";

	// The recall values are saved into a namespace called "Recall"
	// and if it currently doesn't exist, set it up
	If( Namespace Exists( "Recall" ) == 0,
		recall = New Namespace(
			"Recall"
		)
	);

	// Get a list of all of the numeric columns in the data table
	AllNumericColumns = dt << get column names( numeric, string );
	AllNumericColumnsCompressed = AllNumericColumns;

	// Uppercase and compress the column names
	For( i = 1, i <= N Items( AllNumericColumns ), i++,
		AllNumericColumnsCompressed[i] = Uppercase( AllNumericColumnsCompressed[i] );
		While( Pat Match( AllNumericColumnsCompressed[i], " ", "" ) > 0, Pat Match( AllNumericColumnsCompressed[i], " ", "" ) );
	);

	// This iist is the list of the names used within JMP Contol Limits and is used by the script
	// to set the type of contol limit being updated
	ControlLimitList = {"XBar", "R", "S", "Individual Measurement", "Moving Range", "Median Moving Range", "P", "NP", "C", "U", "UWMA", "EWMA",
	"CUSUM", "Levey Jennings", "Individual on Group Means", "Individual on Group Std Devs", "Moving Range on Group Means",
	"Moving Range on Group Stds", "Median Moving Range on Group Means", "Median Moving Range on Group Std Devs", "Run Chart", "G", "T"};

	// Variale used to set the length of the column selection list
	nc = N Col( dt );

	lbWidth = 160;

	// Create a list of all of the open tables
	list_tables = {};

	For( i = 1, i <= N Table(), i++,
		Insert Into( list_tables, Data Table( i ) << get name )
	);
	
	// Display the Limits Table Selection window
	nw = New Window( "Populate Limits",
		V List Box(
			Spacer Box( size( 1, 5 ) ),
			H List Box( IconBB = Button Box( "" ), IconTB = Text Box( "Populate Limits Column Property from a separate table" ) ),
			Spacer Box( size( 1, 20 ) ), 
	
			TheHlB = H List Box(
				V List Box(
					Text Box( "Select Limits Table" ),
					Spacer Box( size( 1, 2 ) ),
					Panel Box( "Set Limits in " || Char( dt << get name ) || " with",
						TableLB = List Box( list_tables, n lines( 6 ), width( 220 ) ), 

					)
				)
			),
			Spacer Box( size( 1, 10 ) ),
			H List Box(
				Spacer Box( size( 10, 1 ) ),
				createtableBB = Button Box( "Create Limits Table from Current Active Data Table", createlimitstable )
			),
			Spacer Box( size( 1, 10 ) )
		), 	

		// When the user selection for the limits data table changes, reset the column dialog boxes
		TableLB << on change(
		
			// Delete the button box
			Try( createtableBb << delete );
		
			// Blank out the low,avg and high lists
			resetlists;
			
			// Delete the current column dialog boxes
			Try( clusterdlg << delete );
			
			// If the user has selected a new limits table, reset the window
			If( Try( (tablelb << get selected)[1], "" ) != "",
				dtlimits = Data Table( (tablelb << get selected)[1] );
				Current Data Table( dtlimits );
				
				// Reinput the code for the column dialog boxes....this is 
				// required, because the col list boxes do not lose the data
				// table references without totally deleting the col list box
				// and bringing in new instances
				clusterexpr;
				
				// Add the col dialog boxes to the current display
				theHLB << append( clusterdlg );
				
				// Reset required lists
				AllLimitColList = AllLimitColListCompressed = {};
				Try( AllLimitColList = Column( dtlimits, Column Names ) << get values );
				AllLimitColListCompressed = AllLimitColList;
				
				// Uppercase and remove blanks from the column list
				For( i = 1, i <= N Items( AllLimitColList ), i++,
					AllLimitColListCompressed[i] = Uppercase( AllLimitColListCompressed[i] );
					While( Pat Match( AllLimitColListCompressed[i], " ", "" ) > 0, Pat Match( AllLimitColListCompressed[i], " ", "" ) );
				);
			);
		), 
	
	// Set the icon 
		iconbb << set icon( "Update" )
	);

);

/*****************************************************************************/
/*                                                                           */
/*  Create a Limits Table populating limits from the current data table      */
/*                                                                           */
/*****************************************************************************/
CreateLimitsTable = Expr(
	HasBeenSeen = Repeat( 0, 24 );
	
	dtnew = New Table( "Limits Table", invisible, New Column( "Column Names", character, values( AllNumericColumns ) ) );

// Pass through all of the specified parameters and extract any limits found in the
	// active data table and move them to the new limits data table
	For( allvars = 1, allvars <= N Items( AllNumericColumns ), allvars++,  

		col = Parse( "dt:" || AllNumericColumns[allvars] );

	// Gather in the already set Control and Spec Limits
		controls = col << get property( "Control Limits" );
		specs = col << get property( "Spec Limits" );

	// Process Spec Limits
		
		If( !(Char( specs ) == "Scriptable[]" | Is Empty( specs ) == 1),
			If( HasBeenSeen[24] == 0,
				dtnew << New Column( "LSL" );
				dtnew << New Column( "Target" );
				dtnew << New Column( "USL" );
				HasBeenSeen[24] = 1;
			);
			For( i = 1, i <= N Items( specs ), i++,
				If( Is Missing( Try( Specs["LSL"], . ) ) == 0,
					dtnew:LSL[AllVars] = Specs["LSL"]
				);
				If( Is Missing( Try( Specs["Target"], . ) ) == 0,
					dtnew:Target[AllVars] = Specs["Target"]
				);
				If( Is Missing( Try( Specs["LSL"], . ) ) == 0,
					dtnew:USL[AllVars] = Specs["USL"]
				);
			);
		);
		

	
	//  All of the Control limits have the same structure.  This code extracts all of the different
		//  types of control limits and moves them to the limits table
		If( !(Char( controls ) == "Scriptable[]" | Is Empty( controls ) == 1), 
		
			For( i = 1, i <= N Items( controls ), i++, 

				If( Contains( ControlLimitList, Trim( Word( 1, Char( controls[i] ), "(,)" ) ) ) > 0,
					Control Limit Type = Trim( Word( 1, Char( controls[i] ), "(,)" ) );
					LimitListNumber = Contains( ControlLimitList, Trim( Word( 1, Char( controls[i] ), "(,)" ) ) );
					If( HasBeenSeen[LimitListNumber] == 0,
						dtnew << New Column( ControlLimitList[LimitListNumber] || "LCL" );
						dtnew << New Column( ControlLimitList[LimitListNumber] || "AVG" );
						dtnew << New Column( ControlLimitList[LimitListNumber] || "UCL" );
						HasBeenSeen[LimitListNumber] = 1;
					);
			
					For( ctype = 2, ctype <= 6, ctype = ctype + 2,
						If( Trim( Word( ctype, Char( controls[i] ), "(,)" ) ) == "Avg",
							Column( dtnew, ControlLimitList[LimitListNumber] || "AVG" )[AllVars] = Num(
								Word( ctype + 1, Char( controls[i] ), "(,)" )
							)
						);
						If( Trim( Word( ctype, Char( controls[i] ), "(,)" ) ) == "LCL",
							Column( dtnew, ControlLimitList[LimitListNumber] || "LCL" )[AllVars] = Num(
								Word( ctype + 1, Char( controls[i] ), "(,)" )
							)
						);
						If( Trim( Word( ctype, Char( controls[i] ), "(,)" ) ) == "UCL",
							Column( dtnew, ControlLimitList[LimitListNumber] || "UCL" )[AllVars] = Num(
								Word( ctype + 1, Char( controls[i] ), "(,)" )
							)
						);

					);
				)
			);
	  
		);
	);

	If( N Cols( dtnew ) == 1,
		dtnew << New Column( "LSL" );
		dtnew << New Column( "Target" );
		dtnew << New Column( "USL" );
		dtnew << New Column( "XBarLCL" );
		dtnew << New Column( "XBarAVG" );
		dtnew << New Column( "XBarUCL" );
	);

	Insert Into( List_Tables, "Limits Table" );
	x = List_Tables;
	TableLB << RemoveAll;
	TableLB << append( list_tables );

	dtnew << zoom window;
);


/*****************************************************************************/
/*                                                                           */
/* Blank out all selections and reset to the current limit                   */
/*                                                                           */
/*****************************************************************************/
resetLists = Expr(
	lowList = avgList = highList = {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""};
	If( Try( LimitTypeRB << get, 1 ) == 1,
		limitNum = 24,
		LimitNum = cb << get
	);
);

// Initialize the lists
ResetLists;

/*****************************************************************************/
/*                                                                           */
/* Set the Control Limits for all specified control limits for a single      */
/* row from the limits data table                                            */
/*                                                                           */
/*****************************************************************************/
SetControlLimits = Function( {c, CList, IgnoreMissing, i},
	{c, CList, IgnoreMissing}, 
			
	// Big List pulls in any existing limit control values.  This allows for the addition of
	// replacement of limit values without erasing any limits that currently exist.
	c = AllNumericColumns[Contains( AllnumericColumnsCompressed, AllLimitColListCompressed[i] )];
	biglist = Column( dt, c ) << getproperty( "control limits" );
					
		// Roll across all of the types of contol limits found
	For( cl = 1, cl <= N Items( CList ), cl++,
		little = {__type__( Avg( avexpr ), UCL( ULexpr ), LCL( LLexpr ) )};
		Substitute Into( little, Expr( __type__ ), Parse( controllimitnamesList[CList[cl]] ) );
			
		// Get the new limits to be processed from the limits table
		// If no column has been specified for one of the limits, a
		// missing value is set
		lcl = Try( Column( dtlimits, lowlist[CList[cl]] )[i], . );
		tar = Try( Column( dtlimits, avglist[CList[cl]] )[i], . );
		ucl = Try( Column( dtlimits, highlist[CList[cl]] )[i], . );
			
		// If Control Limits were found interrogate them
		If( Is Empty( biglist ) == 0,
			For( _i_ = N Items( big list ), _i_ >= 1, _i_--, 
			
				If( Uppercase( Word( 1, biglist[_i_], "(" ) ) == Uppercase( controllimitnamesList[CList[cl]] ),
					onelist = Char( biglist[_i_] );
					xlcl = .;
					xtarget = .;
					xucl = .;
					For( limitcnt = 2, limitcnt <= 7, limitcnt = limitcnt + 2,
						alimit = Word( limitcnt, onelist, "(,)" );
						If(
							Contains( alimit, "LCL" ) > 0, xlcl = Num( Word( limitcnt + 1, onelist, "(,)" ) ),
							Contains( alimit, "Avg" ) > 0, xtarget = Num( Word( limitcnt + 1, onelist, "(,)" ) ),
							Contains( alimit, "UCL" ) > 0, xucl = Num( Word( limitcnt + 1, onelist, "(,)" ) )
						);
					);
					Remove From( biglist, _i_ );
		
					dtReport << add Rows( 1 );
					dtReport:Column[N Rows( dtReport )] = c;
					dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
					dtReport:Limit[N Rows( dtReport )] = "Control Limit";
					dtReport:OldNew[N Rows( dtReport )] = "Previous";
					dtReport:Level[N Rows( dtReport )] = "Lower";
					dtReport:Value[N Rows( dtReport )] = xlcl;
					dtReport << add Rows( 1 );
					dtReport:Column[N Rows( dtReport )] = c;
					dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
					dtReport:Level[N Rows( dtReport )] = "Average";
					dtReport:Value[N Rows( dtReport )] = xtarget;
					dtReport:Limit[N Rows( dtReport )] = "Control Limit";
					dtReport:OldNew[N Rows( dtReport )] = "Previous";
					dtReport << add Rows( 1 );
					dtReport:Column[N Rows( dtReport )] = c;
					dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
					dtReport:Level[N Rows( dtReport )] = "Upper";
					dtReport:Value[N Rows( dtReport )] = xucl;
					dtReport:Limit[N Rows( dtReport )] = "Control Limit";
					dtReport:OldNew[N Rows( dtReport )] = "Previous";
					
				);					
					
				// If the limits do not currently exist, but other control limits are there, 
				// insert the new control limits into the large control limit list
				If( Is Missing( lcl ) + Is Missing( tar ) + Is Missing( ucl ) > 0, 
					// If Missing values from the limits table are to signal not to 
					// change the current limit, then process them accoringly
					If( IgnoreMissing == "No",
						If( LowList[CList[cl]] != "",
							Substitute Into( little, Expr( LLexpr ), Eval( lcl ) ),
							Substitute Into( little, Expr( LLexpr ), Eval( xlcl ) );
							lcl = xlcl;
						);
							
						If( AvgList[CList[cl]] != "",
							Substitute Into( little, Expr( avexpr ), Eval( tar ) ),
							Substitute Into( little, Expr( avexpr ), Eval( xtarget ) );
							tar = xtarget;
						);
							
						If( HighList[CList[cl]] != "",
							Substitute Into( little, Expr( ULexpr ), Eval( ucl ) ),
							Substitute Into( little, Expr( ULexpr ), Eval( xucl ) );
							ucl = xucl;
						);
					, 
						
						If( Is Missing( lcl ) == 1 | LowList[CList[cl]] == "",
							Substitute Into( little, Expr( LLexpr ), Eval( xlcl ) );
							lcl = xcl;
						,
							Substitute Into( little, Expr( LLexpr ), Eval( lcl ) )
						);
						If( Is Missing( ucl ) == 1 | HighList[CList[cl]] == "",
							Substitute Into( little, Expr( ULexpr ), Eval( xucl ) );
							ucl = xucl;
						,
							Substitute Into( little, Expr( ULexpr ), Eval( ucl ) )
						);
						If( Is Missing( tar ) == 1 | AvgList[CList[cl]] == "",
							Substitute Into( little, Expr( avexpr ), Eval( xtarget ) );
							tar = xtarget;
						,
							Substitute Into( little, Expr( avexpr ), Eval( tar ) )
						);
					)
				, 
					// Just add the new limits if not concerned about preserving data
					// and replacing limits if missing values have been specified
					If( Is Missing( lcl ) + Is Missing( tar ) + Is Missing( ucl ) < 3,
						If( LowList[CList[cl]] != "",
							Substitute Into( little, Expr( LLexpr ), Eval( lcl ) ),
							Substitute Into( little,
								Expr( LLexpr ),
									Eval( xlcl );
									lcl = xlcl;
							)
						);
							
						If( AvgList[CList[cl]] != "",
							Substitute Into( little, Expr( avexpr ), Eval( tar ) ),
							Substitute Into( little, Expr( avexpr ), Eval( xtarget ) );
							tar = xtarget;
						);
							
						If( HighList[CList[cl]] != "",
							Substitute Into( little, Expr( ULexpr ), Eval( ucl ) ),
							Substitute Into( little, Expr( ULexpr ), Eval( xucl ) );
							ucl = xucl;
						);
					)
	
				);
				// If the big list doesn't exist, create it
				If( Is Empty( biglist ) == 1,
					biglist = {}
				);
				// Add the current limit type to the overall list of control limits
				Insert Into( biglist, little );
				
				dtReport << add Rows( 1 );
				dtReport:Column[N Rows( dtReport )] = c;
				dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
				dtReport:Limit[N Rows( dtReport )] = "Control Limit";
				dtReport:OldNew[N Rows( dtReport )] = "New";
				dtReport:Level[N Rows( dtReport )] = "Lower";
				dtReport:Value[N Rows( dtReport )] = lcl;
				dtReport << add Rows( 1 );
				dtReport:Column[N Rows( dtReport )] = c;
				dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
				dtReport:Level[N Rows( dtReport )] = "Average";
				dtReport:Value[N Rows( dtReport )] = tar;
				dtReport:Limit[N Rows( dtReport )] = "Control Limit";
				dtReport:OldNew[N Rows( dtReport )] = "New";
				dtReport << add Rows( 1 );
				dtReport:Column[N Rows( dtReport )] = c;
				dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
				dtReport:Level[N Rows( dtReport )] = "Upper";
				dtReport:Value[N Rows( dtReport )] = ucl;
				dtReport:Limit[N Rows( dtReport )] = "Control Limit";
				dtReport:OldNew[N Rows( dtReport )] = "New";
			)
		,   
			// No control limits currently exist for the target column, so create the large
			// control limit list by setting it equal to the new input limits
		
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
			dtReport:Limit[N Rows( dtReport )] = "Control Limit";
			dtReport:OldNew[N Rows( dtReport )] = "Previous";
			dtReport:Level[N Rows( dtReport )] = "Lower";
			dtReport:Value[N Rows( dtReport )] = .;
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
			dtReport:Level[N Rows( dtReport )] = "Average";
			dtReport:Value[N Rows( dtReport )] = .;
			dtReport:Limit[N Rows( dtReport )] = "Control Limit";
			dtReport:OldNew[N Rows( dtReport )] = "Previous";
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
			dtReport:Level[N Rows( dtReport )] = "Upper";
			dtReport:Value[N Rows( dtReport )] = .;
			dtReport:Limit[N Rows( dtReport )] = "Control Limit";
			dtReport:OldNew[N Rows( dtReport )] = "Previous";
			
			If( Is Missing( lcl ) + Is Missing( tar ) + Is Missing( ucl ) < 3, 
				
				If( LowList[CList[cl]] != "",
					Substitute Into( little, Expr( LLexpr ), Eval( lcl ) ),
					Substitute Into( little, Expr( LLexpr ), Eval( . ) );
					lcl = .;
				);
						
				If( AvgList[CList[cl]] != "",
					Substitute Into( little, Expr( avexpr ), Eval( tar ) ),
					Substitute Into( little, Expr( avexpr ), Eval( . ) );
					tar = .;
				);
					
				If( HighList[CList[cl]] != "",
					Substitute Into( little, Expr( ULexpr ), Eval( ucl ) ),
					Substitute Into( little, Expr( ULexpr ), Eval( . ) );
					ucl = .;
				);
			);
				
				// If the big list doesn't exist, create it
			If( Is Empty( biglist ) == 1,
				biglist = {}
			);
				// Add the current limit type to the overall list of control limits
			Insert Into( biglist, little );
			
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
			dtReport:Limit[N Rows( dtReport )] = "Control Limit";
			dtReport:OldNew[N Rows( dtReport )] = "New";
			dtReport:Level[N Rows( dtReport )] = "Lower";
			dtReport:Value[N Rows( dtReport )] = lcl;
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
			dtReport:Level[N Rows( dtReport )] = "Average";
			dtReport:Value[N Rows( dtReport )] = tar;
			dtReport:Limit[N Rows( dtReport )] = "Control Limit";
			dtReport:OldNew[N Rows( dtReport )] = "New";
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = controllimitnamesList[CList[cl]];
			dtReport:Level[N Rows( dtReport )] = "Upper";
			dtReport:Value[N Rows( dtReport )] = ucl;
			dtReport:Limit[N Rows( dtReport )] = "Control Limit";
			dtReport:OldNew[N Rows( dtReport )] = "New";
		)
				
		;
	);
	
	If( Char( biglist ) != "Scriptable[]" & Is Empty( biglist ) == 0 & biglist != {},
		Column( dt, c ) << setproperty( "control limits", Eval( biglist ) )
	);
);

/*****************************************************************************/
/*                                                                           */
/* Set the Spec Limits for all specified control limits for a single         */
/* row from the limits data table                                            */
/*                                                                           */
/*****************************************************************************/
SetSpecLimits = Function( {c, lsl, usl, avg, IgnoreMissing, showlims},
	{c, lsl, usl, avg, IgnoreMissing, showlims}, 
	
	// Get the current spec limits
	Try( speclist = Column( dt, c ) << get property( "spec limits" ), {} );
	
	Try( xlsl = speclist["LSL"], xlsl = . );
	Try( xusl = speclist["USL"], xusl = . );
	Try( xavg = speclist["Target"], xavg = . );
	
	dtReport << add Rows( 1 );
	dtReport:Column[N Rows( dtReport )] = c;
	dtReport:Limit Type[N Rows( dtReport )] = "_";
	dtReport:Limit[N Rows( dtReport )] = "Spec Limit";
	dtReport:OldNew[N Rows( dtReport )] = "Previous";
	dtReport:Level[N Rows( dtReport )] = "Lower";
	dtReport:Value[N Rows( dtReport )] = xlsl;
	dtReport << add Rows( 1 );
	dtReport:Column[N Rows( dtReport )] = c;
	dtReport:Limit Type[N Rows( dtReport )] = "_";
	dtReport:Level[N Rows( dtReport )] = "Average";
	dtReport:Value[N Rows( dtReport )] = xavg;
	dtReport:Limit[N Rows( dtReport )] = "Spec Limit";
	dtReport:OldNew[N Rows( dtReport )] = "Previous";
	dtReport << add Rows( 1 );
	dtReport:Column[N Rows( dtReport )] = c;
	dtReport:Limit Type[N Rows( dtReport )] = "_";
	dtReport:Level[N Rows( dtReport )] = "Upper";
	dtReport:Value[N Rows( dtReport )] = xusl;
	dtReport:Limit[N Rows( dtReport )] = "Spec Limit";
	dtReport:OldNew[N Rows( dtReport )] = "Previous";

	// Process the limits
	If( Is Missing( lsl ) + Is Missing( avg ) + Is Missing( usl ) < 3, 
		// If misssing value input is to be Ignored, that means that
		// old values of the limits are to be moved forward if the new
		// input values are missing values
		If( IgnoreMissing == "Yes",
			If( (Is Missing( xusl ) == 1 & Is Missing( usl ) == 0) | HighList[24] != "",
				xusl = usl
			);
			If( (Is Missing( xlsl ) == 1 & Is Missing( lsl ) == 0) | LowList[24] != "",
				xlsl = lsl
			);
			If( (Is Missing( xavg ) == 1 & Is Missing( avg ) == 0) | AvgList[24] != "",
				xavg = avg
			);
			cmd = Expr(
				Column( dt, c ) << SetProperty(
					"Spec LImits",
					{LSL( Expr( xlsl ) ), USL( Expr( xusl ) ), Target( Expr( xavg ) ), Show Limits( Expr( showlims ) )}
				)
			);
			
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = "_";
			dtReport:Limit[N Rows( dtReport )] = "Spec Limit";
			dtReport:OldNew[N Rows( dtReport )] = "New";
			dtReport:Level[N Rows( dtReport )] = "Lower";
			dtReport:Value[N Rows( dtReport )] = xlsl;
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = "_";
			dtReport:Level[N Rows( dtReport )] = "Average";
			dtReport:Value[N Rows( dtReport )] = xavg;
			dtReport:Limit[N Rows( dtReport )] = "Spec Limit";
			dtReport:OldNew[N Rows( dtReport )] = "New";
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = "_";
			dtReport:Level[N Rows( dtReport )] = "Upper";
			dtReport:Value[N Rows( dtReport )] = xusl;
			dtReport:Limit[N Rows( dtReport )] = "Spec Limit";
			dtReport:OldNew[N Rows( dtReport )] = "New";
			
		, // Else
			If( LowList[24] == "",
				lsl = xlsl
			);
			If( AvgList[24] == "",
				avg = xavg
			);
			If( HighList[24] == "",
				usl = xusl
			);
			cmd = Expr(
				Column( dt, c ) << SetProperty(
					"Spec LImits",
					{LSL( Expr( lsl ) ), USL( Expr( usl ) ), Target( Expr( avg ) ), Show Limits( Expr( showlims ) )}
				)
			);
			
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = "_";
			dtReport:Limit[N Rows( dtReport )] = "Spec Limit";
			dtReport:OldNew[N Rows( dtReport )] = "New";
			dtReport:Level[N Rows( dtReport )] = "Lower";
			dtReport:Value[N Rows( dtReport )] = lsl;
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = "_";
			dtReport:Level[N Rows( dtReport )] = "Average";
			dtReport:Value[N Rows( dtReport )] = avg;
			dtReport:Limit[N Rows( dtReport )] = "Spec Limit";
			dtReport:OldNew[N Rows( dtReport )] = "New";
			dtReport << add Rows( 1 );
			dtReport:Column[N Rows( dtReport )] = c;
			dtReport:Limit Type[N Rows( dtReport )] = "_";
			dtReport:Level[N Rows( dtReport )] = "Upper";
			dtReport:Value[N Rows( dtReport )] = usl;
			dtReport:Limit[N Rows( dtReport )] = "Spec Limit";
			dtReport:OldNew[N Rows( dtReport )] = "New";
		);
		Eval( Eval Expr( cmd ) );
	);
);

/*****************************************************************************/
/*                                                                           */
/* Run through each of the columns in the limits table and call the functions*/
/* that update the limits, based upon what limits have been specified in the */
/* dialog input screen                                                       */
/*                                                                           */
/*****************************************************************************/
runit = Expr(
	ControlLimitNamesList = {"XBar", "R", "S", "Individual Measurement", "Moving Range", "Median Moving Range", "P", "NP", "C", "U", "UWMA", "EWMA",
	"CUMSUM", "Levey Jennings", "Individual on Group Means", "Individual on Group Std Devs", "Moving Range on Group Means",
	"Moving Range on Group Std Devs", "Median Moving Range on Group Means", "Median Moving Range on Group Std Devs", "Run Chart", "G", "T"}; 
		
	// Create a list of which control limits have been specified
	CList = {};
	For( i = 1, i <= 23, i++,
		If( LowList[i] || avgList[i] || highList[i] != "",
			Insert Into( CList, i )
		)
	); 
		
	// If spec limits have been specified, setup a similar flag to the control limits
	// to use for spec limit processing
	SLList = {};
	If( LowList[24] || avgList[24] || highList[24] != "",
		Insert Into( SLList, 24 )
	); 

	// Get Excluded Rows in the Limits Table so they can be bypassed in processing 
	Excluded Rows = As List( dtLimits << get excluded rows ); 

	// Roll across the rows in the limits table and process the new limits
	For( ii = 1, ii <= N Items( AllLimitColList ), ii++,
		Process = "Yes";
		If( N Items( Excluded Rows ) > 0,
			If( Loc( Excluded Rows, ii ) > 0,
				Process = "No",
				Process = "Yes"
			)
		);

		If( Process == "Yes",
			targetName = AllNumericColumns[Contains( AllnumericColumnsCompressed, AllLimitColListCompressed[ii] )];	
//ii=30			
				// If there are spec limits to be processed, call the setspeclimits function
			If( N Items( SLList ) > 0,
				return = setspeclimits(
					Targetname,
					Try( Column( dtlimits, lowlist[24] )[ii], . ),
					Try( Column( dtlimits, highlist[24] )[ii], . ),
					Try( Column( dtlimits, avglist[24] )[ii], . ),
					IgnoreMissing,
					showLims
				)
			);	
	
	// If Control limit changes have been specified, call the SetControlLimits function
			If( N Items( CList ) > 0,
				return = SetControlLimits( TargetName, CList, IgnoreMissing, ii )
			);
		);
	);
	
	ShowReport;
	
	close(dtLimits, nosave );
	dtLimits = dtOriginalLimits;
	current data table(dt);
);

	
/*****************************************************************************/
/*                                                                           */
/* This is the display for the column selections where the user matches the  */
/* the limit columns from the limit data table with the columns in the       */
/* data table                                                                */
/*                                                                           */
/*****************************************************************************/	
clusterexpr = Expr(
	all Columns List = dtlimits << get column names;
	Current Data Table( dtlimits );
	clusterDlg = V List Box(
		Border Box( Left( 3 ), top( 2 ),
			V List Box(
				Text Box( "Select Columns to Read the Limits From" ),
				H List Box(
					V List Box(
				
						// Generate the master display box for the columns in the data table
						Panel Box( "Select Columns",
							cldvlb = H List Box(
								Popup Box(
									{"Clear", filterval = "" ; thefilter << set text( "" ) ; clearit ; , 
														
									"Numeric", filterval = "" ; thefilter << set text( "" ) ; clearit ; For( II = 1,
										II <= N Items( All Columns List ), II++,
										If( Column( All Columns List[II] ) << get data type != "Numeric",
											colListData << set selected( II )
										)
									) ; colListData << remove selected ; , 
														
									"Character", filterval = "" ; thefilter << set text( "" ) ; clearit ; For( II = 1,
										II <= N Items( All Columns List ), II++,
										If( Column( All Columns List[II] ) << get data type != "Character",
											colListData << set selected( II )
										)
									) ; colListData << remove selected ; , 
														
									"Name Contains", filterval = "" ; filtervlb << prepend(
										V List Box(
											thefilter = Text Edit Box( "",
												<<set width( 175 ),
												<<set script(
													filterval = Trim( thefilter << get text() );
													clearit;
													// This line seems redundant, but proved to be necessary to
													// insure the correct list of data was evaluated
													All Columns List = colListData << get items;
															
															// If a value is in the filter input box, cycle through all
													// of the columns to find the matches
													If( Is Missing( filterval ) == 0,
														For( II = 1, II <= N Items( All Columns list ), II++, 
																	
															If( Contains( Uppercase( Char( All Columns list[II] ) ), Uppercase( filterval ) ) == 0,        
																// If a match is found select the column because
																// all selected items will be removed at the end,
																// leaving only the non matching items
																colListData << set selected( II )
															)
														)
													);
													colListData << remove selected;
												)
											)
										)
									)},
									Spacer Box( size( 1, 4 ) )
								),
								filtervlb = V List Box( colListData = Col List Box( Data Table( dtlimits ), width( lbWidth ), nLines( 15 ) ) )
							)
						),
						colListData << append( dtlimits << get column names ),    
						
							// At startup, sselect the columns in the selection box for 
						// the columns that are selected in the data table 
						For( II = 1, II <= N Items( All Columns list ), II++,
							If( Column( ii ) << get selected == 1,
								colListData << set selected( II )
							)
						)
					),    
				
					// Setup the selection buttons and the selection boxes
					Panel Box( "Cast Selected Columns into Roles",
						Lineup Box( N Col( 2 ), Spacing( 3 ),
							bb1 = Button Box( "ID Column", colListY << Append( colListData << GetSelected ) ),
							colListY = Col List Box( width( lbWidth - 50 ), nLines( 1 ), min col( 1 ), character, max selected( 1 ) )
						),
						V List Box(
							Lineup Box( N Col( 2 ),
								Text Box( "Type of Limit" ),
								Text Box( " " ),
								LimitTypeRB = Radio Box(
									{"Spec Limit", "Control Limit"},
									Try(
										If( LimitTypeRB << get == 1,
											info << set text( "Spec Limits will be set when you select this option and that is that" );
											cb << delete;
											cbsb << delete;
											vlbcb << append( cbsb = Spacer Box( size( 1, 41 ) ) );
											bbl << set button name( "Lower SL" );
											bba << set button name( "Target" );
											bbU << set button name( "Upper SL" );
											WriteLimitColNames( LimitNum );
											LimitNum = 24;
											ReadLimitColNames( LimitNum );
										,
											Info << set text(
												"Select the Control Limit to be populated and then select the columns that contain those limits"
											);
											Try( cbsb << delete );
											vlbcb << append(
												cbsb = V List Box(
													Spacer Box( size( 1, 3 ) ),
													cb = Combo Box(
														ControlLimitList,
														writeLimitColNames( limitNum );
												
														LimitNum = cb << get;
														ReadLimitColNames( limitNum );
													)
												)
											);
											bbl << set button name( "Lower CL" );
											bba << set button name( "Average" );
											bbU << set button name( "Upper CL" );
											WriteLimitColNames( LimitNum );
											LimitNum = cb << get;
											ReadLimitColNames( LimitNum );
										)
									)
								),
								H List Box(
									//Spacer Box( size( 15, 1 ) ),
									V List Box(
										ReportCB = Check Box( {"Show Summary Rpt"} ),
										ShowCB = Check Box( {"Set Show Specs"} ),
										IgnoreCB = Check Box( {"Ignore Missing"} )
									)
								)
							), 
						
							Info = Text Box( "Spec Limits will be set when you select this option and that is that" ), 
										 
							vlbcb = V List Box( cbsb = Spacer Box( size( 1, 39 ) ) )
						), 
						
						// Setup the Button Boxes and Col List Boxes for the column selections
						lineupvlb = V List Box(
							lub = V List Box(
								Lineup Box( N Col( 2 ), Spacing( 3 ),
									bbL = Button Box( "Lower SL", colLower << Append( colListData << GetSelected ) ),
									colLower = Col List Box( width( lbWidth - 50 ), nLines( 1 ), numeric, max selected( 1 ) ),
									bba = Button Box( "Target", colAverage << Append( colListData << GetSelected ) ),
									colAverage = Col List Box( width( lbWidth - 50 ), nLines( 1 ), numeric, max selected( 1 ) ),
									bbU = Button Box( "Upper SL", colUpper << Append( colListData << GetSelected ) ),
									colUpper = Col List Box( width( lbWidth - 50 ), nLines( 1 ), numeric, max selected( 1 ) ), 

								),
								Spacer Box( size( 1, 12 ) ),
								bb1 << set tip( "Column in the Limits table that matches to the column names in the main data table" );
								info << set width( 200 ) << set wrap( 200 );
							)
						)
					), 	
								
					// Setup the action buttons that control the window processing
					V List Box(
						Panel Box( "Action",
							Lineup Box( N Col( 1 ),
								Button Box( "OK",    
							
									WriteLimitColNames( LimitNum );
									
									lowconcat = highconcat = "";
									For( i = 1, i <= N Items( LowList ), i++,
										lowconcat = lowconcat || LowList[i];
										highconcat = highconcat || HighList[i];
									);
									LimitsChoosen = Length( lowconcat || highconcat ) > 0;
								
									// Check to see if the required data has been input
									If(
										N Items( colListY << GetItems ) == 0, Dialog( "No ID Column Specified", Button( "OK" ) ),          // Else
										LimitsChoosen == 0, Dialog( "No Limit Columns Specified", Button( "OK" ) ),      // Else
									
										RecallList = {lowlist = {}, avglist = {}, highlist = {}};
									
										RecallList["lowlist"] = lowlist;
										RecallList["avglist"] = avglist;
										RecallList["highlist"] = highlist;
									
										// Place the selected values from the screen into the recall list
										Insert Into( RecallList, Parse( "IDColumn = \!"" || Char( (colListY << get items)[1] ) || "\!"" ) );
									
										If( IgnoreCB << get == 1,
											IgnoreMissing = "Yes",
											IgnoreMissing = "No"
										);
										ShowLims = ShowCB << get;
										ShowRpt = ReportCB << get;
									
										Insert Into( RecallList, Parse( "IgnoreMissing = \!"" || IgnoreMissing || "\!"" ) );
									
										Insert Into( RecallList, Parse( "ShowLims = " || Char( showlims ) ) );

										Insert Into( RecallList, Parse( "ShowRpt = " || Char( showRpt ) ) );
										
										// Save recall string to Recall namespace
										Recall:WriteLimits = RecallList;					
									
										nw << CloseWindow;
										
										dtReport = New Table( "Report",
											Private,
											New Column( "Column", Character, "Nominal" ),
											New Column( "Limit Type", Character, "Nominal" ),
											New Column( "Level",
												Character,
												"Nominal",
												Set Property( "Value Ordering", {"Lower", "Average", "Upper"} )
											),
											New Column( "Value", Numeric, "Continuous", Format( "Best", 10 ) ),
											New Column( "Limit",
												Character,
												"Nominal",
												Set Property( "Value Ordering", {"Spec Limit", "Control Limit"} )
											),
											New Column( "OldNew", Character, "Nominal", Set Property( "Value Ordering", {"Previous", "New"} ) )
										);
										dtReport:Column << set property( "Row Order Levels", 1 );
										
										// Create a working copy of the limits table
										dtLimits << Select Rows( NotFoundListRowNumbers );
										dtLimits << invert row selection;
										dtLimitsTemp = dtLimits << subset(invisible, selected rows(1), selected columns(0));
										dtLimits << clear select;
										dtOriginalLimits = dtLimits;
										dtLimits = dtLimitsTemp;
										wait(0);

										// Run the actual code that updates the limits						
										runit;
									);
								), 
								
								Button Box( "Cancel",
									clusterDlg << CloseWindow;
									Names Default To Here( 1 );
								),
								Text Box( " " ), 
								
								Button Box( "Remove",
									colListY << RemoveSelected;
									collower << remove selected;
									colaverage << remove selected;
									colupper << remove selected;
								), 
								
								Button Box( "Recall",
									If( Is List( Recall:WriteLimits ) == 1, 
										
										Eval List( Recall:WriteLimits );
										
										If( Try( (colListY << get items)[1], "" ) != IDcolumn,
											colListY << RemoveAll;
											colListY << append( IDcolumn );
										);
										
										If( IgnoreMissing == "Yes",
											IgnoreCB << set( 1 ),
											IgnoreCB << set( 0 )
										);
										
										ShowCB << set( ShowLims );
										
										ReportCB << set( ShowRpt );
										
										readLimitColNames( LimitNum );
									)
								), 
								
								Button Box( "Help", notImplemented )
							)
						)
					), 

				)
			)
		), 
		
		// Set the default values for the check box options
		IgnoreCB << set( 1 );
		IgnoreMissing = "Yes";
		ShowCB << set( 1 );
		ShowLims = 1;
		ReportCB << set( 1 );
		ShowRpt = 1;
	);

	// Write the names to the Limit Lists ffrom the display window
	WriteLimitColNames = Function( {lNum},
		LowList[LNum] = Try( (colLower << get items)[1], "" );
		AvgList[LNum] = Try( (colAverage << get items)[1], "" );
		HighList[LNum] = Try( (colUpper << get items)[1], "" );
	);

	// Read the limit values from the Limit Lists and populate
	// the display window with the values
	ReadLimitColNames = Function( {lNum},
		{lNum},
		tmplist = lowlist;
		ColLower << remove all;
		lowlist = tmplist;
		tmplist = {};
		Insert Into( tmplist, lowlist[lnum] );
		collower << append( tmplist );
	
		tmplist = avglist;
		ColAverage << remove all;
		avglist = tmplist;
		tmplist = {};
		Insert Into( tmplist, avglist[lnum] );
		colAverage << append( tmplist );
	
		tmplist = Highlist;
		ColUpper << remove all;
		Highlist = tmplist;
		tmplist = {};
		Insert Into( tmplist, Highlist[lnum] );
		ColUpper << append( tmplist );
	);
	
	// When the value for the column that contains the names of the data
	// table columns that will have their limits changed, process the list,
	// comparing it to the data table to check for columns not found
	ColListY << On Change(
		If( Try( (ColListY << get items)[1], "" ) != LastColListY,
			If( Try( N Items( colListY << get items ), 0 ) > 0,
				AllLimitColList = AllLimitColListCompressed = NotProcessedColumnsList = NotProcessedColumnsListCompressed = {};
				LastColListY = (ColListY << get items)[1];
				If( Try( (colListY << get items)[1], "" ) != "",
					AllLimitColList = Column( dtlimits, (colListY << get items)[1] ) << get values;
					AllLimitColListCompressed = AllLimitColList;
					NotFoundList = {};
					NotFoundListRowNumbers = {};
			
					// Uppercase and compress the column names
					For( i = N Items( AllLimitColList ), i > 0, i--,
						AllLimitColListCompressed[i] = Uppercase( AllLimitColListCompressed[i] );
						While( Pat Match( AllLimitColListCompressed[i], " ", "" ) > 0, Pat Match( AllLimitColListCompressed[i], " ", "" ) );
						If( Contains( AllNumericColumnsCompressed, AllLimitColListCompressed[i] ) == 0,
							Insert Into( NotFoundList, AllLimitColList[i] );
							Insert Into( NotFoundListRowNumbers, i );
							AllLimitColListCompressed = Remove( AllLimitColListCompressed, i, 1 );
							AllLimitColList = Remove( AllLimitColList, i, 1 );
						);
					);
			
					// Find all of the columns that are not going to be processed
					NotProcessedColumnsListCompressed = NotProcessedColumnsList = dt << get column Names( string );
		
					For( i = N Items( NotProcessedColumnsList ), i > 0, i--,
						NotProcessedColumnsListCompressed[i] = Uppercase( NotProcessedColumnsListCompressed[i] );
						While( Pat Match( NotProcessedColumnsListCompressed[i], " ", "" ) > 0,
							Pat Match( NotProcessedColumnsListCompressed[i], " ", "" )
						);
						If( Contains( AllLimitColListCompressed, NotProcessedColumnsListCompressed[i] ) > 0, 
							//Insert Into( NotFoundList, AllLimitColList[i] );
							NotProcessedColumnsListCompressed = Remove( NotProcessedColumnsListCompressed, i, 1 );
							NotProcessedColumnsList = Remove( NotProcessedColumnsList, i, 1 );
						);
					);
		
					// If there are columns that do not exist in the data table, notify the user
					If( N Items( NotFoundList ) > 0,
						winLocation = nw << get window position;
						err = New Window( "Columns Not Found",
							Border Box( top( 10 ), Left( 10 ), Right( 10 ), bottom( 10 ), sides( 15 ),
								V List Box(
									Spacer Box( size( 1, 10 ) ),
									Text Box( "The following column(s) names(s) were referenced in the" ),
									Text Box( "Limit Table, but no numeric column of that/those name(s)" ),
									Text Box( "were found in the data table." ),
									Spacer Box( size( 1, 10 ) ),
									H List Box( Spacer Box( size( 60, 1 ) ), List Box( NotFoundList ) ),
									Spacer Box( size( 1, 10 ) ),
									Text Box( "No limits will be set for the above referenced columns" ),
									Spacer Box( size( 1, 10 ) ),
									H List Box(
										continueBB = Button Box( "Continue Anyway", err << close window ),
										stopBB = Button Box( "Stop Script",
											err << close window;
											nw << close window;
											Throw();
										)
									)
								)
							)
						);
						err << move window( winLocation[1] + 100, winLocation[2] + 100 ) << zoom window;
					);
				);
			)
		)
	);
);

/*****************************************************************************/
/*                                                                           */
/* An Expr to run for any function that has not been implemented.  It        */
// displays a dialog window telling the user that the function has not been  */
/* implemented yet                                                           */
/*                                                                           */
/*****************************************************************************/
notImplemented = Expr(
	Dialog( "Feature Not Implemented Yet", Button( "OK" ) )
);

/*****************************************************************************/
/*                                                                           */
/* Code used in the column selection filter to reset the selection box       */
/*                                                                           */
/*****************************************************************************/
clearit = Expr(
	
	For( II = 1, II <= N Items( colListData << get items ), II++,
		colListData << set selected( II )
	);
	colListData << remove selected;
	colListData << append( all columns list );
	

);

// Call the main expr as the inital piece of script to be run
main;


biv6 = Graph Builder(
	Variables( X( :PPO_26_H ) ),
	Show Control Panel( 0 ),
	Elements( Histogram( X, Legend( 4 ) ), Box Plot( X, Legend( 6 ) ) ),
	SendToReport(
		Dispatch( {}, "PPO_26_H", ScaleBox, {Label Row( Set Font Size( 16 ) )} ),
		Dispatch( {}, "graph title", TextEditBox, {Set Font Size( 24 )} )
	)
);

biv6 << save picture("c:\temp\biv6.png");

 

 

txnelson

JSL does not stop processing a script, just because a window has been displayed.  Therefore, as the window is being displayed, your "Biv6" graph builder is being executed.  You need to change the input dialog box code, to tell it to run the "Biv6" code at the end of the code that is adding the spec limits.

If you change your Graph Builder code to be encapsulated into an Expr() function, it will not be run until the Expr() is executed.

myGraphBuilder = Expr(
	biv6 = Graph Builder(
		Variables( X( :PPO_26_H ) ),
		Show Control Panel( 0 ),
		Elements( Histogram( X, Legend( 4 ) ), Box Plot( X, Legend( 6 ) ) ),
		SendToReport(
			Dispatch( {}, "PPO_26_H", ScaleBox, {Label Row( Set Font Size( 16 ) )} ),
			Dispatch( {}, "graph title", TextEditBox, {Set Font Size( 24 )} )
		)
	);

	biv6 << save picture( "c:\temp\biv6.png" );
);

So, now you just have to decide when to have the dialog box execute the Graph Builder.  My suggestion is to place it into the button box  "OK"  script, right after the execution of the "runit" Expr().

Button Box( "OK",    
							
									WriteLimitColNames( LimitNum );
									
									lowconcat = highconcat = "";
									For( i = 1, i <= N Items( LowList ), i++,
										lowconcat = lowconcat || LowList[i];
										highconcat = highconcat || HighList[i];
									);
									LimitsChoosen = Length( lowconcat || highconcat ) > 0;
								
									// Check to see if the required data has been input
									If(
										N Items( colListY << GetItems ) == 0, Dialog( "No ID Column Specified", Button( "OK" ) ),          // Else
										LimitsChoosen == 0, Dialog( "No Limit Columns Specified", Button( "OK" ) ),      // Else
									
										RecallList = {lowlist = {}, avglist = {}, highlist = {}};
									
										RecallList["lowlist"] = lowlist;
										RecallList["avglist"] = avglist;
										RecallList["highlist"] = highlist;
									
										// Place the selected values from the screen into the recall list
										Insert Into( RecallList, Parse( "IDColumn = \!"" || Char( (colListY << get items)[1] ) || "\!"" ) );
									
										If( IgnoreCB << get == 1,
											IgnoreMissing = "Yes",
											IgnoreMissing = "No"
										);
										ShowLims = ShowCB << get;
										ShowRpt = ReportCB << get;
									
										Insert Into( RecallList, Parse( "IgnoreMissing = \!"" || IgnoreMissing || "\!"" ) );
									
										Insert Into( RecallList, Parse( "ShowLims = " || Char( showlims ) ) );

										Insert Into( RecallList, Parse( "ShowRpt = " || Char( showRpt ) ) );
										
										// Save recall string to Recall namespace
										Recall:WriteLimits = RecallList;					
									
										nw << CloseWindow;
										
										dtReport = New Table( "Report",
											Private,
											New Column( "Column", Character, "Nominal" ),
											New Column( "Limit Type", Character, "Nominal" ),
											New Column( "Level",
												Character,
												"Nominal",
												Set Property( "Value Ordering", {"Lower", "Average", "Upper"} )
											),
											New Column( "Value", Numeric, "Continuous", Format( "Best", 10 ) ),
											New Column( "Limit",
												Character,
												"Nominal",
												Set Property( "Value Ordering", {"Spec Limit", "Control Limit"} )
											),
											New Column( "OldNew", Character, "Nominal", Set Property( "Value Ordering", {"Previous", "New"} ) )
										);
										dtReport:Column << set property( "Row Order Levels", 1 );
										
										// Create a working copy of the limits table
										dtLimits << Select Rows( NotFoundListRowNumbers );
										dtLimits << invert row selection;
										dtLimitsTemp = dtLimits << subset(invisible, selected rows(1), selected columns(0));
										dtLimits << clear select;
										dtOriginalLimits = dtLimits;
										dtLimits = dtLimitsTemp;
										wait(0);

										// Run the actual code that updates the limits						
										runit;
                                                                                myGraphBuilder;
									);
								), 
								
								
Song

@txnelson Thanks a lot for your help!!

Above script work well!