The first thing I needed to do was write a WSF file to load the library with the jshell REPL I created in my last post. I then found a nice Javascript library online (http://numericjs.com/numeric/index.php) and integrated this library with my jshell.js REPL script into the WSF file. I called my file jlab.wsf.
A blog for my notes on perl and various other programming languages and applications I work on.
Friday, November 14, 2014
Quick and dirty Matlab/Octave replacement using JScript
The first thing I needed to do was write a WSF file to load the library with the jshell REPL I created in my last post. I then found a nice Javascript library online (http://numericjs.com/numeric/index.php) and integrated this library with my jshell.js REPL script into the WSF file. I called my file jlab.wsf.
Tuesday, November 4, 2014
Making new Windows commands using Windows Scripting Host and Jscript
I thought that a good way to learn more about Javascript would be to build some small tools for the machines I use at work. I've learned that Javascript on Windows machines come in at least 3 different flavors, Jscript hosted on the Windows Scripting Host, Jscript .NET compiled with the jsc compiler, and the Jscript running on Internet Explorer and Firefox. Not only that but doing things on Windows XP turns out to be slightly different than on Windows 7. Take for example embedding Jscript in a batch file so that I can call the tool from anywhere on the command line. What I found on StackOverflow was this cool calendar example:
REM Let's call this command "cal"
@set @junk=1 /*
@echo off
cscript //nologo //E:jscript %0 %*
goto :eof
*/
x = WScript.Arguments
Yr = x(0) ; Mo = x(1)
YS = "JanFebMarAprMayJunJulAugSepOctNovDec"
MN = Mo<1 || Mo>12 ? Mo : YS.substr(3*Mo-3, 3) // Month Name
WScript.echo(" ", Yr, " ", MN)
WScript.echo(" Mo Tu We Th Fr Sa Su")
WD = new Date(Yr, Mo-1, 1).getDay() ;
if (WD==0) WD = 7 // Week Day Number of 1st
LD = new Date(Yr, Mo, 0).getDate() // Last Day of month
Wk = "" ; for (D=1 ; D < WD ; D++) Wk += " "
for (D=1 ; D<=LD ; D++) {
Wk = Wk + " " + (D<10 ? "0"+D : D) ; WD++
if ((WD==8) || (D==LD)) { WScript.echo(Wk) ; WD = WD-7 ; Wk = "" }
}
WScript.echo(" ------ ")
@if (@CodeSection == @Batch) @then
@echo off
cscript //nologo //E:jscript %~f0 %*
goto :eof
@endMo Tu We Th Fr Sa Su
01 02
03 04 05 06 07 08 09
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
---------------------------------
@echo off
cscript //nologo //E:jscript %~f0 %*
goto :eof
@end
function hex(n) {
if (n >= 0) {
return n.toString(16);
} else {
n += 0x100000000;
return n.toString(16);
}
}
var scriptText;
var previousLine;
var line;
var result;
while(true) {
WScript.StdOut.Write("jscript> ");
if (WScript.StdIn.AtEndOfStream) {
WScript.Echo("Bye.");
break;
}
line = WScript.StdIn.ReadLine();
scriptText = line + "\n";
if (line === "") {
WScript.Echo(
"Enter two consecutive blank lines to terminate multi-line input.");
do {
if (WScript.StdIn.AtEndOfStream) {
break;
}
previousLine = line;
line = WScript.StdIn.ReadLine();
line += "\n";
scriptText += line;
} while(previousLine != "\n" || line != "\n");
}
try {
result = eval(scriptText);
} catch (error) {
WScript.Echo("0x" + hex(error.number) + " " + error.name + ": " +
error.message);
}
if (result) {
try {
WScript.Echo(result);
} catch (error) {
WScript.Echo("<<
}
}
result = null;
}
One application I have in mind for using Jscript at work is as a quick and small footprint command line calculator. In the past I have used Matlab, Google Sheets, the basic Windows calculator, and Excel. Most of these are overkill for what I need to do, and Windows calculator is too basic. Hard-coding the calculations into the command line sounds a lot more convenient for the things I need to do. Here is calculation I have been doing a lot recently on Google Sheets. It realigns the offset for the camera I am looking through to the desired coordinate system.
@echo off
cscript //nologo //E:jscript %~f0 %*
goto :eof
@end
// realign.bat
// Jovan Trujill
// Arizona State University
// 11/04/2014
//
// Usage: realign XActual YActual XDesired YDesired XOffset_Old YOffset_Old
// This command will return the alignment offset coordinates needed to correct
// camera positioning problems on the Cherry prober system.
//
var X_Actual = 0.0;
var Y_Actual = 0.0;
var X_Desired = 0.0;
var Y_Desired = 0.0;
var dX = 0.0;
var dY = 0.0;
var X_Offset_Old = 0.0;
var Y_Offset_Old = 0.0;
var X_Offset_New = 0.0;
var Y_Offset_New = 0.0;
var args = WScript.Arguments;
if (args.length != 6) {
WScript.echo("Usage: realign XActual YActual XDesired YDesired OldXOffset OldYOffset");
} else {
X_Actual = parseInt(args(0));
Y_Actual = parseInt(args(1));
X_Desired = parseInt(args(2));
Y_Desired = parseInt(args(3));
X_Offset_Old = parseInt(args(4));
Y_Offset_Old = parseInt(args(5));
dX = X_Desired - X_Actual;
dY = Y_Desired - Y_Actual;
X_Offset_New = X_Offset_Old - dX;
Y_Offset_New = Y_Offset_Old + dY;
WScript.echo("X_Actual Y_Actual");
WScript.echo(X_Actual + " " + Y_Actual);
WScript.echo("X_Desired Y_Desired");
WScript.echo(X_Desired + " " + Y_Desired);
WScript.echo("X_Old_Offset Y_Old_Offset");
WScript.echo(X_Offset_Old + " " + Y_Offset_Old);
WScript.echo("X_New_Offset Y_New_Offset");
WScript.echo(X_Offset_New + " " + Y_Offset_New);
}
As the library of functions gets more complicated I will need to figure out a way to call JScript functions from other JScript programs. I think the most convenient way to do that will be:
/* include.js */
(function () {
var L = {/* library interface */};
L.hello = function () {return "greetings!";};
return L;
}).call();
And then the main function calls "include.js" like this:
var Fs = new ActiveXObject("Scripting.FileSystemObject");
var Lib = eval(Fs.OpenTextFile("include.js", 1).ReadAll());
WScript.echo(Lib.hello()); /* greetings! */
Tuesday, March 18, 2014
The day of reckoning has come: Upgrading Excel 2003 VBA code to Excel 2010.
Excel 2003 Code:
Set fs = Application.FileSearch
With fs
.NewSearch
.LookIn = vrtSelectedItem
.SearchSubFolders = True
.Filename = "Macro_I"
.MatchExactly = False
.FileType = msoFileTypeExcelWorkbooks
If .Execute() > 0 Then
For i = 1 To .FoundFiles.Count
Call StuffCore(.FoundFiles(i), summaryType)
Next i
End If
End With
The problem is that Application.FileSearch no longer exists in Excel 2010. Therefore a replacement for this function needed to be found. Others have had the same problem and posted a replacement function here.
The previous code snippet now becomes the following.
Excel 2010 Code:
Dim foundFiles() As FoundFileInfo
Dim foundFilesCount As Integer
searchPattern = "Macro_I*"
recursiveSearch = True
Dim boolFoundFiles As Boolean
boolFoundFiles = FindFiles(vrtSeclectedItem, foundFiles, foundFilesCount, searchPattern, recursiveSearch)
If boolFoundFiles = True Then
For i = 1 To foundFilesCount
With foundFiles(i)
Call StuffCore(.sPath & .sName, summaryType)
End With
Next i
End If
For this code to work a new data type must be declared globally outside of all subroutines. This is the "FoundFileInfo" type and it is created with the following code.
FoundFileInfo type declaration:
Type FoundFileInfo
sPath As String
sName As String
End Type
And the "FindFiles" function is defined as follows.
FindFiles function definition:
Function FindFiles(ByVal sPath As String, _
ByRef recFoundFiles() As FoundFileInfo, _
ByRef iFilesFound As Integer, _
Optional ByVal sFileSpec As String = "*.*", _
Optional ByVal blIncludeSubFolders As Boolean = False) As Boolean
'
' FindFiles
' ---------
' Finds all files matching the specified file spec starting from the specified path and
' searches sub-folders if required.
'
' Parameters
' ----------
' sPath (String): Start-up folder, e.g. "C:\Users\Username\Documents"
'
' recFoundFiles (User-defined data type): a user-defined dynamic array to store the path
' and name of found files. The dimension of this array is (1 To nnn), where nnn is the
' number of found files. The elements of this array are:
' .sPath (String) = File path
' .sName (String) = File name
'
' iFilesFound (Integer): Number of files found.
'
' sFileSpec (String): Optional parameter with default value = "*.*"
'
' blIncludeSubFolders (Boolean): Optional parameter with default value = False
' (which means sub-folders will not be searched)
'
' Return values
' -------------
' True: One or more files found, therefore
' recFoundFiles = Array of paths and names of all found files
' iFilesFound = Number of found files
' False: No files found, therefore
' iFilesFound = 0
'
' Using the function (sample code)
' --------------------------------
' Dim iFilesNum As Integer
' Dim iCount As Integer
' Dim recMyFiles() As FoundFileInfo
' Dim blFilesFound As Boolean
'
' blFilesFound = FindFiles("C:\Users\MBA\Desktop", _
' recMyFiles, iFilesNum, "*.txt?", True)
' If blFilesFound Then
' For iCount = 1 To iFilesNum
' With recMyFiles(iCount)
' MsgBox "Path:" & vbTab & .sPath & _
' vbNewLine & "Name:" & vbTab & .sName, _
' vbInformation, "Found Files"
' End With
' Next
' Else
' MsgBox "No file(s) found matching the specified file spec.", _
' vbInformation, "File(s) not Found"
' End If
'
'
' Constructive comments and Reporting of bugs would be
' appreciated.
Dim iCount As Integer '* Multipurpose counter
Dim sFileName As String '* Found file name
'*
'* FileSystem objects
Dim oFileSystem As Object, _
oParentFolder As Object, _
oFolder As Object, _
oFile As Object
Set oFileSystem = CreateObject("Scripting.FileSystemObject")
On Error Resume Next
Set oParentFolder = oFileSystem.GetFolder(sPath)
If oParentFolder Is Nothing Then
FindFiles = False
On Error GoTo 0
Set oParentFolder = Nothing
Set oFileSystem = Nothing
Exit Function
End If
sPath = IIf(Right(sPath, 1) = "\", sPath, sPath & "\")
'*
'* Find files
'sPath = sPath & sFileSpec
sFileName = Dir(sPath & sFileSpec, vbNormal)
If sFileName <> "" Then
For Each oFile In oParentFolder.Files
If LCase(oFile.Name) Like LCase(sFileSpec) Then
iCount = UBound(recFoundFiles)
iCount = iCount + 1
ReDim Preserve recFoundFiles(1 To iCount)
With recFoundFiles(iCount)
.sPath = sPath
.sName = oFile.Name
End With
End If
Next oFile
Set oFile = Nothing '* Although it is nothing
End If
If blIncludeSubFolders Then
'*
'* Select next sub-forbers
For Each oFolder In oParentFolder.SubFolders
FindFiles oFolder.Path, recFoundFiles, iFilesFound, sFileSpec, blIncludeSubFolders
Next
End If
FindFiles = UBound(recFoundFiles) > 0
iFilesFound = UBound(recFoundFiles)
On Error GoTo 0
'*
'* Clean-up
Set oFolder = Nothing '* Although it is nothing
Set oParentFolder = Nothing
Set oFileSystem = Nothing
End Function
Another database stuffing macro needed a syntax change for the Sort method. The old code looked like this:
Old range sorting code:
' Define the range for the sort
Range("A" & SortStart & ":" & "BH" & SortEnd).Select
Selection.Sort Key1:=Range("B1"), Order1:=xlAscending, _
Header := xlNo, custom:=1, MatchCase:=False, _
Orientation:=xlTopToBottom, DataOption1:=xlSortNormal
New range sorting code:
With ActiveWorkbook.Worksheets(ActiveSheet.Name).Sort
.SortFields.Clear
.SortFields.Add Key:=Range("B1"), _
SortOn:=xlSortOnValues, Order:=xlAscending, _
DataOption:=xlSortNormal
.SetRange Range("A" & SortStart & ":" & "BH" & SortEnd)
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.Apply
End With
Who knows what else we will need to fix. We have a dozen or so more macro to test on Excel 2010. I will keep taking notes here for future reference.
Monday, November 25, 2013
Drawing lines in Gimp using Python-fu
import math
from gimpfu import *
rows = 640
cols = 480
def draw_vertical_lines(drawable,pixel,rows,cols,width):
for i in range(1,rows):
for j in range(1,cols,width):
pdb.gimp_drawable_set_pixel(drawable,i,j,3,pixel)
return
img = gimp.Image(cols,rows,RGB)
layer_one = gimp.Layer(img,"Layer1",cols,rows,RGB_IMAGE,100,NORMAL_MODE)
pdb.gimp_edit_fill(layer_one,BACKGROUND_FILL)
img.add_layer(layer_one,0)
pdb.gimp_edit_fill(layer_one,BACKGROUND_FILL)
gimp.Display(img)
pixel = [0.0,0.0,0.0,0.0]
draw_vertical_lines(layer_one, pixel, rows, cols, 25)
Thursday, September 12, 2013
Today's script
#!/usr/bin/perl
use strict;
use warnings;
if ($#ARGV != 0) {
print "ARGV = ", $#ARGV, "\n";
print "Usage: FDC46CountLines.pl \n";
exit(1);
}
my $rootPath = $ARGV[0];
my @lotList = ();
my $dataFile = ();
my @dataLine = ();
my @X = ();
my @Y = ();
my @radius = ();
my $tempPath;
my $i;
my $j;
my $panel;
my $lot;
my $lineCount = 0;
my $outputFile = '';
# Generate list of subfolders
opendir DIR, $rootPath or die "Couldn't open dir $rootPath: $!\n";
my @folders = readdir DIR;
closedir DIR;
foreach (@folders) {
if (-d $rootPath . "\\" . $_) {
if ($_ eq ".") {}
elsif($_ eq "..") {}
else {
push(@lotList, $_);
}
}
}
my %lotHash = ();
my $currentLot;
#Generate list of lot folders
foreach (@lotList) {
$tempPath = $rootPath . "\\" . $_;
$currentLot = $_;
opendir DIR, $tempPath or die "Couldn't open dir $tempPath: $!\n";
@folders = readdir DIR;
closedir DIR;
# Generate list of process folders
foreach (@folders) {
if (-d $tempPath . "\\" . $_) {
if ($_ eq ".") {}
elsif($_ eq "..") {}
else {
push(@{$lotHash{$currentLot}}, $_);
}
}
}
}
@folders = ();
for my $key (keys %lotHash) {
for ($i=0; $i <= $#{$lotHash{$key}}; $i++) {
$tempPath = $rootPath . "\\" . $key . "\\" . $lotHash{$key}[$i];
$lot = $key;
$panel = $lotHash{$key}[$i];
print "tempPath = " . $tempPath . "\n";
print "lot = " . $lot . "\n";
print "panel = " . $panel . "\n";
$dataFile = $tempPath . "\\" . $lot . "_" . $panel . "_Notes.txt";
if (open(DATA, "<$dataFile")) {
while () {
@dataLine = split;
push(@X,$dataLine[3]);
push(@Y,$dataLine[4]);
push(@radius, sqrt($dataLine[3]**2+$dataLine[4]**2));
}
} else { print "dataFile does not exist"; }
@radius = sort(@radius);
$outputFile = $tempPath . "\\" . $lot . "_" . $panel . "_defectCount.txt";
open(OUTPUT, ">$outputFile") or die $!;
$lineCount = 0;
for ($j = 1; $j <= $#radius; $j++) {
if (($radius[$j] - $radius[$j-1]) > 100.0) {
print OUTPUT "$X[$j]\t$Y[$j]\n";
$lineCount += 1;
}
}
print OUTPUT "total unique defects were: $lineCount\n";
@dataLine = ();
@X = ();
@Y = ();
@radius = ();
close OUTPUT;
}
}
Friday, August 9, 2013
Learning assembly in my spare moments
Sunday, August 4, 2013
Fun times at Project Euler.
Sunday, July 21, 2013
Fun with OpenVMS
Thursday, April 25, 2013
My first Arduino project
We decided to control the 9x5 LED matrix using 14 digital outputs on the Arduino Micro because it simplified the hardware design.
/* RS232_MATRIX_CONTROL_v2p0 Single character driver for LED matrix display. Current code is hardcoded to display 46 characters on a 9 row by 5 column matrix. Each character is sent through serial terminal followed by a carriage return. Jovan Trujillo Arizona State University 03/27/2013 */ byte RN[] = {4,5,6,7,8,9,10,11,12}; byte CN[] = {23,22,21,20,19}; byte LED = 13; byte strobe = 2; // strobe rate for each row of matrix in milliseconds. byte inByte = 0; // Input character received from serial port. byte idx = 0; // Generic index counter. byte i = 0; // Generic index counter. int exposure = 250; // Number of times entire character is displayed. // Exposure time is calculated to be: strobe x # rows x exposure byte rowcnt = 9; // Number of rows to display for matrix arrays that have fewer than 9 rows. // Character database contains the column bit patterns for each row in an array. // Character database is hardcoded for a 9x5 LED matrix. byte D_0[] = {17,14,14,12,10,6,14,14,17}; // 1 byte D_1[] = {3,27,27,27,27,26,26,26,0}; // 2 byte D_2[] = {17,14,30,30,30,17,15,15,16}; // 3 byte D_3[] = {1,30,30,30,17,30,30,30,1}; // 4 byte D_4[] = {15,15,14,14,14,0,30,30,30}; // 5 byte D_5[] = {16,23,23,23,16,30,30,14,17}; // 6 byte D_6[] = {23,15,15,15,15,0,14,14,0}; // 7 byte D_7[] = {0,14,30,30,29,27,27,27,27}; // 8 byte D_8[] = {17,21,21,21,0,14,14,14,0}; // 9 byte D_9[] = {0,14,14,0,30,30,30,30,29}; // 10 byte D_A[] = {27,21,14,14,14,0,14,14,14}; // 11 byte D_B[] = {1,14,14,14,1,14,14,14,1}; // 12 byte D_C[] = {24,23,15,15,15,15,15,23,24}; // 13 byte D_D[] = {3,21,22,22,22,22,22,21,3}; // 14 byte D_E[] = {0,15,15,15,3,15,15,15,0}; // 15 byte D_F[] = {0,15,15,3,15,15,15,15,15}; // 16 byte D_G[] = {24,23,15,15,15,12,14,14,17}; // 17 byte D_H[] = {14,14,14,14,0,14,14,14,14}; // 18 byte D_I[] = {0,27,27,27,27,27,27,27,0}; // 19 byte D_J[] = {30,30,30,30,30,30,14,14,17}; // 20 byte D_K[] = {14,13,11,7,15,7,11,13,14}; // 21 byte D_L[] = {15,15,15,15,15,15,15,15,0}; // 22 byte D_M[] = {14,4,10,10,10,14,14,14,14}; // 23 byte D_N[] = {14,6,6,10,10,10,12,12,14}; // 24 byte D_O[] = {27,21,14,14,14,14,14,21,27}; // 25 byte D_P[] = {1,14,14,14,1,15,15,15,15}; // 26 byte D_Q[] = {30,26,22,14,14,14,10,9,22}; // 27 byte D_R[] = {1,14,14,1,15,7,11,13,14}; // 28 byte D_S[] = {17,14,15,23,27,29,30,14,17}; // 29 byte D_T[] = {0,10,27,27,27,27,27,27,27}; // 30 byte D_U[] = {14,14,14,14,14,14,14,14,0}; // 31 byte D_V[] = {14,14,14,14,14,14,14,21,27}; // 32 byte D_W[] = {14,14,14,10,10,10,10,10,21}; // 33 byte D_X[] = {14,14,14,21,27,21,14,14,14}; // 34 byte D_Y[] = {14,14,14,21,27,27,27,27,27}; // 35 byte D_Z[] = {0,30,30,21,27,21,15,15,0}; // 36 byte COLON[] = {31,17,17,17,31,17,17,17,31}; // 37 byte SEMICOLON[] = {17,17,17,31,17,17,17,25,23}; // 38 byte LESSTHAN[] = {30,29,27,23,15,23,27,29,30}; // 39 byte EQUAL[] = {31,31,0,31,31,31,0,31,31}; // 40 byte GRTRTHAN[] = {15,23,27,29,30,29,27,23,15}; // 41 byte QUESTION[] = {17,14,30,25,27,27,31,27,31}; // 42 byte D_AT[] = {31,0,14,8,10,8,15,0,31}; // 43 byte D_DASH[] = {31,31,31,0,0,0,31,31,31}; // 44 byte D_SPC[] = {31,31,31,31,31,31,31,31,31}; // 45 byte D_PER[] = {31,31,31,31,31,31,17,17,17}; // 46 byte D_BLK[] = {0,0,0,0,0,0,0,0,0}; // 47 char datalist[47]; // datalist holds an array of all available chracters to pattern match. byte *decodelist[47]; // decodelist holds an array of memory references to the bit patterns for each character. byte *refval = 0; // refval contains a reference to the bit pattern for a specific character entered through the serial terminal. // the setup routine runs once when you press reset: void setup() { // Initialize datalist with valid characters. datalist[0] = '0'; datalist[1] = '1'; datalist[2] = '2'; datalist[3] = '3'; datalist[4] = '4'; datalist[5] = '5'; datalist[6] = '6'; datalist[7] = '7'; datalist[8] = '8'; datalist[9] = '9'; datalist[10] = 'A'; datalist[11] = 'B'; datalist[12] = 'C'; datalist[13] = 'D'; datalist[14] = 'E'; datalist[15] = 'F'; datalist[16] = 'G'; datalist[17] = 'H'; datalist[18] = 'I'; datalist[19] = 'J'; datalist[20] = 'K'; datalist[21] = 'L'; datalist[22] = 'M'; datalist[23] = 'N'; datalist[24] = 'O'; datalist[25] = 'P'; datalist[26] = 'Q'; datalist[27] = 'R'; datalist[28] = 'S'; datalist[29] = 'T'; datalist[30] = 'U'; datalist[31] = 'V'; datalist[32] = 'W'; datalist[33] = 'X'; datalist[34] = 'Y'; datalist[35] = 'Z'; datalist[36] = ':'; datalist[37] = ';'; datalist[38] = '<'; datalist[39] = '='; datalist[40] = '>'; datalist[41] = '?'; datalist[42] = '@'; datalist[43] = '-'; datalist[44] = ' '; datalist[45] = '.'; datalist[46] = '*'; // Initialize decodelist with references to the bit patterns for valid characters. decodelist[0] = D_0; decodelist[1] = D_1; decodelist[2] = D_2; decodelist[3] = D_3; decodelist[4] = D_4; decodelist[5] = D_5; decodelist[6] = D_6; decodelist[7] = D_7; decodelist[8] = D_8; decodelist[9] = D_9; decodelist[10] = D_A; decodelist[11] = D_B; decodelist[12] = D_C; decodelist[13] = D_D; decodelist[14] = D_E; decodelist[15] = D_F; decodelist[16] = D_G; decodelist[17] = D_H; decodelist[18] = D_I; decodelist[19] = D_J; decodelist[20] = D_K; decodelist[21] = D_L; decodelist[22] = D_M; decodelist[23] = D_N; decodelist[24] = D_O; decodelist[25] = D_P; decodelist[26] = D_Q; decodelist[27] = D_R; decodelist[28] = D_S; decodelist[29] = D_T; decodelist[30] = D_U; decodelist[31] = D_V; decodelist[32] = D_W; decodelist[33] = D_X; decodelist[34] = D_Y; decodelist[35] = D_Z; decodelist[36] = COLON; decodelist[37] = SEMICOLON; decodelist[38] = LESSTHAN; decodelist[39] = EQUAL; decodelist[40] = GRTRTHAN; decodelist[41] = QUESTION; decodelist[42] = D_AT; decodelist[43] = D_DASH; decodelist[44] = D_SPC; decodelist[45] = D_PER; decodelist[46] = D_BLK; // Initialize serial and wait for port to open: Serial.begin(9600); Serial.println("Started...\n"); // initialize the digital pin as an output. pinMode(4, OUTPUT); pinMode(5, OUTPUT); pinMode(6, OUTPUT); pinMode(7, OUTPUT); pinMode(8, OUTPUT); pinMode(9, OUTPUT); pinMode(10, OUTPUT); pinMode(11, OUTPUT); pinMode(12, OUTPUT); pinMode(19, OUTPUT); pinMode(20, OUTPUT); pinMode(21, OUTPUT); pinMode(22, OUTPUT); pinMode(23, OUTPUT); // Initialize LED matrix to OFF state. This will have to change for different array dimensions. for (i=0; i<=8; i++) { digitalWrite(RN[i], HIGH); } for (i=0; i<=4; i++) { digitalWrite(CN[i], LOW); } } // the loop routine runs over and over again forever: void loop() { // read from port 3 // TODO: Need to shut off serial port if using pins 0 and 1 for digital I/O while (Serial.available() > 0) { inByte = Serial.read(); // Find serial input character in datalist and set refval to bit pattern reference based on datalist index. if (Serial.read() == '\r') { idx = exposure; for (i=0; i<=46; i++) { if (datalist[i] == inByte) { idx = i; } } refval = decodelist[idx]; if (idx != exposure) { idx = 0; } // Write bit pattern to LED matrix. while (idx < exposure) { for (i=0; i<rowcnt; i++) { digitalWrite(RN[i], LOW); digitalWrite(CN[4], !bitRead(refval[i],4)); digitalWrite(CN[3], !bitRead(refval[i],3)); digitalWrite(CN[2], !bitRead(refval[i],2)); digitalWrite(CN[1], !bitRead(refval[i],1)); digitalWrite(CN[0], !bitRead(refval[i],0)); delay(strobe); digitalWrite(RN[i],HIGH); } digitalWrite(RN[i], HIGH); digitalWrite(CN[0], LOW); digitalWrite(CN[1], LOW); digitalWrite(CN[2], LOW); digitalWrite(CN[3], LOW); digitalWrite(CN[4], LOW); idx = idx + 1; } Serial.println(inByte, DEC); } } }
Friday, January 4, 2013
Data file conversion script
It's been a while since I have posted anything to this blog. In my memory the last use of Perl for work was a file parser that did a coordinate transform on one of the fields. I think it's some of the more complicated regular expression applications I have made so far.
The file takes defectivity data generated by an Orbotech display circuit scanner and converts it to a format that is compatible with a TNP laser tool. With the TNP laser we can move to the defective coordinates found by the Orbotech and attempt to salvage the circuit using laser ablation.
# Orbotech_to_TNP_Converter_20111121.pl
#
# Jovan Trujillo
# Arizona State University
# 11/21/2011
#
# This script formats an Orbotech file for use with the TNP Laser system.
#
use strict;
if ($#ARGV != 0) {
print "ARGV = ", $#ARGV, "\n";
print "Usage: Orbotech_to_TNP_Converter_20111121.pl \n";
exit(1);
}
my $rootPath = $ARGV[0];
my @lotList;
my @dataFile;
my $dataLine;
my $tempPath;
# Generate list of subfolders
opendir DIR, $rootPath or die "Couldn't open dir $rootPath: $!\n";
my @folders = readdir DIR;
closedir DIR;
foreach (@folders) {
if (-d $rootPath . "\\" . $_) {
if ($_ eq ".") {}
elsif($_ eq "..") {}
else {
push(@lotList, $_);
}
}
}
my %lotHash = ();
my $currentLot;
#Generate list of lot folders
foreach (@lotList) {
$tempPath = $rootPath . "\\" . $_;
$currentLot = $_;
opendir DIR, $tempPath or die "Couldn't open dir $tempPath: $!\n";
@folders = readdir DIR;
closedir DIR;
# Generate list of process folders
foreach (@folders) {
if (-d $tempPath . "\\" . $_) {
if ($_ eq ".") {}
elsif($_ eq "..") {}
else {
push(@{$lotHash{$currentLot}}, $_);
}
}
}
}
my $lot = "NULL";
my $process_step = "NULL";
my $data_file = "NULL";
my $panel_type = "";
my $panel_id = "";
my $operator_id = "";
my $scan_status = "";
my $queue = "";
my $go_repeats = "";
my $nogo_repeats = "";
my $num_repeats = "";
my $image_file_prefix = "";
my $slot_no = "";
my $glass_size_X = "";
my $glass_size_Y = "";
my $board_center_X = "";
my $board_center_Y = "";
my $panel_status = "NOT CLASSIFIED";
my $align_targets = "";
my $num_defects = "";
my $rep_no = "NULL";
my $X_coord = "NULL";
my $Y_coord = "NULL";
my $size = "NULL";
my $retype = "NULL";
my $type = "NULL";
my $cell_zone = "NULL";
my $size_X = "NULL";
my $size_Y = "NULL";
my $class = "NULL";
my $imagesuffix = "NULL";
my $row_cell_num = "NULL";
my $col_cell_num = "NULL";
my $i = 0;
my $output_file;
my @defect_list;
my $defect_string;
@folders = ();
for my $key (keys %lotHash) {
for ($i=0; $i <= $#{$lotHash{$key}}; $i++) {
$tempPath = $rootPath . "\\" . $key . "\\" . $lotHash{$key}[$i];
$lot = $key;
$process_step = $lotHash{$key}[$i];
opendir DIR, $tempPath or die "Couldn't open dir $tempPath: $!\n";
@folders = readdir DIR;
closedir DIR;
# Open each data file and parse for valid Orbotech Data
foreach (@folders) {
$tempPath = $rootPath . "\\" . $key . "\\" . $lotHash{$key}[$i] . "\\" . $_;
$data_file = $_;
open(DATA, "<$tempPath");
# Open report output file
$output_file = "TNP_" . $data_file;
$tempPath = $rootPath . "\\" . $key . "\\" . $lotHash{$key}[$i] . "\\" . $output_file;
open (REPORT, ">$tempPath") or die "Could not open $tempPath: $!\n";
@dataFile = ;
foreach $dataLine (@dataFile) {
# print "dataLine: $dataLine\n";
if ($dataLine =~ /panel_type\s*=\s*"([\w\s]*)"/) {
$panel_type = $1;
}
if ($dataLine =~ /panel_id\s*=\s*"([\w\s]*)"/) {
$panel_id = $1;
}
if ($dataLine =~ /operator_id\s*=\s*"([\w\s]*)"/) {
$operator_id = $1;
}
if ($dataLine =~ /queue\s*=\s*"([\w\s]*)"/) {
$queue = $1;
}
if ($dataLine =~ /scan_status\s*=\s*"([\w\s]*)"/) {
$scan_status = $1;
}
if ($dataLine =~ /go_repeats\s*=\s*"([\w\s]*)"/) {
$go_repeats = $1;
}
if ($dataLine =~ /nogo_repeats\s*=\s*"([\w\s]*)"/) {
$nogo_repeats = $1;
}
if ($dataLine =~ /image_file_prefix\s*=\s*"([\w\s\.]*)"/) {
$image_file_prefix = $1;
}
if ($dataLine =~ /slot_no\s*=\s*([\d\.\-]*)/) {
$slot_no = $1;
}
if ($dataLine =~ /num_repeats\s*=\s*([\d\.\-]*)/) {
$num_repeats = $1;
}
if ($dataLine =~ /glass_size.x\s*=\s*([\d\.\-]*)/) {
$glass_size_X = $1;
}
if ($dataLine =~ /glass_size.y\s*=\s*([\d\.\-]*)/) {
$glass_size_Y = $1;
}
if ($dataLine =~ /board_center.x\s*=\s*([\d\.\-]*)/) {
$board_center_X = $1;
}
if ($dataLine =~ /board_center.y\s*=\s*([\d\.\-]*)/) {
$board_center_Y = $1;
}
if ($dataLine =~ /panel_status\s*=\s*"([\w\s]*)"/) {
$panel_status = $1;
}
if ($dataLine =~ /align_targets\s*=\s*\(([\d\.\-]*)\s*([\d\.\-]*)\)\s*\(([\d\.\-]*)\s*([\d\.\-]*)\)\s*\(([\d\.\-]*)\s*([\d\.\-]*)\)\s*\(([\d\.\-]*)\s*([\d\.\-]*)\)/ ) {
$align_targets = "(" . $2 . " " . -$1 . ")(" . $4 . " " . -$3 . ")(" . $6 . " " . -$5 . ")(" . $8 . " " . -$7 . ")";
}
if ($dataLine =~ /num_defects\s*=\s*([\d\.\-]*)/) {
$num_defects = $1;
}
if ($dataLine =~ /([\d\.\-]*)\s*,\s*([\d\.\-]*)\s*,\s*([\d\.\-]*)\s*,\s*"*([\w\+\-\.]*)"*\s*,\s*"*([\w\+\-\[\]\.]*)"*\s*,\s*"*([\w\+\-\.]*)"*\s*,\s*"*([\w\+\-\.]*)"*\s*,\s*"*([\w\+\-\.]*)"*\s*,\s*"*([\w\+\-\.]*)"*\s*,\s*"*([\w\+\-\.]*)"*\s*,\s*"*([\w\+\-\.]*)"*\s*,\s*([\d\.]*)\s*,\s*([\d\.]*),?/) {
$rep_no = $1;
$X_coord = $3;
$Y_coord = -$2;
$size = $4;
$retype = $5;
$type = $6;
$cell_zone = $7;
$size_X = $8;
$size_Y = $9;
$class = $10;
$imagesuffix = $11;
$row_cell_num = $12;
$col_cell_num = $13;
$defect_string = $rep_no . ", " . $X_coord . ", " . $Y_coord . ", \"" . $size . "\", \"" . $retype . "\", \"" . $type . "\", \"" . $cell_zone . "\", \"" . $size_X . "\", \"" . $size_Y . "\", \"" . $class . "\", \"" . $imagesuffix . "\", " . $row_cell_num . ", " . $col_cell_num . ", \n";
push(@defect_list, $defect_string);
}
}
print REPORT "panel_type =\"$panel_type\"\n";
print REPORT "panel_id =\"$panel_id\"\n";
print REPORT "operator_id =\"$operator_id\"\n";
print REPORT "queue =\"$queue\"\n";
print REPORT "scan_status =\"$scan_status\"\n";
print REPORT "go_repeats =\"$go_repeats\"\n";
print REPORT "nogo_repeats =\"$nogo_repeats\"\n";
print REPORT "image_file_prefix =\"$image_file_prefix\"\n";
print REPORT "slot_no =$slot_no\n";
print REPORT "num_repeats =$num_repeats\n";
print REPORT "glass_size.x =$glass_size_X\n";
print REPORT "glass_size.y =$glass_size_Y\n";
print REPORT "board_center.x =$board_center_X\n";
print REPORT "board_center.y =$board_center_Y\n";
print REPORT "panel_status =\"$panel_status\"\n";
print REPORT "align_targets =$align_targets\n";
print REPORT "num_defects =$num_defects\n";
print REPORT "defect_list =\"#Rep no. X coord. Y coord. Size-X Size-Y Class ImageSuffix Row_Cell_Num Col_Cell_Num\"\n";
foreach (@defect_list) {
print REPORT $_;
}
$panel_type = "";
$panel_id = "";
$operator_id = "";
$queue = "";
$scan_status = "";
$go_repeats = "";
$nogo_repeats = "";
$image_file_prefix = "";
$slot_no = "";
$num_repeats = "";
$glass_size_X = "";
$glass_size_Y = "";
$board_center_X = "";
$board_center_Y = "";
$panel_status = "NOT CLASSIFIED";
$align_targets = "";
$num_defects = "";
@defect_list = ();
close(DATA);
close(REPORT);
}
@folders = ();
}
}
print "Data conversion complete!\n";
Saturday, February 19, 2011
Matching two data tables using sort and Perl
At work we were comparing two measurement techniques that involved two different instrument setups, one taking digital voltage threshold values and the other taking an analog resistance measurement to detect a gross shorting in a circuit. The software interfaces to these instruments were written in LabView a long long time ago, and did not output the data in a similar format. Both measurements were done on the same set of devices, and I wanted to match up the results between the two. My original perl script for this was just a brute force linear search between the two data files, looking for matching pairs and outputting the results in a new table file. Turns out that the ~23,000 data points was a bit too much for such lazy programming, and soon realized that this simple task was taking all day. After a friend showed me how quickly he could match the data sets using C# and LINQ I realized that this script could do something similar if I used perl's "sort" function. So here is the script for future reference.
use warnings;
use strict;
open(DIGITAL, "<spider_mask_defect_database.txt");
open(ANALOG, "<spider_mask_resistance_database.txt");
open(OUTPUT, ">Spider_Mask_Measurement_Verification.txt");
open(OUTPUTA, ">Analog_Data.txt");
open(OUTPUTD, ">Digital_Data.txt");
print "Spider_Mask_Measurement_Validation_20101129: Script started...\n";
my %LotNames = ("FDC25-001_Res" => "FDC25-001_Retest2",
"FDC25-002_Res" => "FDC25-002_Retest",
"FDC25-002_Shorts_Res" => "FDC25-002_Retest",
"FDC25-003_Cont_Res" => "FDC25-003",
"FDC25-003_Shorts_Res" => "FDC25-003",
"FDC25-004_Res" => "FDC25-004",
"FDC25-005_Res" => "FDC25-005",
"FDC25-006_Res" => "FDC25-006",
"Spider_Mask_Res" => "Spider_Mask_PEN_Lot",
"FDC25-007_Res" => "FDC25-007");
my %PitchNames = ("3um" => 3,
"6um" => 6,
"9um" => 9,
"12um"=> 12);
my $DigitalHeader = <DIGITAL>;
my $AnalogHeader = <ANALOG>;
my $i=0;
my $j=0;
my @OutputMatrix;
$OutputMatrix[0][0] = "Lot ID";
$OutputMatrix[0][1] = "Wafer ID";
$OutputMatrix[0][2] = "Device Pitch";
$OutputMatrix[0][3] = "Row";
$OutputMatrix[0][4] = "Column";
$OutputMatrix[0][5] = "Device Site";
$OutputMatrix[0][6] = "Analog Continuity";
$OutputMatrix[0][7] = "Analog Shorts";
$OutputMatrix[0][8] = "Digital Continuity";
$OutputMatrix[0][9] = "Digital Shorts\n";
#
# Note that:
# "Lot ID" = "Lot ID"
# "Wafer ID" = "Wafer ID"
# "Device ID" = "Pitch"
# "Row" = "Position X"
# "Column" = "Position Y"
# "Site ID" = "Site"
#
#
# In resistance database column labels are:
# | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
# |Lot ID|Wafer ID|Device ID|Row|Column|Site ID|Resistance|Measurement Type|
#
# In defect database column labels are:
# | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
# |Lot ID|Wafer|Site|Position X|Position Y|Pitch|Continuity|Shorts|
#
my @DIGITALDATA = ();
@DIGITALDATA = <DIGITAL>;
close(DIGITAL);
my @ANALOGDATA = ();
my @dataline = ();
# Use %LotNames to rename all analog lot labels to their digital counterparts.
while (<ANALOG>) {
@dataline = split(/\t/, $_);
$dataline[0] = $LotNames{$dataline[0]} or die "$dataline[0] not listed in LotNames: $!\n";
push(@ANALOGDATA, join("\t", @dataline));
}
if ($#ANALOGDATA <= 0) {
print "ANALOGDATA is empty\n";
exit(-1);
}
if ($#DIGITALDATA <= 0) {
print "DIGITALDATA is empty\n";
exit(-1);
}
close(ANALOG);
print "Done loading database into memory. Starting data sorting and merging...\n";
my @Sorted_Digital_Data = ();
my @Sorted_Analog_Data = ();
my @Temp_Analog_Data = ();
@Temp_Analog_Data = map { (split /\t/, $_)[2] =~ /([0-9]+)um/; [$1, $_] } @ANALOGDATA;
@Sorted_Digital_Data = sort {(split /\t/, $a)[0] cmp (split /\t/,$b)[0] || (split /\t/,$a)[1] cmp (split /\t/,$b)[1] || (split /\t/,$a)[2] cmp (split /\t/,$b)[2] || (split /\t/,$a)[5] <=> (split /\t/,$b)[5] || (split /\t/,$a)[3] <=> (split /\t/,$b)[3] || (split /\t/,$a)[4] <=> (split /\t/,$b)[4]} @DIGITALDATA;
@Temp_Analog_Data = sort {(split /\t/,$a->[1])[0] cmp (split /\t/,$b->[1])[0] || (split /\t/,$a->[1])[1] cmp (split /\t/,$b->[1])[1] || (split /\t/,$a->[1])[5] cmp (split /\t/,$b->[1])[5] || $a->[0] <=> $b->[0] || (split /\t/,$a->[1])[3] <=> (split /\t/,$b->[1])[3] || (split /\t/,$a->[1])[4] <=> (split /\t/,$b->[1])[4] || (split /\t/,$a->[1])[7] cmp (split /\t/,$b->[1])[7]} @Temp_Analog_Data;
@Sorted_Analog_Data = map { $_->[1] } @Temp_Analog_Data;
# Print out sorted data into files for analysis
print OUTPUTD $DigitalHeader;
for ($i=0; $i<=$#Sorted_Digital_Data; $i++) {
print OUTPUTD $Sorted_Digital_Data[$i];
}
print OUTPUTA $AnalogHeader;
for ($i=0; $i<=$#Sorted_Analog_Data; $i++) {
print OUTPUTA $Sorted_Analog_Data[$i];
}
#
# Combine sorted databases into OutputMatrix then dump to file.
# OutputMatrix column labels are:
# | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
# | Lot ID | Wafer ID | Device Pitch | Row | Column | Device Site | Analog Continuity | Analog Shorts | Digital Continuity | Digital Shorts |
#
my $k=1;
$i = 0;
for ($j = 0; $j <= $#Sorted_Analog_Data; $j=$j+2) {
@dataline = split(/\t/, $Sorted_Digital_Data[$i]);
if ( ($dataline[0] eq (split /\t/, $Sorted_Analog_Data[$j])[0]) && ($dataline[1] eq (split /\t/, $Sorted_Analog_Data[$j])[1]) ) {
$OutputMatrix[$k][0] = $dataline[0]; # Lot ID
$OutputMatrix[$k][1] = $dataline[1]; # Wafer ID
$OutputMatrix[$k][5] = $dataline[2]; # Site ID
$OutputMatrix[$k][3] = $dataline[3]; # Row
$OutputMatrix[$k][4] = $dataline[4]; # Column
$OutputMatrix[$k][2] = $dataline[5]; # Pitch
$OutputMatrix[$k][8] = $dataline[6]; # Digital continuity
$OutputMatrix[$k][9] = $dataline[7]; # Digital shorts
$OutputMatrix[$k][6] = (split /\t/, $Sorted_Analog_Data[$j])[6]; # Analog Continuity
$OutputMatrix[$k][7] = (split /\t/, $Sorted_Analog_Data[$j+1])[6]; # Analog Shorts
$i = $i + 1;
$k = $k + 1;
}
}
# Print sorted database to file.
for ($j=0; $j<=$#OutputMatrix; $j++) {
for ($k=0; $k<=9; $k++) {
if ($k<9) {
print OUTPUT $OutputMatrix[$j][$k], "\t";
}
else {
print OUTPUT $OutputMatrix[$j][$k];
}
}
}
close(OUTPUT);
close(OUTPUTA);
close(OUTPUTD);
print "Spider_Mask_Measurement_Validation_20101129: Script finished...\n";