Novell Home

HowTo: Create a Factory Rejects Data Recording system Part 4

Novell Cool Solutions: Feature
By Stomfi

Digg This - Slashdot This

Posted: 16 Mar 2005
 

StomfiLearning to use Linux at Home and Work
Welcome 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

© 2014 Novell