From 9288466bb0e47e129fcc2d6194177561059c08d1 Mon Sep 17 00:00:00 2001 From: Adam Kadlec Date: Tue, 5 Jul 2022 23:15:09 +0200 Subject: [PATCH] Refactoring connector factory (#3) * Refactored connector command --- composer.json | 4 +- fastybird_devices_module/__init__.py | 2 +- package.json | 2 +- src/Commands/ConnectorCommand.php | 183 ++++++++++++- src/Connectors/Connector.php | 248 ------------------ src/Connectors/IConnector.php | 7 - src/DI/DevicesModuleExtension.php | 25 -- src/DataStorage/Writer.php | 2 - .../Unit/Connectors/ConnectorFactoryTest.phpt | 4 - 9 files changed, 179 insertions(+), 298 deletions(-) delete mode 100644 src/Connectors/Connector.php diff --git a/composer.json b/composer.json index c60d4667..387339b1 100644 --- a/composer.json +++ b/composer.json @@ -43,9 +43,9 @@ "ext-pcntl": "*", "contributte/flysystem": "^0.3.0", "cweagans/composer-patches": "^1.7", - "fastybird/exchange": "^0.47", + "fastybird/exchange": "^0.48", "fastybird/json-api": "^0.10", - "fastybird/metadata": "^0.65", + "fastybird/metadata": "^0.66", "fastybird/simple-auth": "^0.3", "ipub/doctrine-dynamic-discriminator-map": "^1.4", "ipub/doctrine-timestampable": "^1.5", diff --git a/fastybird_devices_module/__init__.py b/fastybird_devices_module/__init__.py index 67ec06b3..36a27696 100644 --- a/fastybird_devices_module/__init__.py +++ b/fastybird_devices_module/__init__.py @@ -18,4 +18,4 @@ Devices module """ -__version__ = "0.67.0" +__version__ = "0.68.0" diff --git a/package.json b/package.json index 7e57c467..0c0de924 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fastybird/devices-module", - "version": "0.66.0", + "version": "0.68.0", "description": "Devices module data model plugin", "keywords": [ "devices", diff --git a/src/Commands/ConnectorCommand.php b/src/Commands/ConnectorCommand.php index 4ba71268..6e17fec8 100644 --- a/src/Commands/ConnectorCommand.php +++ b/src/Commands/ConnectorCommand.php @@ -15,13 +15,20 @@ namespace FastyBird\DevicesModule\Commands; +use FastyBird\DateTimeFactory; use FastyBird\DevicesModule\Connectors; use FastyBird\DevicesModule\DataStorage; +use FastyBird\DevicesModule\Entities; +use FastyBird\DevicesModule\Events; use FastyBird\DevicesModule\Exceptions; use FastyBird\DevicesModule\Models; +use FastyBird\DevicesModule\Queries; +use FastyBird\Metadata\Entities as MetadataEntities; +use FastyBird\Metadata\Types as MetadataTypes; use League\Flysystem; use Nette\Localization; use Nette\Utils; +use Psr\EventDispatcher as PsrEventDispatcher; use Psr\Log; use Ramsey\Uuid; use React\EventLoop; @@ -29,6 +36,7 @@ use Symfony\Component\Console\Input; use Symfony\Component\Console\Output; use Symfony\Component\Console\Style; +use Throwable; /** * Module connector command @@ -41,12 +49,29 @@ class ConnectorCommand extends Console\Command\Command { - /** @var Connectors\Connector */ - private Connectors\Connector $connector; + private const SHUTDOWN_WAITING_DELAY = 3; + + /** @var Connectors\ConnectorFactory */ + private Connectors\ConnectorFactory $factory; /** @var Models\DataStorage\IConnectorsRepository */ private Models\DataStorage\IConnectorsRepository $connectorsRepository; + /** @var Models\Connectors\Properties\IPropertiesRepository */ + private Models\Connectors\Properties\IPropertiesRepository $connectorPropertiesRepository; + + /** @var Models\Connectors\Properties\IPropertiesManager */ + private Models\Connectors\Properties\IPropertiesManager $connectorPropertiesManager; + + /** @var Models\States\ConnectorPropertiesRepository */ + private Models\States\ConnectorPropertiesRepository $connectorPropertiesStateRepository; + + /** @var Models\States\ConnectorPropertiesManager */ + private Models\States\ConnectorPropertiesManager $connectorPropertiesStateManager; + + /** @var DateTimeFactory\DateTimeFactory */ + private DateTimeFactory\DateTimeFactory $dateTimeFactory; + /** @var DataStorage\Reader */ private DataStorage\Reader $reader; @@ -56,13 +81,22 @@ class ConnectorCommand extends Console\Command\Command /** @var Log\LoggerInterface */ private Log\LoggerInterface $logger; + /** @var PsrEventDispatcher\EventDispatcherInterface|null */ + private ?PsrEventDispatcher\EventDispatcherInterface $dispatcher; + /** @var EventLoop\LoopInterface */ private EventLoop\LoopInterface $eventLoop; public function __construct( - Connectors\Connector $connector, + Connectors\ConnectorFactory $factory, Models\DataStorage\IConnectorsRepository $connectorsRepository, + Models\Connectors\Properties\IPropertiesRepository $connectorPropertiesRepository, + Models\Connectors\Properties\IPropertiesManager $connectorPropertiesManager, + Models\States\ConnectorPropertiesRepository $connectorPropertiesStateRepository, + Models\States\ConnectorPropertiesManager $connectorPropertiesStateManager, DataStorage\Reader $reader, + DateTimeFactory\DateTimeFactory $dateTimeFactory, + ?PsrEventDispatcher\EventDispatcherInterface $dispatcher, Localization\Translator $translator, EventLoop\LoopInterface $eventLoop, ?Log\LoggerInterface $logger = null, @@ -70,12 +104,20 @@ public function __construct( ) { parent::__construct($name); - $this->connector = $connector; + $this->factory = $factory; $this->connectorsRepository = $connectorsRepository; + $this->connectorPropertiesRepository = $connectorPropertiesRepository; + $this->connectorPropertiesManager = $connectorPropertiesManager; + $this->connectorPropertiesStateRepository = $connectorPropertiesStateRepository; + $this->connectorPropertiesStateManager = $connectorPropertiesStateManager; + $this->reader = $reader; + $this->dateTimeFactory = $dateTimeFactory; + $this->translator = $translator; $this->eventLoop = $eventLoop; + $this->dispatcher = $dispatcher; $this->logger = $logger ?? new Log\NullLogger(); } @@ -139,13 +181,83 @@ protected function execute(Input\InputInterface $input, Output\OutputInterface $ return 0; } + $service = $this->factory->create($connector); + try { - $this->eventLoop->futureTick(function () use ($connector): void { - $this->connector->execute($connector); + $this->eventLoop->futureTick(function () use ($connector, $service): void { + if ($this->dispatcher !== null) { + $this->dispatcher->dispatch(new Events\BeforeConnectorStartEvent($connector)); + } + + $this->logger->debug('Starting connector...', [ + 'source' => 'devices-module', + 'type' => 'connector', + ]); + + try { + // Start connector service + $service->execute(); + + $this->setConnectorState( + $connector, + MetadataTypes\ConnectionStateType::get(MetadataTypes\ConnectionStateType::STATE_RUNNING) + ); + } catch (Throwable $ex) { + throw new Exceptions\TerminateException('Connector can\'t be started'); + } + + if ($this->dispatcher !== null) { + $this->dispatcher->dispatch(new Events\AfterConnectorStartEvent($connector)); + } }); - $this->eventLoop->addSignal(SIGINT, function (int $signal): void { - $this->connector->terminate(); + $this->eventLoop->addSignal(SIGINT, function (int $signal) use ($connector, $service): void { + $this->logger->debug('Stopping connector...', [ + 'source' => 'devices-module', + 'type' => 'connector', + ]); + + try { + if ($this->dispatcher !== null) { + $this->dispatcher->dispatch(new Events\BeforeConnectorTerminateEvent($service)); + } + + $service->terminate(); + + $now = $this->dateTimeFactory->getNow(); + + $waitingForClosing = true; + + // Wait until connector is fully terminated + while ( + $waitingForClosing + && ( + $this->dateTimeFactory->getNow()->getTimestamp() - $now->getTimestamp() + ) < self::SHUTDOWN_WAITING_DELAY + ) { + if (!$service->hasUnfinishedTasks()) { + $waitingForClosing = false; + } + } + + if ($this->dispatcher !== null) { + $this->dispatcher->dispatch(new Events\AfterConnectorTerminateEvent($service)); + } + + $this->setConnectorState( + $connector, + MetadataTypes\ConnectionStateType::get(MetadataTypes\ConnectionStateType::STATE_STOPPED) + ); + } catch (Throwable $ex) { + $this->logger->error('Connector couldn\'t be stopped. An unexpected error occurred', [ + 'source' => 'devices-module', + 'type' => 'connector', + 'exception' => [ + 'message' => $ex->getMessage(), + 'code' => $ex->getCode(), + ], + ]); + } }); $this->eventLoop->run(); @@ -156,4 +268,59 @@ protected function execute(Input\InputInterface $input, Output\OutputInterface $ return 0; } + /** + * @param MetadataEntities\Modules\DevicesModule\IConnectorEntity $connector + * @param MetadataTypes\ConnectionStateType $state + * + * @return void + */ + private function setConnectorState( + MetadataEntities\Modules\DevicesModule\IConnectorEntity $connector, + MetadataTypes\ConnectionStateType $state + ): void { + $findProperty = new Queries\FindConnectorPropertiesQuery(); + $findProperty->byConnectorId($connector->getId()); + $findProperty->byIdentifier(MetadataTypes\ConnectorPropertyNameType::NAME_STATE); + + $property = $this->connectorPropertiesRepository->findOneBy($findProperty); + + if ($property === null) { + $property = $this->connectorPropertiesManager->create(Utils\ArrayHash::from([ + 'connector' => $connector->getId(), + 'entity' => Entities\Connectors\Properties\DynamicProperty::class, + 'identifier' => MetadataTypes\ConnectorPropertyNameType::NAME_STATE, + 'data_type' => MetadataTypes\DataTypeType::get(MetadataTypes\DataTypeType::DATA_TYPE_ENUM), + 'unit' => null, + 'format' => [ + MetadataTypes\ConnectionStateType::STATE_RUNNING, + MetadataTypes\ConnectionStateType::STATE_STOPPED, + MetadataTypes\ConnectionStateType::STATE_UNKNOWN, + MetadataTypes\ConnectionStateType::STATE_SLEEPING, + MetadataTypes\ConnectionStateType::STATE_ALERT, + ], + 'settable' => false, + 'queryable' => false, + ])); + } + + $propertyState = $this->connectorPropertiesStateRepository->findOne($property); + + if ($propertyState === null) { + $this->connectorPropertiesStateManager->create($property, Utils\ArrayHash::from([ + 'actualValue' => $state->getValue(), + 'expectedValue' => null, + 'pending' => false, + 'valid' => true, + ])); + + } else { + $this->connectorPropertiesStateManager->update($property, $propertyState, Utils\ArrayHash::from([ + 'actualValue' => $state->getValue(), + 'expectedValue' => null, + 'pending' => false, + 'valid' => true, + ])); + } + } + } diff --git a/src/Connectors/Connector.php b/src/Connectors/Connector.php deleted file mode 100644 index 84463484..00000000 --- a/src/Connectors/Connector.php +++ /dev/null @@ -1,248 +0,0 @@ - - * @package FastyBird:DevicesModule! - * @subpackage Connectors - * @since 0.60.0 - * - * @date 31.05.22 - */ - -namespace FastyBird\DevicesModule\Connectors; - -use FastyBird\DateTimeFactory; -use FastyBird\DevicesModule\Entities; -use FastyBird\DevicesModule\Events; -use FastyBird\DevicesModule\Exceptions; -use FastyBird\DevicesModule\Models; -use FastyBird\DevicesModule\Queries; -use FastyBird\Metadata\Entities as MetadataEntities; -use FastyBird\Metadata\Types\ConnectionStateType; -use FastyBird\Metadata\Types\ConnectorPropertyNameType; -use FastyBird\Metadata\Types\DataTypeType; -use Nette; -use Nette\Utils; -use Psr\EventDispatcher as PsrEventDispatcher; -use Psr\Log; -use Throwable; - -/** - * Devices connector - * - * @package FastyBird:DevicesModule! - * @subpackage Connectors - * - * @author Adam Kadlec - */ -final class Connector -{ - - use Nette\SmartObject; - - private const SHUTDOWN_WAITING_DELAY = 3; - - /** @var bool */ - private bool $stopped = true; - - /** @var ConnectorFactory */ - private ConnectorFactory $factory; - - /** @var IConnector|null */ - private ?IConnector $connector = null; - - /** @var Models\Connectors\Properties\IPropertiesRepository */ - private Models\Connectors\Properties\IPropertiesRepository $connectorPropertiesRepository; - - /** @var Models\Connectors\Properties\IPropertiesManager */ - private Models\Connectors\Properties\IPropertiesManager $connectorPropertiesManager; - - /** @var Models\States\ConnectorPropertiesRepository */ - private Models\States\ConnectorPropertiesRepository $connectorPropertiesStateRepository; - - /** @var Models\States\ConnectorPropertiesManager */ - private Models\States\ConnectorPropertiesManager $connectorPropertiesStateManager; - - /** @var DateTimeFactory\DateTimeFactory */ - private DateTimeFactory\DateTimeFactory $dateTimeFactory; - - /** @var PsrEventDispatcher\EventDispatcherInterface|null */ - private ?PsrEventDispatcher\EventDispatcherInterface $dispatcher; - - /** @var Log\LoggerInterface */ - private Log\LoggerInterface $logger; - - public function __construct( - ConnectorFactory $factory, - Models\Connectors\Properties\IPropertiesRepository $connectorPropertiesRepository, - Models\Connectors\Properties\IPropertiesManager $connectorPropertiesManager, - Models\States\ConnectorPropertiesRepository $connectorPropertiesStateRepository, - Models\States\ConnectorPropertiesManager $connectorPropertiesStateManager, - DateTimeFactory\DateTimeFactory $dateTimeFactory, - ?PsrEventDispatcher\EventDispatcherInterface $dispatcher, - ?Log\LoggerInterface $logger = null - ) { - $this->factory = $factory; - - $this->connectorPropertiesRepository = $connectorPropertiesRepository; - $this->connectorPropertiesManager = $connectorPropertiesManager; - $this->connectorPropertiesStateRepository = $connectorPropertiesStateRepository; - $this->connectorPropertiesStateManager = $connectorPropertiesStateManager; - - $this->dateTimeFactory = $dateTimeFactory; - $this->dispatcher = $dispatcher; - - $this->logger = $logger ?? new Log\NullLogger(); - } - - /** - * @param MetadataEntities\Modules\DevicesModule\IConnectorEntity $connector - * - * @return void - * - * @throws Exceptions\TerminateException - */ - public function execute(MetadataEntities\Modules\DevicesModule\IConnectorEntity $connector): void - { - if ($this->dispatcher !== null) { - $this->dispatcher->dispatch(new Events\BeforeConnectorStartEvent($connector)); - } - - $this->connector = $this->factory->create($connector); - - $this->logger->debug('Starting connector...', [ - 'source' => 'devices-module', - 'type' => 'connector', - ]); - - try { - // Start connector service - $this->connector->execute(); - - $this->stopped = false; - - $this->setConnectorState(ConnectionStateType::get(ConnectionStateType::STATE_RUNNING)); - } catch (Throwable $ex) { - throw new Exceptions\TerminateException('Connector can\'t be started'); - } - - if ($this->dispatcher !== null) { - $this->dispatcher->dispatch(new Events\AfterConnectorStartEvent($connector)); - } - } - - /** - * @return void - */ - public function terminate(): void - { - $this->stopped = true; - - $this->logger->debug('Stopping connector...', [ - 'source' => 'devices-module', - 'type' => 'connector', - ]); - - try { - if ($this->connector !== null) { - if ($this->dispatcher !== null) { - $this->dispatcher->dispatch(new Events\BeforeConnectorTerminateEvent($this->connector)); - } - - $this->connector->terminate(); - - $now = $this->dateTimeFactory->getNow(); - - $waitingForClosing = true; - - // Wait until connector is fully terminated - while ( - $waitingForClosing - && ( - $this->dateTimeFactory->getNow()->getTimestamp() - $now->getTimestamp() - ) < self::SHUTDOWN_WAITING_DELAY - ) { - if (!$this->connector->hasUnfinishedTasks()) { - $waitingForClosing = false; - } - } - - if ($this->dispatcher !== null) { - $this->dispatcher->dispatch(new Events\AfterConnectorTerminateEvent($this->connector)); - } - - $this->setConnectorState(ConnectionStateType::get(ConnectionStateType::STATE_STOPPED)); - } - } catch (Throwable $ex) { - $this->logger->error('Connector couldn\'t be stopped. An unexpected error occurred', [ - 'source' => 'devices-module', - 'type' => 'connector', - 'exception' => [ - 'message' => $ex->getMessage(), - 'code' => $ex->getCode(), - ], - ]); - } - } - - /** - * @param ConnectionStateType $state - * - * @return void - */ - private function setConnectorState(ConnectionStateType $state): void - { - if ($this->connector === null) { - return; - } - - $findProperty = new Queries\FindConnectorPropertiesQuery(); - $findProperty->byConnectorId($this->connector->getId()); - $findProperty->byIdentifier(ConnectorPropertyNameType::NAME_STATE); - - $property = $this->connectorPropertiesRepository->findOneBy($findProperty); - - if ($property === null) { - $property = $this->connectorPropertiesManager->create(Utils\ArrayHash::from([ - 'connector' => $this->connector, - 'entity' => Entities\Connectors\Properties\DynamicProperty::class, - 'identifier' => ConnectorPropertyNameType::NAME_STATE, - 'data_type' => DataTypeType::get(DataTypeType::DATA_TYPE_ENUM), - 'unit' => null, - 'format' => [ - ConnectionStateType::STATE_RUNNING, - ConnectionStateType::STATE_STOPPED, - ConnectionStateType::STATE_UNKNOWN, - ConnectionStateType::STATE_SLEEPING, - ConnectionStateType::STATE_ALERT, - ], - 'settable' => false, - 'queryable' => false, - ])); - } - - $propertyState = $this->connectorPropertiesStateRepository->findOne($property); - - if ($propertyState === null) { - $this->connectorPropertiesStateManager->create($property, Utils\ArrayHash::from([ - 'actualValue' => $state->getValue(), - 'expectedValue' => null, - 'pending' => false, - 'valid' => true, - ])); - - } else { - $this->connectorPropertiesStateManager->update($property, $propertyState, Utils\ArrayHash::from([ - 'actualValue' => $state->getValue(), - 'expectedValue' => null, - 'pending' => false, - 'valid' => true, - ])); - } - } - -} diff --git a/src/Connectors/IConnector.php b/src/Connectors/IConnector.php index 3fac7380..750609ad 100644 --- a/src/Connectors/IConnector.php +++ b/src/Connectors/IConnector.php @@ -15,8 +15,6 @@ namespace FastyBird\DevicesModule\Connectors; -use Ramsey\Uuid; - /** * Devices connector interface * @@ -28,11 +26,6 @@ interface IConnector { - /** - * @return Uuid\UuidInterface - */ - public function getId(): Uuid\UuidInterface; - /** * @return void */ diff --git a/src/DI/DevicesModuleExtension.php b/src/DI/DevicesModuleExtension.php index 2aaa0e7f..5199b6cb 100644 --- a/src/DI/DevicesModuleExtension.php +++ b/src/DI/DevicesModuleExtension.php @@ -385,9 +385,6 @@ public function loadConfiguration(): void $builder->addDefinition($this->prefix('connector.factory'), new DI\Definitions\ServiceDefinition()) ->setType(Connectors\ConnectorFactory::class); - $builder->addDefinition($this->prefix('connector.service'), new DI\Definitions\ServiceDefinition()) - ->setType(Connectors\Connector::class); - // Consumers $builder->addDefinition($this->prefix('consumer.exchange'), new DI\Definitions\ServiceDefinition()) ->setType(Consumers\DataExchangeConsumer::class); @@ -430,28 +427,6 @@ public function beforeCompile(): void if ($routerService instanceof DI\Definitions\ServiceDefinition) { $routerService->addSetup('?->registerRoutes(?)', [$builder->getDefinitionByType(Router\Routes::class), $routerService]); } - - /** - * Connectors - */ - - $connectorServiceService = $builder->getDefinitionByType(Connectors\Connector::class); - - if ($connectorServiceService instanceof DI\Definitions\ServiceDefinition) { - $connectorsServices = $builder->findByType(Connectors\IConnector::class); - - foreach ($connectorsServices as $connectorsService) { - if ($connectorsService->getType() !== Connectors\Connector::class) { - // Connector is not allowed to be autowired - $connectorsService->setAutowired(false); - - $connectorServiceService->addSetup('?->registerConnector(?)', [ - '@self', - $connectorsService, - ]); - } - } - } } /** diff --git a/src/DataStorage/Writer.php b/src/DataStorage/Writer.php index 8c22123f..1cb0fd42 100644 --- a/src/DataStorage/Writer.php +++ b/src/DataStorage/Writer.php @@ -66,10 +66,8 @@ public function write(): void $findConnectors = new Queries\FindConnectorsQuery(); $connectors = $this->connectorsRepository->findAllBy($findConnectors); - var_dump(count($connectors)); foreach ($connectors as $connector) { - var_dump($connector->getIdentifier()); $devices = []; foreach ($connector->getDevices() as $device) { diff --git a/tests/cases/Unit/Connectors/ConnectorFactoryTest.phpt b/tests/cases/Unit/Connectors/ConnectorFactoryTest.phpt index 84373037..ad02165b 100644 --- a/tests/cases/Unit/Connectors/ConnectorFactoryTest.phpt +++ b/tests/cases/Unit/Connectors/ConnectorFactoryTest.phpt @@ -38,10 +38,6 @@ final class ConnectorFactoryTest extends DbTestCase $writer->write(); $reader->read(); - foreach ($connectorsRepository as $connector) { - var_dump($connector); - } - $connectorEntity = $connectorsRepository->findById(Uuid\Uuid::fromString('7a3dd94c-7294-46fd-8c61-1b375c313d4d')); Assert::notNull($connectorEntity);