%%%
  VERSION:1
  LANGUAGE:ENGLISH
%%%

MODULE MAIN0001

! Changelog:
!----------------------------------------------------------------------------
!
! W734: Added output for current program instead of last toggled program.
!
! W737: Added proc to preload all programs
!
! W739: Moved queue handling module to background task
!
! W741: Serial load/parallel load coexist.
!
! W747: Added handshaking vs. task psjobq: bNextJobOK introduced.
!
! W747: According to IRB programmers, it is consensus that the program usually executing as the main program
!       is named "main" + robot number (within a group of robots). Thus, default name of this file is changed 
!       to: "main0001.prg"
!
! W751: Added updating of common PERS variables when main program restarts.   
!
! W752: Brush tables and PROC SelectBrushTab loacted in dedicated module.
!
! W809: Added Queue-Tracking functionality. (psjobqtr.prg, mainqt01.prg)
!
! W818: Added Interface to ipscfg (PERS BOOL bCfgDone) to tell when config finished.
!
! W828: Added Background Load functionality and conformed to new Naming Convention.
!
! W837: 2nd job in the job queue made available.
!
! W846: CONST BOOL parameters changed to conform to naming convention.
!
! W912: Improvements for v3.2.01 plus more changes to conform to naming convention.
!
! W947: ShowJob changed so goCurModNo, goCurBrushTab are updated unconditionally.
!
!----------------------------------------------------------------------------

!
! "Parameters":
!
  CONST BOOL bDebugMode      := FALSE;! Writes debug info to TPU if TRUE

  CONST BOOL bTestRack       := FALSE;! Used when running on the test rack
    
  CONST bool bBrushSelActive := TRUE; ! Brush selection installed if TRUE

  CONST BOOL bQueueTrackMode := TRUE; ! Queue Tracking installed if TRUE

  CONST BOOL bForceJob       := FALSE; ! Override checkpoint functionality if TRUE

  CONST BOOL bNormlTrackMode := FALSE;! Ordinary Tracking installed if TRUE. 
                                      ! (Paint program will be executed at once, 
                                      ! not waiting for a startsignal, 
                                      ! assuming there is a WaitWObject instruction coming up.)
   
  CONST BOOL bLoadAllMode    := FALSE;! If TRUE, all programs will be loaded into work memory when program is tarted.
                                      ! If FALSE, programs will be loaded into work memory when needed, and then unloaded.
                                      ! TRUE should be used when programs are few and small enough so there is room for all
                                      ! programs in work memory at a time. Cycle time performance will be best then.
                                      ! If programs are too many and too big to fit in memory all at once, FALSE must be used. 
                                      ! Penalty: Load time will be added to cycle time.

  CONST BOOL bRepeatLastMode := FALSE;! Repeat last executed program if empty queue.
                                      ! NOTE ! bQueueTrackMode and bRepeatLastMode are 
                                      ! mutually exclusive (Only one may be TRUE)

  CONST BOOL bBgLoadMode    := FALSE; ! If TRUE, programs will be loaded into work memory while current paint program is running.

  !=============================================================================================
  !
  ! INTERFACE TO BACKGROUND TASK psjobq !
  ! 
  ! PERS vars below are defined identically in task psjobq.prg !
  ! PERS vars below serve as interface between main prog and task !

  PERS NUM nNoOfJobs    := 0;   ! Number of jobs in the job queue
  PERS NUM nNextModNo   := 0;   ! Next program   in the job queue 
  PERS NUM nNextBrushTab:= 0;   ! Next Brushtab  in the job queue 
  PERS NUM nNextObjPos  := 0;   ! Next position  in the job queue (low)
  PERS NUM nNextObjPos2 := 0;   ! Next position  in the job queue (high)

  PERS NUM nCmdPopQue   := 0;   ! This PERS is a command to pop the queue.
                                ! Writing to this PERS means to send a command to the queue
                                ! handler telling it to pop the oldest job from the queue.

  PERS BOOL bInMod      := FALSE; ! TRUE if executing an external module (Paint program) !
  PERS BOOL bNextJobOK  := FALSE; ! Handshake to/from task psjobq !


  ! Run-without-queue-shift: Permits a program selection to "intrude" before the job queue
  PERS num  nSelIntruder := 0;      ! Selected intruder prog no.
  PERS bool bNewIntruder := FALSE;  ! Message from Program Selection: New intruder program selected.

  PERS bool bAutoStartOn := TRUE;    ! Autostart ON if TRUE.
  
  PERS num  nSelectedBrush:=0;
  
  ! Data for the second job in the queue:
  ! nJob2: Job2 number
  ! bJob2: Job2 present  
  PERS NUM  nJob2         := 0;
  PERS BOOL bJob2         := TRUE;
  
  PERS STRING strCurBrushTab := "";   ! Current brush table.
  
  ! END psjobq-interface !
  
  ! INTERFACE TO BACKGROUND TASK ipscfg !
  
  PERS BOOL bCfgDone := TRUE;! Used to tell other processes when the configuration download is finished.
  ! Note ! Intitialized to TRUE here to avoid deadlock if ipscfg not present.

  
  !!!! END  INTERFACE !!!!!


  !=============================================================================================



  PERS BOOL bContinue    := TRUE;
  PERS NUM  nLastObject  := 0;     ! Last object processed by the robot
  PERS NUM  nLastBrush   := 0;     ! Last brush used by the robot

  PERS NUM  nLastProgNo  := 0;     ! Progno     from last update in Showjob
  PERS NUM  nLastBrushNo := 0;     ! Brushtab   from last update in Showjob

  PERS STRING strCurModNo:= "";    ! Current loaded module file.
  PERS BOOL bPreloaded   := FALSE; ! TRUE if next program to run is loaded and ready.

  PERS BOOL bNewSync     := FALSE; ! A new sync signal arrives when an object operates the startsignal switch.
  PERS BOOL bNewLoad     := FALSE; ! Trap routine for New Load ("Cancel") sets this to TRUE.


  PERS STRING RunProc    := "UnInit";! Name of external procedure to call 
  
  VAR  BOOL bExeInitDone   := FALSE; ! Init code (Traps etc.) OK if TRUE
  VAR  BOOL bCreateNewProg := FALSE; ! Operator has decided to create a new program number !
  VAR  BOOL bgLoading      := FALSE; ! Background loading in progress if TRUE !
  VAR  loadsession lsBgLoadId;       ! Background load id !


  VAR intnum irNewSync;
  VAR intnum irNewLoad;
  VAR intnum irAutoStartOn;
  VAR intnum irAutoStartOff;
  
  !TEST VAR intnum irPassedOut; 



  !===============MAIN ROUTINE========================
  
  PROC main()
      
    bContinue    := TRUE; ! If restarting program from the top, loop condition must be reset.
    bPreloaded   := FALSE;! When starting from top, we want to redo loading!      
    nLastProgNo  := 0;    ! If starting from top, this must be reset, 
                          ! or else a new load will not take place
    WaitTime 2;           ! Workaround for SPR 1004570
    ExeInit;
      
    TPErase;              ! Start with a blank screen 
      

    IF bQueueTrackMode AND bRepeatLastMode THEN  ! bQueueTrackMode and bRepeatLastMode are 
                                                 ! mutually exclusive (Only one may be TRUE)
      TPReadFK reg1, "bQueueTrackMode and bRepeatLastMode are both TRUE", "", "", "", "", "Cont";

    ENDIF

    IF bNormlTrackMode OR bQueueTrackMode THEN
       ActUnit CNV1;
    ENDIF


    SetDO do_9PS, 1;  ! Initiate an interrupt to update queue vars ! 

    WHILE bContinue DO

      WHILE NOT bCfgDone DO
             
        ! If system is reset, this program may restart in main loop !
        WaitTime 1; ! Wait for cfg download to complete !

      ENDWHILE

      IF bLoadAllMode THEN
        ExePar;                 ! Check if ready to execute. (Parallel mode)
        CheckTime;              ! Check if any of the modules should be reloaded !
        LoadNextPar;            ! External input: Load Next Job! ("Cancel")
      ELSE
        ExeSer;                 ! Check if ready to execute. (Serial mode)
        LoadNextSer;            ! External input: Load Next Job! ("Cancel")
      ENDIF

      IF bCreateNewProg CreateNewProc;  ! Operator has decided to create a new program number !
      
      
  !   IF bTestRack THEN ! Operating AUTO_ON from I/O box..
  !     IF AUTO_ON THEN
  !       bAutoStartOn := TRUE;
  !     ELSE
  !       bAutoStartOn := FALSE;
  !     ENDIF
  !   ENDIF
         
      IF bAutoStartOn=TRUE THEN
         SetDO doAutoLamp,1;
      ELSE
         SetDO doAutoLamp,0;
         WaitTime 0.1;
      ENDIF
        
      WaitTime 0.01;
         
    ENDWHILE
      
  ENDPROC


  !===================================================================================
  
  !TRAP tpPassedOut
  !  TPWrite "c1PassedStWin activated!!!";
  !  TPWrite "==========================";
  !  RETURN;
  !ENDTRAP

  TRAP tpObjIdNewSync
    bNewSync:=TRUE;
    RETURN;
  ENDTRAP

  TRAP tpObjIdNewLoad
    !VAR bool Dummy;

    IF bInMod THEN
      ! Possible to pop jobs from the queue (in background) even when job is running !
      !Dummy := jqPopJob();
    ELSE
      bNewLoad:=TRUE;
    ENDIF
    RETURN;
  ENDTRAP

  TRAP tpAutoStartOn
    bAutoStartOn:=TRUE;
    IF bDebugMode TPWrite "Main/tpAutoStartOn";
    !Set doAutoLamp;
  ENDTRAP

  TRAP tpAutoStartOff
    bAutoStartOn:=FALSE;
    IF bDebugMode TPWrite "Main/tpAutoStartOff";
    !Reset doAutoLamp;
  ENDTRAP


  PROC ExeInit()

    ! Place all init code for module Exe here!
    
    IF NOT bExeInitDone THEN

      CONNECT irNewSync      WITH tpObjIdNewSync;
      CONNECT irNewLoad      WITH tpObjIdNewLoad;
      CONNECT irAutoStartOn  WITH tpAutoStartOn;
      CONNECT irAutoStartOff WITH tpAutoStartOff;

     !CONNECT irPassedOut WITH tpPassedOut;

      ISignalDI diSync,      high, irNewSync;
      ISignalDI diLoadNext,  high, irNewLoad;

      ! For TESTRACK (office) !
      !ISignalDI diAutoStartOn,1,irAutoStartOn;
      !ISignalDI diAutoStartOn,0,irAutoStartOff;
      
      ! For control panel (lab) !
      ISignalDI diAutoStartOn, 1, irAutoStartOn;
      ISignalDI diAutoStartOff,1, irAutoStartOff;
      
      !TEST! ISignalDI c1PassStw, 1, irPassedOut;
      
      IF bLoadAllMode LoadAllProgs;  ! Load all programs required !

      Load "ram1disk:brush100.mod";  ! Load all brushes for this application !
 
      SetDO c1PosInJobQ, 1;          ! Make ready for queue-tracking !

      bInMod:=FALSE;
      bExeInitDone:=TRUE;
    ENDIF
    bNewSync:=FALSE;
    bNewLoad:=FALSE;

   ERROR

     IF ERRNO = ERR_LOADED
       THEN TRYNEXT;
     ELSE
       TPWrite "Error handler ExeInit!";
     ENDIF  
     
  ENDPROC



  !==================== Job queue interface routines ==================================


  FUNC bool jqPopJob()
    ! Removes the oldest job-entry from the queue.
    IF nNoOfJobs=0 THEN
      RETURN FALSE;
    ELSE
      WHILE nCmdPopQue>0 DO
        IF bDebugMode TPWrite "Main:Waiting for nCmdPopQue ";
        WaitTime 0.1; ! Wait until ready for new command
      ENDWHILE
      IF bDebugMode TPWrite "Main/jqPopJob:Pop queue, nNoOfJobs = "\Num:=nNoOfJobs;
      nCmdPopQue :=1; ! This gives a command to the background task to pop the queue.
      SetDO do_9PS,1; ! Initiate an interrupt on this signal ! 
      bNextJobOK:=FALSE; ! Next job not ready for reading now!
    ENDIF
    RETURN TRUE;
  ENDFUNC


  FUNC bool jqReadNextJob(
    INOUT num nProgNo,
    INOUT num nBrushNo,
    INOUT num nObjPos1,
    INOUT num nObjPos2,
    INOUT num nNoJobs)

    ! Retrieves the oldest job-entry from the queue.
    
    VAR NUM nTimeCount := 10; ! Timeout = 2 sec.
    
    ! Wait for next job to be ready !
    WHILE (NOT bNextJobOK) AND (nTimeCount > 0) DO
       WaitTime 0.2;
       nTimeCount := nTimeCount - 1;
    ENDWHILE
    
    IF bNextJobOK THEN
       nProgNo  := nNextModNo;   !jobqueue{outx,prg};
       nBrushNo := nNextBrushTab;!jobqueue{outx,bru};
       nObjPos1 := nNextObjPos;  !jobqueue{outx,pos};
       nObjPos2 := nNextObjPos2; !jobqueue{outx,pos};
       nNoJobs:= nNoOfJobs;
       IF bDebugMode OR bTestRack TPWrite "Main:jqReadNextJob : NextProg = "\Num:=nProgNo;
       RETURN TRUE;
    ELSE
       RETURN FALSE;
    ENDIF
  ENDFUNC

  !==================== End Job queue interface routines ==================================
  !MODULE TESTpnt
  !
  !   PROC main()
  !      Load "mod1.mod";
  !      Load "mod2.mod";
  !      Load "mod3.mod";
  !      Load "mod4.mod";
  !
  !      WHILE TRUE DO
  !         WaitTime 1;
  !         TPWrite "Next loop";
  !   IF FileTime("mod1.mod") > ModTime("mod1") THEN
  !           TPWrite "New module 1";
  !     UnLoad "mod1.mod";
  !           Load "mod1.mod";
  !   ENDIF
  !   %"mod1main"%;
  !   IF FileTime("mod2.mod") > ModTime("mod2") THEN
  !           TPWrite "New module 2";
  !     UnLoad "mod2.mod";
  !           Load "mod2.mod";
  !   ENDIF
  !   %"mod2main"%;
  !   IF FileTime("mod3.mod") > ModTime("mod3") THEN
  !           TPWrite "New module 3";
  !     UnLoad "mod3.mod";
  !           Load "mod3.mod";
  !   ENDIF
  !   %"mod3main"%;
  !   IF FileTime("mod4.mod") > ModTime("mod4") THEN
  !           TPWrite "New module 4";
  !     UnLoad "mod4.mod";
  !           Load "mod4.mod";
  !   ENDIF
  !   %"mod4main"%;
  !      ENDWHILE
  !  ENDPROC
  !ENDMODULE
ENDMODULE


MODULE TimeFile
  PROC CheckTime()
    VAR string LdStr:="";
    VAR string MdStr:="";

    IF (bAutoStartOn=TRUE) RETURN;    ! Don't do this if full production !
    !Has been tested OK Oct. 10th -97 by TH !
    !Will be re-inserted when base system can support FileTime, ModTime
    !   LdStr := "ram1disk:m0.mod";
    !   MdStr := "M0";
    !   IF FileTime(LdStr) > ModTime(MdStr) THEN
    !      TPWrite "New module " + MdStr;
    !      UnLoad LdStr;
    !      Load LdStr;
    !   ENDIF
  ENDPROC
ENDMODULE


MODULE ExeP
  PROC LoadAllProgs()
    ! If bLoadAllMode is TRUE, all programs will be loaded into work memory when program is started.
    ! If FALSE, programs will be loaded into work memory when needed, and then unloaded.
    VAR string LdStr:="";

    LdStr:="ram1disk:m0.mod";
    Load LdStr;
    LdStr:="ram1disk:m1.mod";
    Load LdStr;
    LdStr:="ram1disk:m2.mod";
    Load LdStr;
    LdStr:="ram1disk:m3.mod";
    Load LdStr;
    LdStr:="ram1disk:m4.mod";
    Load LdStr;
  ERROR
    TPWrite "Preload Program not found: "+LdStr;
    TRYNEXT;
    ! If one program is not found, just go on to the next one !
  ENDPROC

  PROC ShowJob()
    SetGO goCurModNo,nNextModNo;  ! Display current style
    SetGO goCurBrushTab,nNextBrushTab; ! Display current brush
    nLastProgNo :=nNextModNo;
    nLastBrushNo:=nNextBrushTab;
  ENDPROC

  PROC ClearJob()
    SetGO goCurBrushTab,0;
    SetGO goCurModNo,0;
  ENDPROC

  LOCAL PROC RunJobP()

    VAR num nCurObject :=0;
    VAR num nCurBrush  :=0;
    VAR num nCurObjPos1:=0;
    VAR num nCurObjPos2:=0;
    VAR num nJobs      :=0;
    
    VAR string SelStr:="SelectBrushTab";
    
    ! Retreive a job from the job queue !
    IF jqReadNextJob(nCurObject,nCurBrush,nCurObjPos1,nCurObjPos2, nJobs) AND jqPopJob() THEN
      IF nJobs > 0 THEN
         IF bDebugMode TPWrite "RunJobP: jqReadNextJob returned:"\Num:=nCurObject;
      ENDIF
    ELSE
      IF bRepeatLastMode AND (NOT bQueueTrackMode) THEN
        nCurObject:=nLastObject;
        nCurBrush :=nLastBrush;
      ELSE
        IF bTestRack TPWrite "RunJobP: Queue is empty ";
        RETURN;
      ENDIF
    ENDIF
    RunProc:="r"+ValToStr(nCurObject); ! Add a prefix.
    nSelectedBrush:=nCurBrush;         ! nSelectedBrush is used by SelectBrushTab
    
    IF bBrushSelActive THEN
       %SelStr%;   ! Select brush table !
    ENDIF
    
    bInMod:=TRUE;
    SetDO doInMod,1;     ! Switch "Run lamp" on !
    %RunProc%;           ! Call procedure in loaded module !
    SetDO doInMod,0;     ! Switch "Run lamp" off!
    bInMod:=FALSE;
    nLastObject:=nCurObject;
    nLastBrush :=nCurBrush;
    !ShowJob 0, 0; ! Clear out display until next job is loaded.

  ERROR
    ! Error handler
    TPWrite "RunJobP:External procedure "+RunProc+" not found";
    bInMod:=FALSE;
    SetDO doInMod,0;
    RETURN;
  ENDPROC


  PROC LoadNextPar()
    ! Load next / parallel mode
    ! Preload next job and remove the job from the queue.
    ! This is the equivalent of the 'Cancel' button on the TR-5000 
    VAR bool Dummy;

    IF bNewLoad THEN
      !PreLoadJob;   
      Dummy:=jqPopJob();
      bNewLoad:=FALSE;
    ENDIF
  ENDPROC


  PROC ExePar()
    ! Execute in parallel mode !
    ! If a new sync-signal has arrived, and a job is present in the queue: Run job!
    IF bNormlTrackMode OR bQueueTrackMode THEN
      !  WaitWobj Wobj1;      ! In bQueueTrackMode, we must take care if a job was dropped. 
      !  If instruction above fails, error handler takes over.
    ENDIF
    ! If NOT Current job = first job in queue (Jobs may have been cancelled in background)
    !    Load next job !?
    IF bNewSync OR bNormlTrackMode OR bQueueTrackMode THEN
      !? When Tracking, we don't wait for startsignal. 
      bNewSync:=FALSE;
      IF bAutoStartOn=TRUE RunJobP; ! Run if job available. 
    ENDIF
    ShowJob;
  ERROR
    ! Error handler: If WaitWobj failed, execution will continue here
    ! TPWrite "Job was dropped"; !
    IF jqPopJob() THEN
      ! Process next job !
    ENDIF
    ClearJob;
    RETURN;
  ENDPROC

  !++++++++++++++++++++++ END MODULE EXEP  +++++++++++++++++++++++++++++++++++++++

ENDMODULE 




MODULE ExeS
  
  ! Execute in serial mode (Load one module at a time) 
  
  PROC LoadJob(
    num CurObject,
    num CurBrush,
    num CurObjPos1,
    num CurObjPos2)

    VAR string LoadPgStr:="";
    VAR string DriveStr :="ram1disk:/";
    VAR string DefaultPg:="ram1disk:/m0.mod";
    VAR string SelStr   :="SelectBrushTab";
    VAR num Myreg1:=0;

    nSelectedBrush:=CurBrush; ! nSelectedBrush is used by SelectBrushTab
    %SelStr%;! Select brush table ! nSelectedBrush, nLastBrush used as "parameters".

    LoadPgStr:=DriveStr+"m"+ValToStr(CurObject)+".mod";  ! Example: LOAD "ram1disk:m1.mod";
    IF strCurModNo<>"" UnLoad strCurModNo; ! No unload first time !
    !SetDO do_6PS , 1;  ! Load signal: Loading !
    IF NOT bgLoading THEN
      IF bDebugMode TPWrite "LoadJob: Loading" + LoadPgStr;
      Load LoadPgStr;     ! If program not found, default program should run. (See error handler)
    ENDIF
    !SetDO do_6PS , 0;  ! Done Loading !
    strCurModNo:=LoadPgStr;
    RunProc:="r"+ValToStr(CurObject);
    IF bDebugMode TPWrite "LoadJob: RunProc:=" + RunProc;
    ShowJob; ! If a new module has been loaded, display the new id using LEDs.
    bPreloaded:=TRUE;
  ERROR
    ! Error handler
    IF ERRNO=ERR_FILNOTFND THEN
      ! Load default program instead ! (Note: Old prog is already unloaded)
      TPWrite "Unknown Object detected!";
      ! Temporarily disabled...  TPReadFK Myreg1,"Create New Program?","","","","Yes","No";
      IF Myreg1=4 THEN
        bCreateNewProg:=TRUE;
      ELSE
        Load DefaultPg;
        !SetDO do_6PS , 0;  ! Done Loading !
        strCurModNo:=DefaultPg;
        RunProc:="r"+ValToStr(0);
        bPreloaded:=TRUE;
      ENDIF
      RETURN;
    ELSEIF ERRNO=ERR_LOADED THEN
      ! OK ! Program is already loaded!
      !SetDO do_6PS , 0;  ! Done Loading !
      RETURN;
    ELSEIF ERRNO=ERR_UNLOAD THEN
      ! No program to unload; Restart, manual intervention etc. (OK!)
      TRYNEXT;
    ELSE
      !SetDO do_6PS , 0;  ! Done Loading !
      ! Other error: Stop !
      ! NB! What about syntax error in loaded program -> pop queue!
    ENDIF
  ENDPROC




  PROC StLoadJob(num CurObject)

    VAR string StLoadPgStr :="";
    VAR string DriveStr    :="ram1disk:/";
    VAR string DefaultPg   :="ram1disk:/m0.mod";

    StLoadPgStr:=DriveStr+"m"+ValToStr(CurObject)+".mod";  ! Example: LOAD "ram1disk:m1.mod";
    IF strCurModNo<>StLoadPgStr THEN
      StartLoad StLoadPgStr, lsBgLoadId; 
      bgLoading:=TRUE;  ! Background loading in progress !
    ENDIF
  ERROR
    ! Error handler
    IF ERRNO=ERR_FILNOTFND THEN
      ! Load default program instead ! (Note: Old prog is already unloaded)
      !strCurModNo:=DefaultPg;
      RETURN;
    ELSEIF ERRNO=ERR_LOADED THEN
      ! OK ! Program is already loaded!
      !SetDO do_6PS , 0;  ! Done Loading !
      RETURN;
    ELSE
      !SetDO do_6PS , 0;  ! Done Loading !
      ! Other error; stop!
    ENDIF
  ENDPROC



  LOCAL PROC StartBLoad(var num nMyJob2)    ! Start Background load.

    VAR num nCurObject  :=0;
    VAR num nCurObjPos1 :=0;
    VAR num nCurObjPos2 :=0;
    VAR num nCurBrush   :=0;
    VAR num nNoJobs     :=0;
    
    ! Wait for next job to be ready !
    
    IF bBgLoadMode THEN
    
       IF bJob2 THEN
          StLoadJob nMyJob2;
       ELSE
          IF bRepeatLastMode AND (nLastObject > 0) THEN
            StLoadJob nLastObject;
          ENDIF
       ENDIF
    ENDIF 
  ENDPROC

  LOCAL PROC oldStartBLoad()    ! Start Background load.

    VAR NUM nTimeCount := 5; ! Timeout = 0.5 sec.
    VAR num nCurObject  :=0;
    VAR num nCurObjPos1 :=0;
    VAR num nCurObjPos2 :=0;
    VAR num nCurBrush   :=0;
    VAR num nNoJobs     :=0;
    
    ! Wait for next job to be ready !
    
    IF bBgLoadMode THEN
    
       WHILE (NOT bNextJobOK) AND (nTimeCount > 0) DO
          WaitTime 0.1;
          nTimeCount := nTimeCount - 1;
       ENDWHILE
       IF bNextJobOK THEN
          IF jqReadNextJob(nCurObject,nCurBrush,nCurObjPos1,nCurObjPos2, nNoJobs) THEN
             IF nNoJobs > 0 THEN
                StLoadJob nCurObject;
             ELSE
                ! Queue was empty !
                IF bRepeatLastMode AND (nLastObject > 0) THEN
                  StLoadJob nLastObject;
                ENDIF
             ENDIF
          ENDIF
       ENDIF
    ENDIF 
  ENDPROC



  LOCAL PROC RunJobS()
    ! Pop Queue and call procedure in loaded module !
    ! 
    VAR NUM job2 := 0;
    
    job2 := nJob2;        ! Make a copy of 2nd job in the queue before popping.
    IF jqPopJob() THEN
      StartBLoad job2;    ! Start Background load.
    ENDIF
    bInMod:=TRUE;         ! "Executing in Paint Module"
    SetDO doInMod,1;      ! "Running"
    %RunProc%;
    bInMod:=FALSE;        ! "Executing in Paint Module" no more
    SetDO doInMod,0;      ! "Running" no more
    SetDO do_9PS, 1;      ! Initiate an interrupt to update queue display ! 
    ClearJob;
    ! Clear out display until next job is loaded.
  ERROR
    ! Error handler
    TPWrite "RunJobS:External procedure "+RunProc+" not found";
    ! Should we try to find and load the missing program here? 
    bInMod:=FALSE;
    SetDO doInMod,0;
    RETURN;
  ENDPROC


  LOCAL PROC RunIntruderS()
    ! Load an intruding program without queue-shift !
    VAR string IntruderPg;    ! Procedure in intruding program
    VAR string LoadPgStr:="";
    VAR string DriveStr :="ram1disk:/";
    VAR string DefaultPg:="ram1disk:/m0.mod";

    ! Example: LOAD "flp1:m1.mod";
    ! Example: LOAD "ram1disk:m1.mod";

    LoadPgStr:=DriveStr+"m"+ValToStr(nSelIntruder)+".mod";
    ! TPWrite "Loading Intruding Program: " + LoadPgStr;

    IF strCurModNo<>"" THEN     ! No unload first time !
      IF bgLoading THEN
        WaitLoad lsBgLoadId;
        bgLoading:=FALSE;
        UnLoad strCurModNo;
      ELSE
        UnLoad strCurModNo;
      ENDIF
    ENDIF
    !SetDO do_6PS , 1;  ! Load signal: Loading !
    Load LoadPgStr;     ! If program not found, default program should run. (See error handler)
    !SetDO do_6PS , 0;  ! Done Loading !
    strCurModNo:=LoadPgStr;
    
    IntruderPg:="r"+ValToStr(nSelIntruder);
    nSelIntruder:=0;
    bNewIntruder:=FALSE;
    !TPWrite "Running Intruding Proc: " + IntruderPg;
    bInMod:=TRUE;
    SetDO doInMod,1;
    %IntruderPg%;
    bInMod:=FALSE;
    SetDO doInMod,0;
    ClearJob;         ! Clear out display until next job is loaded.
    bPreloaded:=FALSE; ! Next preload may take place now !
  ERROR
    ! Error handler
    TPWrite "Intruding Procedure "+RunProc+" not found";
    bInMod:=FALSE;
    SetDO doInMod,0;
    RETURN;
  ENDPROC


  PROC LoadNextSer()
    ! Preload next job and remove the job from the queue.
    ! This is the equivalent of the 'Cancel' button on the TR-5000 
    VAR bool Dummy;

    IF bNewLoad THEN
      PreLoadJob;
      Dummy:=jqPopJob();
      bNewLoad:=FALSE;
    ENDIF
  ENDPROC


  PROC PreLoadJob()
    ! Preload job without removing job from the queue.
    ! Job is removed from the queue when the program is started
    VAR num CurObject  :=0;
    VAR num CurObjPos1 :=0;
    VAR num CurObjPos2 :=0;
    VAR num CurBrush   :=0;
    VAR num nJobs      :=0;

    IF jqReadNextJob(CurObject,CurBrush,CurObjPos1,CurObjPos2, nJobs) THEN
       IF bDebugMode TPWrite "PreLoadJob: Calling LoadJob";
       LoadJob CurObject,CurBrush,CurObjPos1,CurObjPos2;
       nLastObject:=CurObject;
       nLastBrush :=CurBrush;
       IF bQueueTrackMode THEN
          !!! Only with queue-tracking node dsqc 377 configured !!!!!
          !  Send CurObjPos to Encoder Node !
          SetGO c1CntToEnc1,CurObjPos1;
          SetGO c1CntToEnc2,CurObjPos2;
          IF bForceJob Set c1ForceJob;
          PulseDO c1CntToEncStr;
          IF bForceJob Reset c1ForceJob;
          IF bDebugMode TpWrite "PreLoadJob/Writing Pos to ENC!";
       ENDIF
    ELSE
       ! Queue was empty !
       IF bRepeatLastMode AND (nLastObject>0) THEN
          IF bDebugMode TpWrite "PreLoadJob:jqReadNextJob returned FALSE!";
          LoadJob nLastObject,nLastBrush,CurObjPos1,CurObjPos2;
       ENDIF
    ENDIF
  ENDPROC


  PROC ExeSer()
    ! If a new sync-signal has arrived, and a job is present in the queue: Run job!
    
    VAR string DefaultPg:="ram1disk:/m0.mod";
    
    IF nSelIntruder>0 THEN
      RunIntruderS;
    ENDIF
    
    IF bNormlTrackMode OR bQueueTrackMode THEN
      !  WaitWobj Wobj1;      ! In TrackMode, we must take care if a job was dropped. 
      !  If instruction above fails, error handler takes over.
    ENDIF

    ! If NOT Current job = first job in queue (Jobs may have been cancelled in background)
    !    Load next job !?

    IF bNewSync OR bNormlTrackMode OR bQueueTrackMode THEN
      ! When Tracking, we don't wait for startsignal. 
      bNewSync:=FALSE;
      IF bAutoStartOn=TRUE THEN
        IF bBgLoadMode AND bgLoading THEN
          WaitLoad lsBgLoadId;
          bgLoading:=FALSE;
          RunJobS;  ! Run job when available. 
        ELSE
          IF NOT bPreloaded PreLoadJob;
          IF NOT bPreloaded THEN ! PreLoadJob did not succeed !
            IF bTestRack TPWrite "ExeSer: Queue is empty";
            ! If still not preloaded: no more jobs !
          
          ELSE
            RunJobS;  ! Run if job available. 
          ENDIF
        ENDIF
        bPreloaded:=FALSE;
      ENDIF
    ENDIF
    IF (NOT bPreloaded) AND (nNoOfJobs>0) AND (bAutoStartOn=TRUE) PreLoadJob;
    ! Load Next Job if possible
  ERROR
    ! Error handler
    ! If WaitWobj failed, execution will continue here
    ! TPWrite "Job was dropped"; !?
    bPreloaded:=FALSE;
    ClearJob;
    IF bDebugMode TPWrite "ExeSer: ErrorHandler";
    IF ERRNO=ERR_FILNOTFND THEN
      IF bDebugMode TPWrite "ExeSer: ErrorHandler ERR_FILNOTFND";
      load DefaultPg;
      strCurModNo:=DefaultPg;
      RunProc:="r"+ValToStr(0);
      bPreloaded:=TRUE;
      TRYNEXT;
    ENDIF  
    RETURN;
  ENDPROC


   LOCAL FUNC BOOL ReadFromFile(VAR STRING str, VAR iodev f1)

      VAR NUM TimeOut := 0.1;

      ! At timeout, the error handler will run
      str := ReadStr(f1\Time := TimeOut);
      IF str = "EOF" THEN ! End-of-file reached !
         RETURN FALSE;
      ELSE
         RETURN TRUE;
      ENDIF
      
   ERROR ! Error handler
      IF ERRNO = ERR_DEV_MAXTIME THEN
         str := "EOF";
      ELSE
         str := "ERROR";
      ENDIF
      RETURN FALSE;
   ENDFUNC



   LOCAL PROC GetTemplateName(STRING file, VAR STRING name)

      VAR iodev f1;
      VAR STRING str;
      CONST STRING pattern := "!!TemplateName!!";
   
      name:="";   
      Open "ram1disk:" + file, f1\Read;
     !Only reading first line

      IF ReadFromFile(str, f1) THEN
         IF StrLen (str) > StrMatch (str, 1, pattern) THEN ! Found !
            ! Extract from pattern to the end of line 
              name := StrPart(str, StrLen(pattern), StrLen(str)-StrLen(pattern));
        ENDIF
      ENDIF
    
     CLOSE f1;
     RETURN;

   ERROR ! Error handler
     name:="";
     !TPWrite "Something ugly happened in GetTemplateName";
     RETURN;

   ENDPROC



   PROC CreateNewProc()
   
      VAR NUM TemplNo := 0;
      VAR NUM MyReg1  := 0;
      VAR NUM strPos1 := 0;
      VAR NUM strPos2 := 0;
      VAR NUM strPos3 := 0;
      VAR NUM strLengde := 0;

      VAR BOOL WContinue := TRUE;

      VAR string TemplName := " ";
      VAR string ModuleName:= " ";
      VAR STRING str := " ";
      VAR STRING str2:= " ";

      VAR STRING tpl1:= "t1";
      VAR STRING tpl2:= "t2";
      VAR STRING tpl3:= "t3";
      VAR STRING tpl4:= "t4";
      VAR STRING tpl5:= "t5";
      
      CONST STRING pattern := "##ModuleNumber##";
      
      VAR iodev f1;
      VAR iodev f2;

      TPWrite "CreateNewProc() executing";
      !GetTemplateName "t1.prg", tpl1;
      !GetTemplateName "t2.prg", tpl2;
      !GetTemplateName "t3.prg", tpl3;
      !GetTemplateName "t4.prg", tpl4;
      !GetTemplateName "t5.prg", tpl5;
     
      TPReadFK TemplNo, "Select template", tpl1, tpl2, tpl3, tpl4, tpl5;
      
      TemplName  := "T" +ValToStr(TemplNo) + ".prg";
      ModuleName := "m" +ValToStr(TemplNo) + ".mod";

      TPWrite "TemplName:  " + TemplName;
      TPWrite "ModuleName: " + ModuleName;

      
      TPReadFK MyReg1, "Template " + TemplName + " chosen. OK to proceed?", "", "", "", "OK", "Cancel";
      
      IF MyReg1 = 4 THEN
         
         Open "ram1disk:" + TemplName, f1\Read;
         Open "ram1disk:" + ModuleName, f2\Write;

         WHILE WContinue DO


            IF ReadFromFile(str, f1) THEN
               strLengde := StrLen (str);
               strPos1 := StrMatch (str, 1, pattern);
               IF strLengde > strPos1 THEN ! Found !
                  ! Extract first part of string up to pattern, 
                  ! remove pattern, insert module number, 
                  ! then add rest of string.
                  strPos2 := strPos1 + StrLen(pattern);
                  strPos3 := StrLen(str) -  (strPos1 + StrLen(pattern));

                  str2 := StrPart(str, 1, strPos1 - 1) + ValToStr(TemplNo); 
        IF strPos3 > 0 THEN      
                     str2:=str2 + StrPart(str, strPos2, strPos3 +1 );
                     !TPWrite "Str2: " + str2;
                  ENDIF
       
        str := str2;
               ENDIF
               
               Write f2, str;
              ! TPWrite "OutPut file: " + str;

            ELSE
               
               Close f1;
               Close f2;
               WContinue := FALSE;
               
            ENDIF

         ENDWHILE

      ENDIF

      bCreateNewProg := FALSE;
      
   ERROR
   
   ENDPROC



ENDMODULE


