123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 |
- <?php
- /**
- * ONVIF-explorer and custom device templates manager implementation
- */
- class ModelCraft {
- /**
- * ONVIF layer placeholder
- *
- * @var object
- */
- protected $onvif = '';
- /**
- * System messages helper instance placeholder
- *
- * @var object
- */
- protected $messages = '';
- /**
- * Custom device templates database abstraction layer
- *
- * @var object
- */
- protected $custTemplatesDb = '';
- /**
- * Contains all custom device templates data as id=>custtpldata
- *
- * @var array
- */
- protected $allCustTemplatesData = array();
- /**
- * some predefined stuff here
- */
- const TABLE_TEMPLATES = 'custtpls';
- const URL_ME = '?module=modelcraft';
- const ROUTE_EXPLORER = 'onvifexplorer';
- const PROUTE_EXPLORE = 'explorecamera';
- const PROUTE_EXPLORE_IP = 'exploredevip';
- const PROUTE_EXPLORE_LOGIN = 'exploredevlogin';
- const PROUTE_EXPLORE_PASSWORD = 'exploredevpassword';
- const PROUTE_TPLCREATE_DEV = 'customtemplatedevicename';
- const PROUTE_TPLCREATE_PROTO = 'customtemplateproto';
- const PROUTE_TPLCREATE_MAIN = 'customtemplatemainstream';
- const PROUTE_TPLCREATE_SUB = 'customtemplatesubstream';
- const PROUTE_TPLCREATE_PORT = 'customtemplateport';
- const PROUTE_TPLCREATE_SOUND = 'customtemplatesound';
- const PROUTE_TPLCREATE_PTZ = 'customtemplateptz';
- const ROUTE_TPL_DEL = 'deletecusttpl';
- const CUSTOM_TEMPLATE_MARK = '_CUSTOM';
- /**
- * Spirits gathering around my dead coil
- * Infernal baptism, hungry for blood
- */
- public function __construct() {
- $this->initMessages();
- $this->initTemplatesDb();
- $this->loadCustomTemplates();
- }
- /**
- * Inits system messages helper
- *
- * @return void
- */
- protected function initMessages() {
- $this->messages = new UbillingMessageHelper();
- }
- /**
- * Initializes the Onvif object.
- *
- * This method creates a new instance of the Ponvif class and assigns it to the $onvif property.
- * The Ponvif class is responsible for handling Onvif-related operations.
- *
- * @return void
- */
- protected function initOnvif() {
- $this->onvif = new Ponvif();
- }
- /**
- * Inits templates database abstraction layer
- *
- * @return void
- */
- protected function initTemplatesDb() {
- $this->custTemplatesDb = new NyanORM(self::TABLE_TEMPLATES);
- }
- /**
- * Loads all existing custom templates data from database
- *
- * @return void
- */
- protected function loadCustomTemplates() {
- $this->allCustTemplatesData = $this->custTemplatesDb->getAll('id');
- }
- /**
- * Polls the ONVIF capable device for information and retrieves the available sources and their stream URLs.
- *
- * @param string $ip The IP address of the camera.
- * @param string $login The login username for accessing the camera.
- * @param string $password The login password for accessing the camera.
- *
- * @return array
- */
- public function pollDevice($ip, $login, $password) {
- $result = array();
- if (zb_PingICMP($ip)) {
- $this->initOnvif();
- $this->onvif->setIPAddress($ip);
- $this->onvif->setUsername($login);
- $this->onvif->setPassword($password);
- try {
- $this->onvif->initialize();
- $sources = $this->onvif->getSources();
- if (!empty($sources)) {
- $result['sources'] = $sources[0];
- foreach ($sources[0] as $io => $each) {
- if (isset($each['profiletoken'])) {
- $streamUri = $this->onvif->media_GetStreamUri($each['profiletoken']);
- $result['sources'][$io]['url'] = $streamUri;
- if (!empty($streamUri)) {
- $parsedUrl = parse_url($streamUri);
- if (!empty($parsedUrl)) {
- $result['sources'][$io]['urldata'] = $parsedUrl;
- if (!isset($result['sources'][$io]['urldata']['query'])) {
- $result['sources'][$io]['urldata']['query'] = '';
- }
- }
- }
- } else {
- unset($result['sources'][$io]);
- }
- }
- }
- } catch (Exception $e) {
- //nothing here right now
- }
- }
- return ($result);
- }
- /**
- * Renders the ONVIF device explore form.
- *
- * This method generates the HTML code for an explore form, which includes input fields for IP, login, password, and a submit button.
- *
- * @return string
- */
- public function renderExploreForm() {
- $result = '';
- $inputs = wf_HiddenInput(self::PROUTE_EXPLORE, 'true');
- $inputs .= wf_TextInput(self::PROUTE_EXPLORE_IP, __('IP'), ubRouting::post(self::PROUTE_EXPLORE_IP), false, 16, 'ip') . ' ';
- $inputs .= wf_TextInput(self::PROUTE_EXPLORE_LOGIN, __('Login'), ubRouting::post(self::PROUTE_EXPLORE_LOGIN), false, 12, '') . ' ';
- $inputs .= wf_PasswordInput(self::PROUTE_EXPLORE_PASSWORD, __('Password'), ubRouting::post(self::PROUTE_EXPLORE_PASSWORD), false, 12, true) . ' ';
- $inputs .= wf_Submit(__('Explore'));
- $result .= wf_Form('', 'POST', $inputs, 'glamour');
- return ($result);
- }
- public function renderTemplateCreationForm($deviceData) {
- $result = '';
- $availableSources = array();
- $port = 554;
- $proto = 'rtsp';
- $sound = 0;
- $ptz = 0;
- if (!empty($deviceData)) {
- if (isset($deviceData['sources'])) {
- $preselMain='';
- $preselSub='';
- foreach ($deviceData['sources'] as $io => $each) {
- if (isset($each['profilename']) and isset($each['urldata'])) {
- $profileLabel = $each['profilename'];
- $shortUrl = $each['urldata']['path'];
- if (!empty($each['urldata']['query'])) {
- $shortUrl .= '?' . $each['urldata']['query'];
- }
- $port = $each['urldata']['port'];
- $proto = $each['urldata']['scheme'];
- if (empty($availableSources)) {
- $preselMain=$shortUrl; //first of available sources
- } else {
- $preselSub=$shortUrl; //last source
- }
- $availableSources[$shortUrl] = $profileLabel . ' (' . $each['width'] . 'x' . $each['height'] . ')';
- }
- }
-
- //some form here
- $inputs = wf_TextInput(self::PROUTE_TPLCREATE_DEV, __('Template name'), '', true, 20, '');
- $inputs .= wf_Selector(self::PROUTE_TPLCREATE_MAIN, $availableSources, __('Mainstream'), $preselMain, true);
- $inputs .= wf_Selector(self::PROUTE_TPLCREATE_SUB, $availableSources, __('Substream'), $preselSub, true);
- $inputs .= wf_CheckInput(self::PROUTE_TPLCREATE_SOUND, __('Sound'), true, false);
- $inputs .= wf_CheckInput(self::PROUTE_TPLCREATE_PTZ, __('PTZ'), true, false);
- $inputs .= wf_HiddenInput(self::PROUTE_TPLCREATE_PORT, $port);
- $inputs .= wf_HiddenInput(self::PROUTE_TPLCREATE_PROTO, $proto);
- $inputs .= wf_Submit(__('Create'));
- $result .= wf_Form('', 'POST', $inputs, 'glamour');
- } else {
- $result .= $this->messages->getStyledMessage(__('Device media sources is empty'), 'error');
- }
- } else {
- $result .= $this->messages->getStyledMessage(__('Device polling data is empty'), 'error');
- }
- return ($result);
- }
- /**
- * Renders polled device data with few controls
- *
- * @param array $deviceData
- *
- * @return string
- */
- public function renderPollingResults($deviceData) {
- $result = '';
- if (!empty($deviceData)) {
- if (isset($deviceData['sources'])) {
- $cells = wf_TableCell(__('Profile name'));
- $cells .= wf_TableCell(__('Token'));
- $cells .= wf_TableCell(__('Encoding'));
- $cells .= wf_TableCell(__('Resolution'));
- $cells .= wf_TableCell(__('MP'));
- $cells .= wf_TableCell(__('FPS'));
- $cells .= wf_TableCell(__('Bitrate'));
- $cells .= wf_TableCell(__('Protocol'));
- $cells .= wf_TableCell(__('Port'));
- $cells .= wf_TableCell(__('URL'));
- $rows = wf_TableRow($cells, 'row1');
- foreach ($deviceData['sources'] as $io => $each) {
- $cells = wf_TableCell($each['profilename']);
- $cells .= wf_TableCell($each['profiletoken']);
- $cells .= wf_TableCell($each['encoding']);
- $pix = $each['width'] * $each['height'];
- $mpix = round($pix / 1000000, 1);
- $cells .= wf_TableCell($each['width'] . 'x' . $each['height'], '', '', 'sorttable_customkey="' . $pix . '"');
- $cells .= wf_TableCell($mpix);
- $cells .= wf_TableCell($each['fps']);
- $cells .= wf_TableCell($each['bitrate'] . ' ' . __('Kbit/s'));
- $cells .= wf_TableCell($each['urldata']['scheme']);
- $cells .= wf_TableCell($each['urldata']['port']);
- $shortUrl = $each['urldata']['path'];
- if (!empty($each['urldata']['query'])) {
- $shortUrl .= '?' . $each['urldata']['query'];
- }
- $cells .= wf_TableCell($shortUrl);
- $rows .= wf_TableRow($cells, 'row5');
- }
- $result .= wf_TableBody($rows, '100%', 0, 'sortable resp-table');
- $result .= wf_delimiter();
- $rawData = wf_tag('pre') . print_r($deviceData, true) . wf_tag('pre', true);
- $result .= wf_modal(wf_img('skins/brain.png') . ' ' . __('Device inside'), __('Device inside'), $rawData, 'ubButton', '900', '600');
- $result .= wf_modalAuto(web_icon_create() . ' ' . __('Create device model template'), __('Create device model template'), $this->renderTemplateCreationForm($deviceData), 'ubButton');
- } else {
- $result .= $this->messages->getStyledMessage(__('Device media sources is empty'), 'error');
- }
- } else {
- $result .= $this->messages->getStyledMessage(__('Device polling data is empty'), 'error');
- }
- return ($result);
- }
- /**
- * Creates a custom device template database record with the given parameters.
- *
- * @param string $name The name of the custom template.
- * @param string $proto The protocol of the custom template.
- * @param string $main The main parameter of the custom template.
- * @param string $sub The sub parameter of the custom template.
- * @param int $rtspport The RTSP port of the custom template.
- * @param int $sound The sound parameter of the custom template. Default is 0.
- * @param int $ptz The PTZ parameter of the custom template. Default is 0.
- *
- * @return void
- */
- public function createCustomTemplate($name, $proto, $main, $sub, $rtspport, $sound = 0, $ptz = 0) {
- $nameF = ubRouting::filters($name, 'mres');
- $protoF = ubRouting::filters($proto, 'mres');
- $mainF = ubRouting::filters($main, 'mres');
- $subF = ubRouting::filters($sub, 'mres');
- $rtspport = ubRouting::filters($rtspport, 'int');
- $sound = ($sound) ? 1 : 0;
- $ptz = ($ptz) ? 1 : 0;
- $this->custTemplatesDb->data('name', $nameF);
- $this->custTemplatesDb->data('proto', $protoF);
- $this->custTemplatesDb->data('main', $mainF);
- $this->custTemplatesDb->data('sub', $subF);
- $this->custTemplatesDb->data('rtspport', $rtspport);
- $this->custTemplatesDb->data('sound', $sound);
- $this->custTemplatesDb->data('ptz', $ptz);
- $this->custTemplatesDb->create();
- $newId = $this->custTemplatesDb->getLastId();
- log_register('MODELCRAFT CREATE [' . $newId . '] NAME `' . $name . '`');
- //reload data from database
- $this->loadCustomTemplates();
- //generate new template
- $this->generateCustomTemplateFile($newId);
- }
- /**
- * Renders the module controls
- *
- * @return string
- */
- public function renderControls() {
- $result = '';
- if (!ubRouting::checkGet(self::ROUTE_EXPLORER)) {
- $result .= wf_BackLink(Models::URL_ME);
- $result .= wf_Link(self::URL_ME . '&' . self::ROUTE_EXPLORER . '=true', web_icon_search() . ' ' . __('ONVIF device explorer'), false, 'ubButton');
- } else {
- $result .= wf_BackLink(self::URL_ME);
- }
- return ($result);
- }
- /**
- * Deletes a custom template by its ID.
- *
- * @param int $templateId The ID of the template to be deleted.
- *
- * @return void|string
- */
- public function deleteCustomTemplate($templateId) {
- $result = '';
- $templateId = ubRouting::filters($templateId, 'int');
- if (isset($this->allCustTemplatesData[$templateId])) {
- $templateName = $templateId . self::CUSTOM_TEMPLATE_MARK;
- $templateLabel = $this->allCustTemplatesData[$templateId]['name'];
- $models = new Models();
- if (!$models->isTemplateProtected($templateName)) {
- $this->custTemplatesDb->where('id', '=', $templateId);
- $this->custTemplatesDb->delete();
- $this->destroyCustomTemplateFile($templateName);
- log_register('MODELCRAFT DELETE [' . $templateId . '] NAME `' . $templateLabel . '`');
- } else {
- $result .= __('Something went wrong') . ': ' . __('Template now is used');
- }
- } else {
- $result .= __('Something went wrong') . ': ' . __('Template') . ' [' . $templateId . '] ' . __('not exists');
- }
- return ($result);
- }
- /**
- * Renders custom device templates list
- *
- * @return void
- */
- public function renderCustomTemplatesList() {
- $result = '';
- if (!empty($this->allCustTemplatesData)) {
- $cells = wf_TableCell(__('ID'));
- $cells .= wf_TableCell(__('Name'));
- $cells .= wf_TableCell(__('Actions'));
- $rows = wf_TableRow($cells, 'row1');
- foreach ($this->allCustTemplatesData as $io => $each) {
- $cells = wf_TableCell($each['id']);
- $cells .= wf_TableCell($each['name']);
- $delUrl = self::URL_ME . '&' . self::ROUTE_TPL_DEL . '=' . $each['id'];
- $actLinks = wf_JSAlert($delUrl, web_delete_icon(), $this->messages->getDeleteAlert());
- $cells .= wf_TableCell($actLinks);
- $rows .= wf_TableRow($cells, 'row5');
- }
- $result .= wf_TableBody($rows, '100%', 0, 'sortable resp-table');
- //sync templates on render
- $this->syncCustomTemplates();
- } else {
- $result .= $this->messages->getStyledMessage(__('Nothing to show'), 'warning');
- }
- return ($result);
- }
- /**
- * Syncs the custom templates on FS with existing in database
- *
- * @return void
- */
- public function syncCustomTemplates() {
- $customTemplates = array();
- $customTemplatesRaw = rcms_scandir(Models::CUSTOM_TEMPLATES_PATH);
- if (!empty($customTemplatesRaw)) {
- foreach ($customTemplatesRaw as $io => $eachTemplate) {
- $templateData = rcms_parse_ini_file(Models::CUSTOM_TEMPLATES_PATH . $eachTemplate);
- if (is_array($templateData)) {
- $customTemplates[$eachTemplate] = $templateData;
- }
- }
- }
- //may be some templates is missing?
- if (!empty($this->allCustTemplatesData)) {
- foreach ($this->allCustTemplatesData as $io => $each) {
- $templateName = $each['id'] . self::CUSTOM_TEMPLATE_MARK;
- //sync is required
- if (!isset($customTemplates[$templateName])) {
- $this->generateCustomTemplateFile($each['id']);
- log_register('MODELCRAFT SYNC [' . $each['id'] . '] TEMPLATE `' . $templateName . '`');
- }
- }
- }
- }
- /**
- * Deletes a custom template from filesystem
- *
- * @param string $templateName The name of the template file to be deleted.
- * @return void
- */
- protected function destroyCustomTemplateFile($templateName) {
- if (!empty($templateName)) {
- $fsPath = Models::CUSTOM_TEMPLATES_PATH . $templateName;
- if (file_exists($fsPath)) {
- unlink($fsPath);
- log_register('MODELCRAFT DESTROY TEMPLATE `' . $templateName . '`');
- }
- }
- }
- /**
- * Generates a custom device template based on the provided template ID.
- *
- * @param int $templateId The ID of the template.
- *
- * @return void
- */
- protected function generateCustomTemplateFile($templateId) {
- $templateId = ubRouting::filters($templateId, 'int');
- if (isset($this->allCustTemplatesData[$templateId])) {
- $templateName = $templateId . self::CUSTOM_TEMPLATE_MARK;
- $fsPath = Models::CUSTOM_TEMPLATES_PATH . $templateName;
- $devData = $this->allCustTemplatesData[$templateId];
- $templateBody = 'DEVICE="' . $devData['name'] . '"' . PHP_EOL;
- $templateBody .= 'PROTO="' . $devData['proto'] . '"' . PHP_EOL;
- $templateBody .= 'MAIN_STREAM="' . $devData['main'] . '"' . PHP_EOL;
- $templateBody .= 'SUB_STREAM="' . $devData['sub'] . '"' . PHP_EOL;
- $templateBody .= 'RTSP_PORT=' . $devData['rtspport'] . '' . PHP_EOL;
- $templateBody .= 'HTTP_PORT=80' . PHP_EOL;
- $templateBody .= 'SOUND=' . $devData['sound'] . '' . PHP_EOL;
- $templateBody .= 'PTZ=' . $devData['ptz'] . '' . PHP_EOL;
- file_put_contents($fsPath, $templateBody);
- chmod($fsPath, 0777);
- log_register('MODELCRAFT GENERATE [' . $templateId . '] TEMPLATE `' . $templateName . '`');
- }
- }
- }
|