Examples
Three complete programs, exactly as they run. They are adapted from the examples that ship in the FoxCore.Runtime 3.4.2 package, in the ejemplos\ folder: you can copy them, paste them into a .prg and run them from the Visual FoxPro IDE.
.prg files in the package read the JWT from a file on disk because they are loose files you run by hand and have to get it from somewhere; here it lives in a constant, which is what a real application does. See Licensing.1. A long task that says where it is
The worker calls runtime.Progress(), and here it is collected both ways: by asking with GetProgress() and by being told through a sink. The second one is the one worth understanding.
LOCAL loRt, loEco, lcId, lcScript, lcJwt
lcJwt = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...." && paste your JWT from the customer portal here
loRt = CREATEOBJECT("FoxCore.Runtime")
IF !loRt.Activate(lcJwt)
MESSAGEBOX("La licencia de FoxCore no es valida.", 16, "FoxCore")
RETURN
ENDIF
loRt.Attach(_VFP)
loEco = CREATEOBJECT("EcoDeProgreso")
? "sink aceptado :", loRt.SetEventSink(loEco)
TEXT TO lcScript NOSHOW
FOR i = 1 TO 5
runtime.Progress(i * 20, 'paso ' + TRANSFORM(i) + ' de 5')
=INKEY(0.2)
ENDFOR
runtime.Log('info', 'terminado sin novedad')
RETURN 'listo'
ENDTEXT
lcId = loRt.RunScript(lcScript, "con progreso")
DO WHILE !loRt.IsCompleted(lcId)
loRt.Pump() && the sink is called FROM HERE
ENDDO
loRt.Pump(200, 1000) && one last round: there may be notices queued
? "avisos recibidos :", loEco.nAvisos
? "ultimo aviso :", loEco.cUltimo
? "ultimo log :", loEco.cLog
? "y sin sink :", loRt.GetProgress(lcId), loRt.GetProgressMessage(lcId)
loRt.ClearEventSink() && important: while it holds it, your object stays alive
loRt.Shutdown()
RETURN
DEFINE CLASS EcoDeProgreso AS Custom
nAvisos = 0
cUltimo = ""
cLog = ""
*-- OnProgress y OnLog son OBLIGATORIOS: sin los dos, SetEventSink da .F.
PROCEDURE OnProgress(tcTaskId, tnPercent, tcMessage)
THIS.nAvisos = THIS.nAvisos + 1
THIS.cUltimo = TRANSFORM(tnPercent) + "% " + TRANSFORM(tcMessage)
ENDPROC
PROCEDURE OnLog(tcTaskId, tcLevel, tcMessage)
THIS.cLog = TRANSFORM(tcLevel) + ": " + TRANSFORM(tcMessage)
ENDPROC
*-- Este es opcional: se detecta si esta, y si no, no pasa nada.
PROCEDURE OnTaskCompleted(tcTaskId, tcState)
ENDPROC
ENDDEFINEWhat you will see
sink aceptado : .T.
avisos recibidos : 5
ultimo aviso : 100% paso 5 de 5
ultimo log : info: terminado sin novedad
y sin sink : 100,00 paso 5 de 52. Splitting a table across several workers
The most requested recipe, and the one most often done wrong. The key is that the cursor is NOT split: the RANGE is. Each worker opens the table on its own and handles one stretch, so nothing is shared and no locking is needed.
LOCAL loRt, lcDbf, i, lnDesde, lnHasta, lnPorTramo, lnTotal, lnHechas, lcTerm, lcJwt
LOCAL ARRAY laIds[4], laTerm[1]
lcJwt = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...." && paste your JWT from the customer portal here
*-- Una tabla de ejemplo con 400 lineas
CREATE CURSOR ventas (id I, importe Y)
FOR i = 1 TO 400
INSERT INTO ventas VALUES (i, i * 1.25)
ENDFOR
lcDbf = ADDBS(SYS(2023)) + "r09_ventas.dbf"
COPY TO (lcDbf)
USE IN SELECT("ventas")
loRt = CREATEOBJECT("FoxCore.Runtime")
IF !loRt.Activate(lcJwt)
MESSAGEBOX("La licencia de FoxCore no es valida.", 16, "FoxCore")
RETURN
ENDIF
loRt.Attach(_VFP)
loRt.MaxWorkers = 4
*-- Cuatro tramos de 100. each worker opens the table ON ITS OWN y suma el suyo.
lnPorTramo = 100
FOR i = 1 TO 4
lnDesde = (i - 1) * lnPorTramo + 1
lnHasta = i * lnPorTramo
laIds[i] = loRt.RunScript( ;
"USE '" + lcDbf + "' IN 0 SHARED ALIAS ventas" + CHR(13) + CHR(10) + ;
"SELECT ventas" + CHR(13) + CHR(10) + ;
"SUM importe TO lnT FOR BETWEEN(id, " + TRANSFORM(lnDesde) + ", " + TRANSFORM(lnHasta) + ")" + CHR(13) + CHR(10) + ;
"USE IN ventas" + CHR(13) + CHR(10) + ;
"RETURN lnT", "tramo " + TRANSFORM(i))
ENDFOR
lnHechas = 0
DO WHILE lnHechas < 4
loRt.Pump()
lcTerm = loRt.PollCompleted()
IF !EMPTY(lcTerm)
lnHechas = lnHechas + ALINES(laTerm, lcTerm, 1, ";")
ENDIF
ENDDO
*-- and it is joined here, where there is a single thread
lnTotal = 0
FOR i = 1 TO 4
? " tramo " + TRANSFORM(i) + " :", loRt.GetState(laIds[i]), loRt.GetResult(laIds[i])
IF loRt.GetState(laIds[i]) == "Completed"
lnTotal = lnTotal + loRt.GetResult(laIds[i])
ENDIF
ENDFOR
? "total en paralelo :", lnTotal, " (esperado 100250,00)"
ERASE (lcDbf)
loRt.Shutdown()
RETURNWhat you will see
tramo 1 : Completed 6312,50
tramo 2 : Completed 18812,50
tramo 3 : Completed 31312,50
tramo 4 : Completed 43812,50
total en paralelo : 100250,00 (esperado 100250,00)3. A handler of your own, called from the worker
The classic case: a sequence number. Eight processes asking for a number at the same time and not one duplicate, with an object that carries no lock at all.
LOCAL loRt, loCaja, i, lnHechas, lcTerminadas, lcJwt
LOCAL ARRAY laIds[8], laTerm[1]
lcJwt = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...." && paste your JWT from the customer portal here
loRt = CREATEOBJECT("FoxCore.Runtime")
IF !loRt.Activate(lcJwt)
MESSAGEBOX("La licencia de FoxCore no es valida.", 16, "FoxCore")
RETURN
ENDIF
loRt.Attach(_VFP)
loRt.MaxWorkers = 4
loCaja = CREATEOBJECT("CajaDeFolios")
? "handler aceptado :", loRt.RegisterHandler("SiguienteFolio", loCaja, "Siguiente")
FOR i = 1 TO 8
laIds[i] = loRt.RunScript("RETURN runtime.Call('SiguienteFolio', 'FRA')", "folio " + TRANSFORM(i))
ENDFOR
lnHechas = 0
DO WHILE lnHechas < 8
loRt.Pump() && this is where the calls get served
lcTerminadas = loRt.PollCompleted()
IF !EMPTY(lcTerminadas)
lnHechas = lnHechas + ALINES(laTerm, lcTerminadas, 1, ";")
ENDIF
ENDDO
? "folios emitidos :", loCaja.nUltimo
FOR i = 1 TO 8
?? " " + TRANSFORM(loRt.GetResult(laIds[i]))
ENDFOR
loRt.Shutdown()
RETURN
DEFINE CLASS CajaDeFolios AS Custom
nUltimo = 0
*-- not a single lock, and none needed: esto se ejecuta en el hilo del host,
*-- de una llamada en una llamada.
FUNCTION Siguiente(tcSerie)
THIS.nUltimo = THIS.nUltimo + 1
RETURN TRANSFORM(tcSerie) + "-" + PADL(THIS.nUltimo, 4, "0")
ENDFUNC
ENDDEFINEWhat you will see
handler aceptado : .T.
folios emitidos : 8 FRA-0001 FRA-0002 FRA-0003 FRA-0004 FRA-0005 FRA-0006 FRA-0007 FRA-0008FoxCoreClass, Run() or WaitFor(). The equivalence table is in "What no longer exists", at the end of the API Reference. Next: API Reference ->