Ferreteria/v0.6/clade/IO/Aspect/Connx/run/Session/@code/cur/2026/09/10

From WoozleCodes
Jump to navigation Jump to search
2026/09/09 2026/09/10 2026/09/11

This is a snapshot from just before cleaning out some code that I've commented out (including some that I commented out just now, shortly after re-enabling it).

/* APipes*  [c,i] */ use Woozalia\Ferret\IO\Aspect\Connx\run\aux\A\{ cPipes as APipesClass, iPipes as APipesIface };
/* Base*   [ca,i] */ use Woozalia\Ferret\Sys\InOut\{ caConnx as BaseClass, iConnx as BaseIface };
/* CanalClass     */ use Woozalia\Ferret\IO\Aspect\Connx\cCanal as CanalClass;
/* Canals*  [c,i] */ use Woozalia\Ferret\IO\Aspect\Connx\aux\A\{ cCanals as CanalsClass, iCanals as CanalsIface };
/* CommandIface   */ use Woozalia\Ferret\IO\Aspect\Connx\run\iCommand as CommandIface;
/* EngineIface    */ use Woozalia\Ferret\Sys\Data\iEngine as EngineIface;
/* EventIface     */ use Woozalia\Ferret\Sys\Events\iEvent as EventIface;
/* EventOnAfter   */ use Woozalia\Ferret\Sys\Events\Event\Loops\cOnAfter as EventOnAfter;
/* EventOnBefore  */ use Woozalia\Ferret\Sys\Events\Event\Loops\cOnBefore as EventOnBefore;
/* EventOnLoop    */ use Woozalia\Ferret\Sys\Events\Event\Loops\cOnLoop as EventOnLoop;
/* EventOnReset   */ use Woozalia\Ferret\IO\Aspect\Connx\Stream\aux\Event\cReset as EventOnReset;
/* OpCmd*  [c,i]  */ use Woozalia\Ferret\IO\Aspect\Connx\run\Session\aux\{ cOpCmd as OpCmdClass, iOpCmd as OpCmdIface };
/* OpDataIface    */ use Woozalia\Ferret\Sys\Events\ItWent\OpComm\cOpData as OpDataIface;
/* OpOpenIface    */ use Woozalia\Ferret\Sys\Events\ItWent\connx\iOpOpen as OpOpenIface;
/* OpShutIface    */ use Woozalia\Ferret\Sys\Events\ItWent\connx\iOpShut as OpShutIface;
/* QPanel*  [c,i] */ use Woozalia\Ferret\IO\O\Panel\aux\{ cQPanel as QPanelClass, iQPanel as QPanelIface };
/* QStream* [c,i] */ use Woozalia\Ferret\Sys\InOut\Connx\aux\Q\{ cStream as QStreamClass, iStream as QStreamIface };
/* SelfIface      */ use Woozalia\Ferret\IO\Aspect\Connx\run\iSession as SelfIface;
/* ServIface      */ use Woozalia\Ferret\Sys\InOut\Connx\iServer as ServIface;
/* StartIface     */ use Woozalia\Ferret\IO\Aspect\Connx\run\iStarter as StartIface;
/* StreamIface    */ use Woozalia\Ferret\Sys\InOut\Connx\iStream as StreamIface;

interface iSession extends BaseIface {
    // SETUP
    #static function FromCommand(CommandIface $o) : SelfIface;
    #static function FromStartCmd(StartIface $o, CommandIface $oCmd) : SelfIface;
    #static function FromStarter(StartIface $o) : SelfIface;
    #static function FromServer(ServIface $o) : SelfIface;
    #static function FromEngine(EngineIface $o) : SelfIface;
    // STATUS
    var bool $IsRunning { get; }
    // ACTION
    #function DoCommand(CommandIface &$oCmd) : OpCmdIface;
    function DoStream(StreamIface $oStrm) : OpCmdIface;
    #function DoRequest(CommandIface $o) : OpDataIface;
    function DoInOutLoop()  : OpCmdIface; // FUTURE (2026/03/17): We *might* eventually need a different Op class. This is what we had been using, however.
    function DoBreak();
    // OBJECTS
    #var StartIface    $OStart { get; }
    #var CommandIface  $OCommand { get; }
    function QPanel() : QPanelIface;
    var OpCmdIface $OOpCmd { get; }
    // OBJECTS: action
    #function NewSession(CommandIface &$oCmd) : SelfIface;
    // OBJECTS: streams
    var StreamIface $OExecLecture { get; }  // stream received from process (stdout) (the process's lecture)
    var StreamIface $OExecRespond { get; }  // stream for talking to process (stdin) (responding to the process)
    var StreamIface $OExecErrsBin { get; }  // stream for process to send errors (stderr) (errors from process)
    var QStreamIface $QOAppOuStrm { get; }
    var QStreamIface $QOAppInStrm { get; }
    var QStreamIface $QOAppEIStrm { get; }
}
class cSession extends BaseClass implements SelfIface {

    // ++ CONFIG ++ //

    // CONFIG
    public function SType() : string { return 'run session'; }

    #public string $SIdentityForLog { get => parent::$SIdentityForLog::get().' '.$this->OCommand->AsString; }

    protected string $SCSession { get => cSession::class; }

    // -- CONFIG -- //
    // ++ SETUP ++ //

    // 2026-09-10 This is just FromServer() again, but with a less confusing name.
    public static function FromRunner(ServIface $o) : SelfIface {
        $oThis = new static;
        $oThis->ORun = $o;
        return $oThis;
    }

    /* 2026-09-10 I actually think maybe Command needs to be the key.
    public static function FromEngine(EngineIface $o) : SelfIface {
        $oThis = new static;
        $oThis->OEng = $o;
        if (!isset($o->OCmdConn)) {
            $this->AmHere('WARNING: Engine connection object needs to be set in the configuration.');
        }
        return $oThis;
    }
    */

    /* 2026-09-07 replacing with FromEngine()
    public static function FromServer(ServIface $o) : SelfIface {
        $oThis = new static;
        $oThis->OServ = $o;
        return $oThis;
    }
    */

    /*
    public static function FromCommand(CommandIface $oCmd) : SelfIface {
        $oThis = new static;
        $oThis->OCommand = $oCmd;
        return $oThis;
    }
    */

    // -- SETUP -- //
    // ++ STATUS ++ //

    var bool $IsRunning { get => $this->ORun->IsRunning; }

    // -- STATUS -- //
    // ++ ACTION ++ //

    private $doBreak;
    public function DoBreak() { $this->doBreak = TRUE; }

    // ACTION: Runs the executable and makes I/O/E Streams available
    public function DoCommand(CommandIface &$oCmd) : OpCmdIface {
        $qoUpd = self::QOUpdater();

        $oUpdSave = $qoUpd->ItNz;

        // if this command has an updater, deploy it:
        $oUpd = $oCmd->OUpdate;
        $qoUpd->ItNz = $oUpd;

        $qoUpd->OnEvent(new EventOnBefore);

        $oRun = $this->ORun;
        $ok = $oRun->Run($oCmd->AsString);
        $this->AmHere('Session after running "'.$oCmd->AsString.'":'.CRLF.$this->DIAG_AsBlock);
        if ($ok) {
            $oOpCmd = $this->DoInOutLoop();
            #$oOpComm->Assimilate($oOpLoop);
        } else {
            $oOpCmd = OpCmdClass::FromOkay(FALSE);
        }

        /* 2026-09-07 old version
        #$oOpComm = $this->SendCommand($oCmd);
        if ($oOpComm->IsOkay) {
            $oOpLoop = $this->DoInOutLoop();
            $oOpComm->Assimilate($oOpLoop);
        }
        */
        $oUpd->OnEvent(new EventOnAfter);

        $qoUpd->ItNz = $oUpdSave;

        return $oOpCmd;
    }
    public function DoStream(StreamIface $oStrm) : OpCmdIface {
        $oUpd = $this->OCommand->OUpdate;
        $qoUpd = self::QOUpdater();
        $qoUpd->It = $oUpd;

        $this->QOAppOuStrm->SetIt($oStrm);
        return $this->DoInOutLoop();
    }
    /* 2026-09-08 no longer used.
    protected function SendCommand(CommandIface &$oCmd) : OpCmdIface {
        $sCmd = $oCmd->AsString;
        $oOpComm = $this->OExecRespond->PushBytes($sCmd."\n");
        $oOpCmd = new OpCmdClass;
        $oOpCmd->Assimilate($oOpComm);
        return $oOpCmd;
    }
    */

    // -- ACTION -- //
    // ++ LIFECYCLE ++ //

    protected function ActualOpen() : OpOpenIface {
        $oConn = $this->ORun;
      $this->AmHere('ActualOpen of process, class '.get_class($oConn));
        return $oConn->Open();

        // TODO 2026-09-09: This is where we need to call Run(), I think -- not in DoCommand(). Or something.
    }
    protected function ActualShut() : OpShutIface {
      $this->AmHere('ActualShut of process, class');
        $oConn = $this->ORun;
        return $oConn->Shut();
    }

    // ++ LIFECYCLE: internals ++ //

    public function DoInOutLoop() : OpCmdIface {
        $this->CanalSetup();
        $qoUpdate = self::QOUpdater();

        if ($qoUpdate->HasIt) {
            $ou = $qoUpdate->It;
            $ou->OSession = $this;
        }

        $oCanals = $this->OCanals;
        $oCanals->Open();

        $oOpConvey = NULL;
        $oOpThis = $this->OOpCmd;

        $doLoop = TRUE;
        $this->doBreak = FALSE;


        $oeBefore = new EventOnBefore;
        $oeLoop = new EventOnLoop;
        $oeAfter = new EventOnAfter;
        $oCanals->OnEvent($oeBefore);
        $qoUpdate->OnEvent($oeBefore);

        $this->AmHere('SESSION: before loop'.CRLF.$this->DIAG_AsBlock);

        while ($doLoop) {
            $qoUpdate->OnEvent($oeLoop);
            $oOpNew = $oCanals->ConveyCheck();
            if (!$oOpNew->IsOkay) {
                $this->DoBreak();
            }
            $oOpThis->Assimilate($oOpNew);
            $doLoop = $this->IsRunning && !$this->doBreak;
        }
        $this->AmHere('SESSION: after loop'.CRLF.$this->DIAG_AsBlock);
        $oCanals->OnEvent($oeAfter);
        $qoUpdate->OnEvent($oeAfter);

        $oCanals->Shut();

        return $oOpThis;
    }
    // 2026-03-30 This will need some tweaking to work on anything except Local\Proc.
    // 2026-04-03 Moved from base Session to Proc.
    // 2026-09-07 Moved back to Session, at least for now. Tweakage shall commence forthwith.
    protected function CanalSetup() {
      $this->AmHere('SETUP local Canals');

        $oRun = $this->ORun;

        $oProcLect = $oRun->OuStrm; // Process Lecturer
        $oProcLstn = $oRun->InStrm;  // Process Listener
        $oProcErrs = $oRun->EOStrm;  // Process Errors

        $qoAppLect = $this->QOAppOuStrm;
        $qoAppLstn = $this->QOAppInStrm;
        $qoAppErrs = $this->QOAppEIStrm;

        // ++ CANALS: build the Canals Stream-list

        $oCanals = CanalsClass::FromVoid();

        $oCanalRecv = CanalClass::FromPair($oProcLect,$qoAppLstn->It);
        $oCanals->AddIt($oCanalRecv);

        if ($qoAppLect->HasIt) {
            $oCanalSend = CanalClass::FromPair($qoAppLect->It,$oProcLstn);
            $oCanals->AddIt($oCanalSend);
        }

        $oCanalErrs = CanalClass::FromPair($oProcErrs,$qoAppErrs->It);
        $oCanals->AddIt($oCanalErrs);

        $this->AmHere('CANALS:'.CRLF.$oCanals->VIEW_AsBlock);

        // -- CANALS

        $oCanals->OnEvent(new EventOnReset);  // reset all the streams
        $this->OCanals = $oCanals;

        /* 2026-09-07 old

        $oaPrcStrms = $oRun->OPipes;
        $oaPrcStrms->Block(FALSE);

        $oaAppStrms = $this->OACodeStrms;

        $oCanals = CanalsClass::FromVoid();

        $oProcLecture = $oaPrcStrms->OStream; // Process Lecturer
        $oProcListen = $oaPrcStrms->IStream;  // Process Listener
        $oProcErrors = $oaPrcStrms->EStream;  // Process Errors

        $qoAppLect = $this->QOAppOuStrm;
        $qoAppLstn = $this->QOAppInStrm;
        $qoAppErrs = $this->QOAppEIStrm;

        $oCmdListen = NULL;
        $oCmdLecture = NULL;
        $oCmdErrors = NULL;
        if ($qoAppLstn->HasIt) {
            $oCmdListen = $qoAppLstn->It;   // Command Listener stream
            $oCanalRecv = CanalClass::FromPair($oProcLecture,$oCmdListen);
            $oCanals->AddIt($oCanalRecv);
        } else {
            $this->AmHereShort('Application has no listener.');
        }
        if ($qoAppLect->HasIt) {
            $oCmdLecture = $qoAppLect->It;  // Command Lecturer stream
            $oCanalSend = CanalClass::FromPair($oCmdLecture,$oProcListen);
            $oCanals->AddIt($oCanalSend);
        #} else {
        #    $this->AmHereShort('No command-lecturer.');
        }
        if ($qoAppErrs->HasIt) {
            $oCmdErrors = $qoAppErrs->It;    // Command Error-messages stream
            $oCanalErrs = CanalClass::FromPair($oProcErrors,$oCmdErrors);
            $oCanals->AddIt($oCanalErrs);
        } else {
            $this->AmHereShort('Application has no error-bin.');
        }
        $oCanals->OnEvent(new EventOnReset);  // reset all the streams
        $this->OCanals = $oCanals;
        */
    }

    // -- LIFECYCLE -- //
    // ++ OBJECTS ++ //

    /* 2026-09-05 not sure about this...
    public function NewSession(CommandIface &$oCmd) : SelfIface {
        $oSess = ($this->SCSession)::FromStarter($this);
        $oSess->OCommand = $oCmd;
        return $oSess;
    }
    */
    #protected ServIface $OServ;
    #protected EngineIface $OEng;
    protected CommandIface $OCmd;
    protected ServIface $ORun { get => $this->OEng->OCmdConn; }

    #public StartIface $OStart;

    public APipesIface $OACodeStrms { get => $this->OACodeStrms ??= new APipesClass; }  // pipes from application code
    // NOTE: Local-Proc defines an additional set for the process (2026-08-23 why don't we need that for SSH2?)

    // OBJECTS: streams
    public StreamIface $OExecLecture { get => $this->ORun->OuStrm; }  // stream received from process (stdout) (the process's lecture)
    public StreamIface $OExecRespond { get => $this->ORun->InStrm; }  // stream for talking to process (stdin) (responding to the process)
    public StreamIface $OExecErrsBin { get => $this->ORun->EOStrm; }  // stream for process to send errors (stderr) (errors from process)
    // SHORTCUTS
    public QStreamIface $QOAppOuStrm { get => $this->OACodeStrms->QOStream; }
    public QStreamIface $QOAppInStrm { get => $this->OACodeStrms->QIStream; }
    public QStreamIface $QOAppEIStrm { get => $this->OACodeStrms->QEStream; }

    protected CanalsIface $OCanals;

    /*
    public CommandIface $OCommand {
        set(CommandIface $o) {
            $o->OSession = $this;
            $this->OCommand = $o;
            $oUpd = $o->OUpdate;
            self::QOUpdater()->SetObjNz($oUpd);
        }
    }
    */

    private $qoPanel = NULL;
    public function QPanel() : QPanelIface { return $this->qoPanel ?? ($this->qoPanel = QPanelClass::AsNew()); }

    // 2026-04-01 copied from Starter
    #private $oRes = NULL;
    #public function OOpCmd(?OpCmdIface $o=NULL) : OpCmdIface { return is_null($o) ? ($this->oRes ?? $this->OOpCmdNew()) : ($this->oRes = $o); }
    public OpCmdIface $OOpCmd { get => $this->OOpCmd ??= $this->OOpCmdNew(); }
    protected function OOpCmdNew() : OpCmdIface { return OpCmdClass::FromSession($this); }

    // -- OBJECTS -- //
    // ++ DEBUG ++ //

    public function VIEW_Inline() : string {
        $a = $this->OACodeStrms->AVals;
        $s = '';
        foreach ($a as $key => $oStream) {
            $sType = $oStream->SType();
            $sID = $oStream->SIdentityForLog;
            $s .= "[#$key:$sID ($sType)]";
        }
        return $s;
    }

    public string $DIAG_AsBlock { get {
        $a = $this->OACodeStrms->AVals;
        $s = 'Code streams'.CRLF;
        foreach ($a as $key => $oStream) {
            $sType = $oStream->SType();
            $sID = $oStream->SIdentityForLog;
            $s .= " - [#$key:$sID ($sType)] - ".get_class($oStream).CRLF;
        }
        if (isset($this->OCanals)) {
            $s .= $this->OCanals->VIEW_AsBlock;
        } else {
            $s .= "(Canals not yet created.)".CRLF;
        }
        return $s;
    } }

    // -- DEBUG -- //
}