480 строки
8.6 KiB
PHP
480 строки
8.6 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
namespace OCA\FullTextSearch\Model;
|
|
|
|
|
|
use OCA\FullTextSearch\Tools\Traits\TArrayTools;
|
|
use Exception;
|
|
use OCA\FullTextSearch\ACommandBase;
|
|
use OCA\FullTextSearch\Exceptions\RunnerAlreadyUpException;
|
|
use OCA\FullTextSearch\Exceptions\TickDoesNotExistException;
|
|
use OCA\FullTextSearch\Exceptions\TickIsNotAliveException;
|
|
use OCA\FullTextSearch\Service\RunningService;
|
|
use OCP\FullTextSearch\Model\IIndex;
|
|
use OCP\FullTextSearch\Model\IRunner;
|
|
use Symfony\Component\Console\Output\OutputInterface;
|
|
|
|
|
|
/**
|
|
* Class Runner
|
|
*
|
|
* @package OCA\FullTextSearch\Model
|
|
*/
|
|
class Runner implements IRunner {
|
|
|
|
|
|
use TArrayTools;
|
|
|
|
|
|
const TICK_MINIMUM = 2;
|
|
const TICK_UPDATE = 10;
|
|
const MEMORY_UPDATE = 5;
|
|
|
|
/** @var RunningService */
|
|
private $runningService;
|
|
|
|
/** @var string */
|
|
private $source;
|
|
|
|
/** @var int */
|
|
private $tickId = 0;
|
|
|
|
/** @var ACommandBase */
|
|
private $base = null;
|
|
|
|
/** @var OutputInterface */
|
|
private $outputInterface = null;
|
|
|
|
/** @var array */
|
|
private $info = [];
|
|
|
|
/** @var int */
|
|
private $oldTick = 0;
|
|
|
|
/** @var string */
|
|
private $oldAction = '';
|
|
|
|
/** @var int */
|
|
private $ramUpdate = 0;
|
|
|
|
/** @var int */
|
|
private $tickUpdate = 0;
|
|
|
|
/** @var array */
|
|
private $methodOnKeyPress = [];
|
|
|
|
/** @var array */
|
|
private $methodOnInfoUpdate = [];
|
|
|
|
/** @var array */
|
|
private $methodOnIndexError = [];
|
|
|
|
/** @var array */
|
|
private $methodOnIndexResult = [];
|
|
|
|
/** @var bool */
|
|
private $paused = false;
|
|
|
|
/** @var bool */
|
|
private $pauseRunning = false;
|
|
|
|
/** @var array */
|
|
private $keys = ['nextStep' => 'n'];
|
|
|
|
|
|
/**
|
|
* Runner constructor.
|
|
*
|
|
* @param RunningService $runningService
|
|
* @param string $source
|
|
* @param array $keys
|
|
*/
|
|
public function __construct(RunningService $runningService, string $source, array $keys = []) {
|
|
$this->runningService = $runningService;
|
|
$this->source = $source;
|
|
|
|
if (sizeof($keys) > 0) {
|
|
$this->keys = $keys;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @throws RunnerAlreadyUpException
|
|
*/
|
|
public function start() {
|
|
$this->tickId = $this->runningService->start($this->source);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $action
|
|
* @param bool $force
|
|
*
|
|
* @return string
|
|
* @throws Exception
|
|
*/
|
|
public function updateAction(string $action = '', bool $force = false): string {
|
|
|
|
if ($this->base !== null) {
|
|
$this->base->abort();
|
|
}
|
|
|
|
$n = '';
|
|
if (sizeof($this->methodOnKeyPress) > 0) {
|
|
$n = fread(STDIN, 9999);
|
|
if ($n !== '') {
|
|
$n = substr($n, 0, 1);
|
|
$this->keyPressed($n);
|
|
}
|
|
}
|
|
|
|
if ($action === '') {
|
|
return $n;
|
|
}
|
|
|
|
$tick = time();
|
|
if ($this->oldAction !== $action || $force) {
|
|
while (true) {
|
|
if (!$this->isPaused()) {
|
|
break;
|
|
}
|
|
|
|
$this->pauseRunning(true);
|
|
$pressed = strtolower($this->updateAction(''));
|
|
if ($pressed === $this->keys['nextStep']) {
|
|
break;
|
|
}
|
|
|
|
usleep(300000);
|
|
if ($this->base !== null) {
|
|
$this->base->abort();
|
|
}
|
|
}
|
|
|
|
$this->pauseRunning(false);
|
|
}
|
|
|
|
if ($this->oldAction === $action && ($this->oldTick + self::TICK_MINIMUM > $tick)) {
|
|
return '';
|
|
}
|
|
|
|
$this->setInfo('action', $action);
|
|
|
|
$this->updateTick($tick, $action);
|
|
$this->updateRamInfo($tick);
|
|
|
|
$this->oldAction = $action;
|
|
$this->oldTick = $tick;
|
|
|
|
return '';
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $info
|
|
* @param string $value
|
|
* @param int $type
|
|
*/
|
|
public function setInfo(string $info, string $value, int $type = 0) {
|
|
$this->info[$info] = $value;
|
|
$this->setInfoColored($info, $type);
|
|
$this->infoUpdated();
|
|
}
|
|
|
|
/**
|
|
* @param string $info
|
|
* @param int $value
|
|
*/
|
|
public function setInfoInt(string $info, int $value): void {
|
|
$this->info[$info] = $value;
|
|
$this->infoUpdated();
|
|
}
|
|
|
|
/**
|
|
* @param array $data
|
|
*/
|
|
public function setInfoArray(array $data) {
|
|
$keys = array_keys($data);
|
|
foreach ($keys as $k) {
|
|
$this->info[$k] = $data[$k];
|
|
}
|
|
|
|
$this->infoUpdated();
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $info
|
|
* @param int $level
|
|
*/
|
|
public function setInfoColored(string $info, int $level) {
|
|
|
|
$value = $this->getInfo($info);
|
|
if ($value === '') {
|
|
return;
|
|
}
|
|
|
|
$color = '';
|
|
switch ($level) {
|
|
case IRunner::RESULT_TYPE_SUCCESS:
|
|
$color = 'info';
|
|
break;
|
|
|
|
case IRunner::RESULT_TYPE_WARNING:
|
|
$color = 'comment';
|
|
break;
|
|
|
|
case IRunner::RESULT_TYPE_FAIL:
|
|
$color = 'error';
|
|
break;
|
|
}
|
|
|
|
if ($color !== '') {
|
|
$this->info[$info . 'Colored'] = '<' . $color . '>' . $value . '</' . $color . '>';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public function getInfoAll(): array {
|
|
return $this->info;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $info
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getInfo(string $info): string {
|
|
return $this->get($info, $this->info, '');
|
|
}
|
|
|
|
/**
|
|
* @param string $info
|
|
*
|
|
* @return int
|
|
*/
|
|
public function getInfoInt(string $info): int {
|
|
return $this->getInt($info, $this->info);
|
|
}
|
|
|
|
/**
|
|
* @param array $method
|
|
*/
|
|
public function onKeyPress(array $method) {
|
|
$this->methodOnKeyPress[] = $method;
|
|
}
|
|
|
|
/**
|
|
* @param string $key
|
|
*/
|
|
public function keyPressed(string $key) {
|
|
foreach ($this->methodOnKeyPress as $method) {
|
|
call_user_func($method, $key);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @param array $method
|
|
*/
|
|
public function onInfoUpdate(array $method) {
|
|
$this->methodOnInfoUpdate[] = $method;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
public function infoUpdated() {
|
|
foreach ($this->methodOnInfoUpdate as $method) {
|
|
call_user_func($method, $this->info);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @param array $method
|
|
*/
|
|
public function onNewIndexError(array $method) {
|
|
$this->methodOnIndexError[] = $method;
|
|
}
|
|
|
|
/**
|
|
* @param IIndex $index
|
|
* @param string $message
|
|
* @param string $class
|
|
* @param int $sev
|
|
*/
|
|
public function newIndexError(IIndex $index, string $message, string $class = '', int $sev = 3
|
|
) {
|
|
$error = [
|
|
'index' => $index,
|
|
'message' => $message,
|
|
'exception' => $class,
|
|
'severity' => $sev
|
|
];
|
|
|
|
foreach ($this->methodOnIndexError as $method) {
|
|
call_user_func($method, $error);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @param array $method
|
|
*/
|
|
public function onNewIndexResult(array $method) {
|
|
$this->methodOnIndexResult[] = $method;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param IIndex $index
|
|
* @param string $message
|
|
* @param string $status
|
|
* @param int $type
|
|
*/
|
|
public function newIndexResult(IIndex $index, string $message, string $status, int $type) {
|
|
$result = [
|
|
'index' => $index,
|
|
'message' => $message,
|
|
'status' => $status,
|
|
'type' => $type
|
|
];
|
|
|
|
foreach ($this->methodOnIndexResult as $method) {
|
|
call_user_func($method, $result);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @param int $tick
|
|
* @param string $action
|
|
*
|
|
* @throws TickDoesNotExistException
|
|
*/
|
|
private function updateTick(int $tick, string $action) {
|
|
if ($this->oldAction === $action && ($this->tickUpdate + self::TICK_UPDATE > $tick)) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$this->runningService->update($this->tickId, $action);
|
|
} catch (TickIsNotAliveException $e) {
|
|
$this->output('Force Quit');
|
|
exit();
|
|
}
|
|
|
|
$this->tickUpdate = $tick;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param int $tick
|
|
*/
|
|
private function updateRamInfo(int $tick) {
|
|
if (($this->ramUpdate + self::MEMORY_UPDATE) > $tick) {
|
|
return;
|
|
}
|
|
|
|
$this->setInfo('_memory', round((memory_get_usage() / 1024 / 1024)) . ' MB');
|
|
$this->ramUpdate = $tick;
|
|
}
|
|
|
|
|
|
/**
|
|
* // TODO: finalize this exception handling about error logs.
|
|
*
|
|
* @param string $reason
|
|
* @param bool $stop
|
|
*/
|
|
public function exception(string $reason, bool $stop) {
|
|
if (!$stop) {
|
|
$this->output('Exception: ' . $reason);
|
|
// TODO: feed an array of exceptions for log;
|
|
} else {
|
|
try {
|
|
$this->runningService->stop($this->tickId, $reason);
|
|
} catch (TickDoesNotExistException $e) {
|
|
/** exception will be managed somewhere else */
|
|
// TODO: Check if above statement is correct.
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @throws TickDoesNotExistException
|
|
*/
|
|
public function stop() {
|
|
$this->runningService->stop($this->tickId);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param ACommandBase $base
|
|
* @param OutputInterface $output
|
|
*/
|
|
public function sourceIsCommandLine(ACommandBase $base, OutputInterface $output) {
|
|
$this->base = $base;
|
|
$this->outputInterface = $output;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param bool $pause
|
|
*/
|
|
public function pause(bool $pause) {
|
|
$this->paused = $pause;
|
|
$this->infoUpdated();
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
public function isPaused(): bool {
|
|
return $this->paused;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param bool $running
|
|
*/
|
|
public function pauseRunning(bool $running) {
|
|
$this->pauseRunning = $running;
|
|
$this->infoUpdated();
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
public function isPauseRunning(): bool {
|
|
return $this->pauseRunning;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $line
|
|
*/
|
|
public function output(string $line) {
|
|
if ($this->outputInterface === null) {
|
|
return;
|
|
}
|
|
|
|
$this->outputInterface->writeln($line);
|
|
}
|
|
|
|
|
|
}
|