HowTo: Create a Factory Rejects Data Recording system Part 4
Novell Cool Solutions: Feature
By Stomfi
Reader Rating 
|
Digg This -
Slashdot This
Posted: 16 Mar 2005 |
Learning to use Linux at Home and WorkWelcome to my ongoing series of HowTo articles designed to help Linux newbies get comfortable with Linux. Before trying any of these HowTos, take a few minutes to study the prerequisites so you can hit the ground running. --Stomfi |
This HowTo explains the creation of a turn key system for collecting and analysing factory reject data.
First a conventional data entry system using collected hand filled out forms will be developed and tested.
Second a factory floor point of origin data entry system using conventional keyboards and screens, will be developed, using the previous data collection files and analysis system.
Third a replacement wireless pocket sized hand held data entry and read out device will be designed to replace conventional keyboards and screens with multi unit base stations for each work area.
Runtime Revolution and the Linux shell tools are the tools used to develop and create this system without requiring any specialised programming skill. Reading HowTos and Linux manuals is as heavy as it gets. When using RunRev you'll find it works best in the Gnome desktop.
The use of Linux has let this system be developed. Any other solution platform would put the cost way out of the reach of the small businesses for whom it was designed.
In the first part we developed this stack and the stack for the New Rejects Entry Screen. We combined both stacks into a single application.
In the second part we developed the stack for the administration of products. We combined the three stacks developed into the single application.
In the third part we developed the Operators admin and Charting stacks and the shell scripts to do the processing, and added these to our RejCtl application. This finished the first aim of this HowTo.
In this Part 4 we will develop collecting the reject data from key strokes using standard keyboards, screens and computers.
This is a completely new stack that has nothing to do with the stacks we already developed, but uses the same file system so that both applications can be used for the data. This means that the charting and administration functions are still used, and the data entry function can be used if the live collection system loses power.
Create a new stack like this:

Here are the scripts.
First the open card script, which puts the operators into the field and empties the existing operator field and the selection field of the next card. You can see that the operators file is the same on we use in the rejctl application.
on openCard put ($HOME & "/frejects/lists/operators.txt") into OpSel open file OpSel for read read from file OpSel until EOF put it into OpList put OpList into field "MOPNAMES" close file OpSel put empty into field "THISOPER" put empty into field "AreaSelection" of card "Header" end openCard
This is the MOPNAMES field script:
on mouseDown put the selectedText of me into choice put choice into field "THISOPER" end mouseDown
This is the START button script:
on mouseUp
if field "THISOPER" <> empty
then
global ThisOper
put field "THISOPER" into ThisOper
go card "Header"
end if
end mouseUp
This is the Header card details:

We are using a new button constructor on this card.
This is a grouped radio button, where only one button of the group can be active.
You probably will have seen this on web pages and active forms.
To make this construction, first make all the buttons and align them.
Then create the individual scripts as show below. (If you want to change them after you have grouped the buttons, you can right click the button in the Application Browser).
Next select all the buttons in the group by dragging the mouse over them, or by clicking each one while holding down the Ctrl key.
From the menu bar use the Object drop down menu and click "Group Selected" thus:
Do this with both radio button groups.
This is the open card script:
on openCard set the hilite of button "CAST" to false set the hilite of button "LIN" to false set the hilite of button "MACH" to false set the hilite of button "PIM" to false put empty into field "OPERNAME" global PrevProd set the hilite of button "NEWPROD" to true put "N" into PrevProd set the hilite of button "PREVPROD" to false global ThisOper put ThisOper into field "OPERNAME" of card "Header" put empty into field "SProd" put empty into field "TOTACCEPT" of card "entry" end openCard
The open card script turns off all the top buttons and sets the NEWPROD button to on, by changing the hilite attribute of each one.
This is one of the top radio buttons. The others are the same except for the AreaSelection word. The other words are: LINISHING, MACHINING & PLASTCS. It uses the same prodlist.sh script we developed for the rejctl application to put the relevant product list into the product selection field.
on mouseUp put "CASTING" into field "AreaSelection" put ($HOME & "/frejects/bin/prodlist.sh" && quote & "CASTING" & quote) into PRODSEL replace return with empty in PRODSEL put the shell of PRODSEL into ProdList put ProdList into field "ProdList" end mouseUp
This is the product selection field script:
on mouseDown put the selectedText of me into choice put choice into field "SProd" end mouseDown
This is one of the Product Run radio buttons. The other puts "Y".
on mouseUp global PrevProd put "N" into PrevProd end mouseUp
This is the Data Entry button script.
on mouseUp
if field "SProd" <> empty
then
put field "SProd" of card "Header" into field "Wproduct" of card "Entry"
put field "AreaSelection" of card "Header" into thisArea
put thisArea into field "Warea" of card "Entry"
put ($HOME & "/frejects/lists/" & thisArea & ".txt") into rejectFile
replace enter with empty in rejectFile
open file rejectFile for read
read from file rejectFile until EOF
put it into rejectNames
close file rejectFile
put rejectNames into field "RLabels" of card "Entry"
put field "SProd" of card "Header" into NewProd
put ($HOME & "/frejects/info/thisproduct.txt") into ThisProduct
open file ThisProduct for write
write NewProd to file ThisProduct
close file ThisProduct
global ThisOper
put ThisOper into NewOper
put ($HOME & "/frejects/bin/proddir.sh" && quote & thisArea & quote && quote & NewOper & quote && quote & NewProd & quote ) into PRODDIR
# creates if required product and date directory structure returns $ROOT/thisArea/operator/productID/YYMMDD
replace return with empty in PRODDIR
open process PRODDIR for read
read from process PRODDIR until EOF
close process PRODDIR
global PFILE
put ($HOME & "/frejects/info/productdir.txt") into ThisDir
open file ThisDir for read
read from file ThisDir until EOF
put it into PFILE
close file ThisDir
put 0 into CNUM
repeat for 8 times
add 1 to CNUM
put ("R" & CNUM ) into RCNUM
replace return with empty in RCNUM
put (PFILE & "/" & RCNUM & ".txt") into RCFile
replace return with empty in RCFile
open file RCFile for read
read from file RCFile until EOF
put it into TempNum
close file RCFile
if TempNum < 0
then
put 0 into TempNum
end if
put TempNum into field RCNUM of card "Entry"
end repeat
put (PFILE & "/ACCEPT.txt") into ACFile
replace return with empty in ACFile
open file ACFile for read
read from file ACFile until EOF
put it into TempNum
close file ACFile
if TempNum < 1
then
put 0 into TempNum
end if
put TempNum into field "ACCEPT" of card "Entry"
global TAKEAWAY
put 0 into TAKEAWAY
#If the run is on the same day and hasn't been saved
#it will be treated as a previous run no matter what the flag says
global PrevProd
put ($HOME & "/frejects/bin/runtot.sh" && quote & PrevProd & quote) into PrevTot
replace return with empty in PrevTot
open process PrevTot for read
read from process PrevTot until EOF
close process PrevTot
global RunTot
put it into field "TOTACCEPT" of card "Entry"
put it into RunTot
go card "Entry"
end if
end mouseUp
This is the Operators button script:
on mouseUp put ($HOME & "/frejects/bin/savedata.sh") into SAVEDATA replace return with empty in SAVEDATA put the shell of SAVEDATA into DUNDATA go card "Start" end mouseUp
This is the Save Data button script. Notice that I put the "No" first so that the "Yes" will be highlighted when the pop up appears.
on mouseUp
answer "Do you really want to save your data" with "No" or "Yes"
put it into ANSW
if ANSW = "Yes"
then
put ( $HOME & "/frejects/bin/savedata.sh") into SAVEDATA
replace return with empty in SAVEDATA
put the shell of SAVEDATA into DUNDATA
end if
end mouseUp
I am going to put all the shell scripts last, if you are wondering where they are. This next card collects the data using raw key strokes. i.e. when a key is pressed the data is incremented or decremented immediately without having to press the Enter key.
This is the card script which collects the data. The onrawKeyDown handler looks for the key presses and the associated script acts on the particular key press. The switch/case construction looks at the number returned by the key. I have identified which ones I want to use in the thisKey field. You can make this field visible and see the number each key returns. Notice that each case statement has a break statement at the end. Otherwise it will keep looking at the other key presses and the subtract function wouldn't work.
After the "end switch", the script works out the count, and updates the relevant file and field. Finally it updates the total accepted product for the run.
on rawKeyDown
global PressCount
global PFILE
global TAKEAWAY
put empty into RCOUNT
put the keysDown into pressedKey
put pressedKey into field "thisKey"
switch pressedKey
case "65505"
put 1 into TAKEAWAY
break
case "65421"
put "ACCEPT" into RCOUNT
put (PFILE & "/ACCEPT.txt") into CFILE
replace return with empty in CFILE
break
case "49"
put "R1" into RCOUNT
put (PFILE & "/R1.txt") into CFILE
replace return with empty in CFILE
break
case "52"
put "R2" into RCOUNT
put (PFILE & "/R2.txt") into CFILE
replace return with empty in CFILE
break
case "55"
put "R3" into RCOUNT
put (PFILE & "/R3.txt") into CFILE
replace return with empty in CFILE
break
case "48"
put "R4" into RCOUNT
put (PFILE & "/R4.txt") into CFILE
replace return with empty in CFILE
break
case "97"
put "R5" into RCOUNT
put (PFILE & "/R5.txt") into CFILE
replace return with empty in CFILE
break
case "102"
put "R6" into RCOUNT
put (PFILE & "/R6.txt") into CFILE
replace return with empty in CFILE
break
case "106"
put "R7" into RCOUNT
put (PFILE & "/R7.txt") into CFILE
replace return with empty in CFILE
break
case "59"
put "R8" into RCOUNT
put (PFILE & "/R8.txt") into CFILE
replace return with empty in CFILE
break
end switch
if RCOUNT <> empty
then
open file CFILE for read
read from file CFILE until EOF
put it into TCOUNT
close file CFILE
if TAKEAWAY > 0
then
put 0 into TAKEAWAY
if TCOUNT > 0
then
subtract 1 from TCOUNT
else
put 0 into TCOUNT
end if
else
if TAKEAWAY < 1
then
add 1 to TCOUNT
end if
end if
put TCOUNT into field RCOUNT
open file CFILE for write
write TCOUNT to file CFILE
close file CFILE
global RunTot
if RCOUNT = "ACCEPT"
then
add 1 to RunTot
put RunTot into field "TOTACCEPT"
end if
end if
end rawKeyDown
You can use raw key to perform other tasks. I created a raw key sound generator using this handler for some fun at home.
This is the Return button script:
on mouseUp go card "Header" end mouseUp
The other interesting part of this program is in the shell scripts. This development is pretty simple, presuming that each operator has a separate computer, and only makes one product at any time. If more than one product is made, some of the files need to be saved in different places. e.g. in each active product directory.
This is the prodlist.sh script which is used in the forms based version as well.
#!/bin/bash
#
#prodlist.sh workarea
#
BINDIR="$HOME/frejects/bin"
case $1 in
CASTING) awk -F# -f $BINDIR/prodsel4.awk $HOME/frejects/lists/products.txt\
|tee $HOME/frejects/lists/prodcast.txt;;
MACHINING) awk -F# -f $BINDIR/prodsel5.awk $HOME/frejects/lists/products.txt\
|tee $HOME/frejects/lists/prodcnc.txt;;
LINISHING) awk -F# -f $BINDIR/prodsel6.awk $HOME/frejects/lists/products.txt\
|tee $HOME/frejects/lists/prodlin.txt;;
PLASTICS) awk -F# -f $BINDIR/prodsel7.awk $HOME/frejects/lists/products.txt\
|tee $HOME/frejects/lists/prodpim.txt;;
NONE) awk -F# -f $BINDIR/prodsel1.awk $HOME/frejects/lists/products.txt;;
esac
And one of the awk scripts:
{ if ($4 !~ "No" ) { print $1 }}
This is the proddir.sh script which creates all or part of a directory structure to collect the keyed data and a product header file for use in saving the results to the database. In this simple development, the database is on the local machine, but in practice there is a mirror of the database files on a networked central file server, which also does the administration in rejctl. We may look into that in a later howto if you like. There is also a handy extra that lets us put dynamic performance graphs directly onto the operator workstations, using local current and networked historical data.
#!/bin/bash
#proddir.sh workarea product operator
#use supplied product name which contains spaces
#and the current date and operator name
#to create a data entry directory structure
#and return the path
#
#set the frejects path for this collection point
FREGROOT="$HOME/frejects"
#
#set the work area
WAREA="$1"
#
#set today's date & epoch date
TDATE=`date +%y%m%d`
EDATE=`date +%s`
#
#Operator Name spaces are deleted
OPNAME=`echo "$2" | awk -F" " '{print $1$2$3$4}'`
#
#create a no space productname
TPROD=`echo "$3"|awk -F" " '{print $1$2$3$4$5$6}'`
#
#create data directory structure for work_area/product/operator/date
#if directories don't exist
if [ ! -e $FREGROOT/data/$WAREA ]
then
mkdir $FREGROOT/data/$WAREA
fi
if [ ! -e $FREGROOT/data/$WAREA/$TPROD ]
then
mkdir $FREGROOT/data/$WAREA/$TPROD
fi
if [ ! -e $FREGROOT/data/$WAREA/$TPROD/$OPNAME ]
then
mkdir $FREGROOT/data/$WAREA/$TPROD/$OPNAME
fi
if [ ! -e $FREGROOT/data/$WAREA/$TPROD/$OPNAME/$TDATE ]
then
mkdir $FREGROOT/data/$WAREA/$TPROD/$OPNAME/$TDATE
#Create a header file for collecting totals
echo "$1#$2#$TDATE#$3###$EDATE" > $DDIR/Header.txt
fi
DDIR="$FREGROOT/data/$WAREA/$TPROD/$OPNAME/$TDATE"
#touch the count data files to make sure they exist and are today's date
touch $DDIR/ACCEPT.txt
touch $DDIR/R1.txt
touch $DDIR/R2.txt
touch $DDIR/R3.txt
touch $DDIR/R4.txt
touch $DDIR/R5.txt
touch $DDIR/R6.txt
touch $DDIR/R7.txt
touch $DDIR/R8.txt
#return DDIR for the RR application
echo $DDIR > $FREGROOT/info/productdir.txt
This is the runtot.sh shell script which returns a total is this is flagged as a continuation of a previous run. This is so that the production team can see this figure on the factory floor and make immediate decisions depending on current work schedules and order times.
#!/bin/bash
#
#runtot.sh [Run_boolean]
#calculate and return total accepted product made in run period
#There is a flag question on the product selection page
#ie is this part of a previous run [no] or yes
PDIR=`cat $HOME/frejects/info/productdir.txt | cut -d"/" -f1-7`
#
PFILE="$PDIR/previous.tot"
#check to see if file doesn't exist
if [ ! -e "$PFILE" ]
then
#Must be a new run so create file
#put 0 into PFILE
echo "0" > "$PFILE"
else
#there is a flag
if [ "$1" = "N" ]
then
#This is a flagged new run
#add contents to old file
CURTOT=`cat "$PFILE"`
OLDTOT=`cat "$PDIR/previous.old"`
if [ ${#CURTOT} -gt 0 ]
then
if [ ${#OLDTOT} -gt 0 ]
then
let MOVTOT=($CURTOT + $OLDTOT)
echo "$MOVTOT" > "$PDIR/previous.old"
else
cat "$PFILE" > "$PDIR/previous.old"
fi
fi
#put 0 into PFILE
echo "0" > "$PFILE"
fi
fi
#get the last total
PTOT=`cat "$PFILE"`
#add the current accepted number
#get the current data directory
DDIR=`cat $HOME/frejects/info/productdir.txt`
CACCEPT=`cat $DDIR/ACCEPT.txt`
if [ ${#CACCEPT} -gt 0 ]
then
let NEWTOT=($PTOT + $CACCEPT)
else
NEWTOT=$PTOT
fi
echo $NEWTOT
The final shell script for this development is savedata.sh
This script has a new construction which we haven't used before "until { condition ] do done" and also uses the "join" tool which joins two indexed files to create a new one with the index and fields from both files. Join is one of the shell database manipulation tools.
#!/bin/bash
#
#savedata.sh
#
#Set the data directory
DDIR=`cat $HOME/frejects/info/productdir.txt`
#Set the run directory
RUNDIR=`echo $DDIR | cut -d"/" -f1-7`
#Saved accepted product into previous.tot
OLDTOT=`cat $RUNDIR/previous.tot`
ATOT=`cat $DDIR/ACCEPT.txt`
NEWTOT=0
if [ ${#ATOT} -gt 0 ]
then
if [ $ATOT -gt 0 ]
then
if [ ${#OLDTOT} -gt 0 ]
then
if [ $OLDTOT -gt 0 ]
then
NEWTOT=($OLDTOT + $ATOT)
else
NEWTOT=$ATOT
fi
else
NEWTOT=$ATOT
fi
fi
fi
if [ $NEWTOT -gt 0 ]
then
echo $NEWTOT > $RUNDIR/previous.tot
fi
#
#Collect all the R* data, matching it with the reject names for the work area
WORKA=`echo $RUNDIR | cut -d"/" -f6`
#Set the workarea file for the names
WAFILE="$HOME/frejects/lists/$WORKA.txt"
#Setup indexed list of names
awk '{print NR "#" $0}' $WAFILE > $HOME/frejects/tmp/waindex.txt
#Setup indexed list of quantities
#Make a file list
rm -f $HOME/frejects/tmp/rqindex.txt
RFILES=`ls $DDIR/R*`
#put the data into indexed file
IDX=0
for I in $RFILES
do
let IDX=($IDX + 1)
RQUANT=`cat $I`
echo "$IDX#$RQUANT" >> $HOME/frejects/tmp/rqindex.txt
done
#Join the files
join -t"#" $HOME/frejects/tmp/waindex.txt $HOME/frejects/tmp/rqindex.txt > $HOME/frejects/tmp/qjoin.txt
#get rid of the index column and put the data into name#quantity format if there is a quantity
awk -F"#" '{if($3 > 0)print $2 "#" $3}' $HOME/frejects/tmp/qjoin.txt > $HOME/frejects/tmp/ajoin.txt
#Using the header file create the data file
#
NRLINES=`wc $HOME/frejects/tmp/ajoin.txt | awk '{print $1}'`
rm -f $HOME/frejects/tmp/dtmp.txt
NCOUNT=1
until [ $NCOUNT -gt $NRLINES ]
do
RLINE=`awk -v NNUM=$NCOUNT '{if(NR == NNUM) print $0}' $HOME/frejects/tmp/ajoin.txt `
RNAME=`echo $RLINE | cut -d"#" -f1`
RNUM=`echo $RLINE | cut -d"#" -f2`
awk -F"#" -v REJ="$RNAME" -v REJQ=$RNUM 'BEGIN{OFS = "#"}; \
{$5 = REJ; $6 = REJQ; print $1,$2,$3,$4,$5,$6,$7}' $DDIR/Header.txt >> $HOME/frejects/tmp/dtmp.txt
let NCOUNT=($NCOUNT + 1)
done
#
#Add it to the database
if [ -s $HOME/frejects/tmp/dtmp.txt ]
then
cat $HOME/frejects/tmp/dtmp.txt >> $HOME/frejects/database/newrejects.txt
fi
#Clear out the saved data by deleting the date directory files
rm -f $DDIR/R*
rm -f $DDIR/A*
#This lets data be saved and a second run take place on the same day
You may notice I haven't implemented a button to save the newrejects.txt file. This should be included on the operators screen in the "Quit" button. You can get the code from the previous articles in this series and DIY.
Anyone who has got this far in my series of howtos should be climbing up the ladder of opportunity in the new generation world of Linux by now. Go for it Ocker. (That's the Aussi word for Mate. Yeah, I know it is also used in Devon UK. Where do you think we came from anyway.)
That's all folks for this part of the factory rejects system development. The keyboard raw key layout was selected so that a plastic cover could be put over the keyboard and named plastic key pressers placed underneath, covering 3 keys but acting on one, so that an operator wearing leather gloves would have no trouble finding and pressing the right key. Cheap second hand P1 200MHz computers, with 2.5GB drives, 15" screens, no mouse (The keyboard mouse is activated), and an inexpensive WiFi Ethernet connection, are used in the prototype.
The final part of this howto describes a design for replacing the bulky workstations with hand held data entry and display devices, low power radio connection and base stations. It is left to the reader to implement the design.
For more information about Runtime Revolution visit http://www.novell.com/coolsolutions/feature/1863.html
Novell Cool Solutions (corporate web communities) are produced by WebWise Solutions. www.webwiseone.com
Learning to use Linux at Home and Work