<?php
$__functions['init'][]='_pp_site_vars' ;
$__functions['boot_site'][]='_pp_site_boot' ;

function PP() {return($_SESSION['pp_system']);$_SESSION['pp_system']=new c_pp_system();return($_SESSION['pp_system']);} ;


function _pp_site_vars()
{
    // роли модуля

    $_SESSION['init_options']['pp']['debug']=0 ;
}

function _pp_site_boot($options)
{
    create_system_modul_obj('pp',$options) ;

}


class c_pp_system
{
    public $table_bill ;
    public $pp_list ;
    private $table_name='obj_site_narko_data' ;
    private $clss_pp=300 ;

    var $pp_result_code=array(
        'norm'=>1,
        'pupils_narrowed'=>2,
        'pupils_dilated'=>4,
        'no_reaction'=>8,
        'weak_reaction'=>16,
        'insuff_recognition'=>32

    );

    var $pp_result_name=array(
        1=>'Норма',
        2=>'Зрачки сужены',
        4=>'Зрачки расширены (мидриаз)',
        8=>'Реакция отсутствует',
        16=>'Реакция ослаблена',
        32=>'Недостаточный процент распознания'
    );

    var $pp_age_group=array(
        '1'=>array('name'=>'До 20','max'=>20),
        '2'=>array('name'=>'От 21 до 30','min'=>21,'max'=>30),
        '3'=>array('name'=>'От 31 до 40','min'=>31,'max'=>40),
        '4'=>array('name'=>'От 41 до 50','min'=>41,'max'=>50),
        '5'=>array('name'=>'От 51 до 60','min'=>51,'max'=>60),
        '6'=>array('name'=>'От 61','min'=>61),
    ) ;

    function __construct($create_options=array())
    {

        ENGINE()->reg_ext('aspmo_pp','Пупиллометрия') ;
    }

    function declare_menu_items_2($page)
    { // только для клиента

        $page->menu['/cab/pp/']=array('title'=>'Пупиллометрия',
            'class'=>'fa fa-eye',
            'items'=>array('/cab/pp/'=>'Пупиллометрия',
                '/cab/pp/journal/'=>array('title'=>'Журнал измерений','class'=>'fa fa-users')
            )
        ) ;


    }

    function declare_menu_items_4($page)
    {
        $page->menu['/cab/setting/']['items']['/cab/esmo_setting/mo/']['items']['/cab/esmo_setting/pp/']='Пупиллометрия' ;
    }


    function get_PP_by_id($id,$options=array())
    { $rec=array() ;
        $debug=$options['debug'] ;
        if ($id)
        { $rec=execSQL_van('select * from '.$this->table_name.' where pkey='.$id) ;
            $rec['leftEye']=$this->get_pp_value($rec['pkey'],'left');
            $rec['rightEye']=$this->get_pp_value($rec['pkey'],'right');
            if ($rec['pkey']) $this->prepare_public_info_to_PP($rec) ;
        }
        return($rec) ;
    }

    function get_pp_id_by_mo_id($mo_id,$options=array())
    {
        $debug=$options['debug'] ;
        if ($mo_id){
            $pp_id=execSQL_value('select pkey from '.$this->table_name.' where id_mo='.$mo_id) ;
        }
        return($pp_id);
    }

    function prepare_public_info_to_PP(&$rec,$options=array())
    {  $rec['__href']='/cab/narko/pp/'.$rec['pkey'].'/' ;

    }

    function get_TO_recs()
    {
        $recs=execSQL('select * from '.$this->table_name.' where clss=360 order by pkey desc') ;
        if (_sizeof($recs)) foreach($recs as $i=>$rec) $this->prepare_public_info_to_TO($recs[$i]) ;
        return($recs) ;
    }

    /*
     * старые файлы по ПП имеют наименование типа: pp_PPID_left.data
     * в новых файлах префикс pp отсутствует для удобства использования функции получения наименования файла
     *      функция ENGINE()->get_dir_by_id не дает возможности добавить префикс перед PPID
     * так же новые файлы зипуются
     * соответственно новые файлы имеют наименование типа: PPID_left.data.gz
     * */
    function create_pp_rec($mo_id,$personal_id,$left,$right,$lux,$proc1,$proc2)
    {  // не сохраняем в базу значенния ПП, только в файлы
        $ts=getdate() ;
        $mo_time=60*$ts['hours']+$ts['minutes'];
        $pp_id=adding_rec_to_table('obj_site_narko_data',array('clss'=>300,'id_mo'=>$mo_id,'personal_id'=>$personal_id,'lux'=>$lux,'proc1'=>$proc1,'proc2'=>$proc2,'mo_time'=>$mo_time),array('no_auto_indx'=>1)) ;
        // сохраняем данные по $pp_id, так все обращения к данным происходят через ID записи по ПП
        $base_dir = _DIR_TO_ROOT.'/public/mo_pp/';
        $dir_name = ENGINE()->get_number_dir_by_id($pp_id,$base_dir);
        $this->gzip_file($dir_name,$pp_id.'_left.data.gz',$left);
        $this->gzip_file($dir_name,$pp_id.'_right.data.gz',$right);
        return($pp_id) ;
    }

    function gzip_file($dir_name,$file_name,$data)
    {
        $gz = gzopen($dir_name.$file_name,'w9');
        gzwrite($gz, $data);
        gzclose($gz);
    }

    function update_pp_rec($pp_id,$mo_id,$personal_id,$left,$right,$lux,$proc1,$proc2)
    {
        $ts=getdate() ;
        $mo_time=60*$ts['hours']+$ts['minutes'];
        update_rec_in_table('obj_site_narko_data',array('clss'=>300,'id_mo'=>$mo_id,'personal_id'=>$personal_id,'lux'=>$lux,'proc1'=>$proc1,'proc2'=>$proc2,'mo_time'=>$mo_time),'pkey='.$pp_id) ;
        $base_dir = _DIR_TO_ROOT.'/public/mo_pp/';
        $dir_name = ENGINE()->get_number_dir_by_id($pp_id,$base_dir);
        if (file_exists($dir_name.$pp_id.'_left.data.gz') and file_exists($dir_name.$pp_id.'_right.data.gz')) {
            $this->gzip_file($dir_name,$pp_id.'_left.data.gz',$left) ;
            $this->gzip_file($dir_name,$pp_id.'_right.data.gz',$right) ;
        } else {
            file_put_contents(_DIR_TO_ROOT.'/public/mo_pp/pp_'.$pp_id.'_left.data',$left) ;
            file_put_contents(_DIR_TO_ROOT.'/public/mo_pp/pp_'.$pp_id.'_right.data',$right) ;
        }
        return($pp_id) ;
    }

    function get_pp_value($pp_id,$mode)
    {
        $options=array('check_directory'=>1,'no_check_exist_file'=>1,'base_dir'=>'/public/mo_pp/') ;
        $dir_name = ENGINE()->get_dir_by_id($pp_id,'dir','_'.$mode.'.data.gz',$options);
        if (file_exists($dir_name)) {
            $file_arr=gzfile($dir_name) ;
            $cont=implode(' ',$file_arr) ;
        } else {
            $fname=_DIR_TO_ROOT.'/public/mo_pp/pp_'.$pp_id.'_'.$mode.'.data' ;
            if (file_exists($fname)) $cont=file_get_contents($fname) ;
            else $cont='' ;
        }
        return($cont) ;

    }

    function increase_pp_cnt($member_id) //увеличение кол-ва ПП сотрудника
    {
        $cnt_pp=execSQL_value('select cnt_pp from '.PERSONAL()->table_personal.' where pkey='.$member_id);
        $cnt_pp++;
        update_rec_in_table(PERSONAL()->table_personal,array('cnt_pp'=>$cnt_pp),'pkey='.$member_id) ;
    }

    function check_narko_by_data($rec, $norms,$options=array())  //функция, вызываемая сторонним скриптом и выносящая вердикт
    {
        $analyzerLeft=new Analyzer();
        $leftEye=($rec['leftEye'])? $rec['leftEye']:$this->get_pp_value($rec['pkey'],'left') ;
        $analyzerLeft->stringToArrays($leftEye,'left');
        $analyzerRight=new Analyzer();
        $rightEye=($rec['rightEye'])? $rec['rightEye']:$this->get_pp_value($rec['pkey'],'right') ;
        $analyzerRight->stringToArrays($rightEye,'right');

        $result=""; $arr_res=array() ;  $code=1 ; $result_code=0; $result_code_arr=[];

        if((!($analyzerLeft->getQuality()))&&(!($analyzerRight->getQuality())))  { $result="Недостаточный процент распознания"; $result_code_arr[$this->pp_result_code['insuff_recognition']]=1; $code=2 ; }//.$analyzerLeft->percent.$analyzerRight->percent;
        else if ($norms['pkey'] or !$options['no_check_outer_norm'])
        {
            if($analyzerLeft->percent>$analyzerRight->percent) $analyzer=$analyzerLeft;
            else                                               $analyzer=$analyzerRight;

            if (!$norms['pkey'])
            {   $use_norms["Tlat1Min"] = $GLOBALS['LS_pp_Tlat1Min'];
                $use_norms["Tlat1Max"] = $GLOBALS['LS_pp_Tlat1Max'];
                $use_norms["Tlat2Min"] = $GLOBALS['LS_pp_Tlat2Min'];
                $use_norms["Tlat2Max"] = $GLOBALS['LS_pp_Tlat2Max'];
                $use_norms["Angle1Min"] = $GLOBALS['LS_pp_Angle1Min'];
                $use_norms["Angle1Max"] = $GLOBALS['LS_pp_Angle1Max'];
                $use_norms["Angle2Min"] = $GLOBALS['LS_pp_Angle2Min'];
                $use_norms["Angle2Max"] = $GLOBALS['LS_pp_Angle2Max'];
                $use_norms["AmplitudeMin"] = $GLOBALS['LS_pp_AmplitudeMin'];
                $use_norms["AmplitudeMax"] = $GLOBALS['LS_pp_AmplitudeMax'];
                $use_norms["AverageMin"]=$GLOBALS['LS_pp_AverageMin'];
                $use_norms["AverageMax"]=$GLOBALS['LS_pp_AverageMax'];
            } else $use_norms=$norms ;


            /*if ($analyzer->getTlatFirst()<($use_norms["Tlat1Min"])) $result=$result."Ускоренная реакция зрачка; ";*/
            /*if (($analyzer->getTlatSecond()>($use_norms["Tlat2Max"]))||($analyzer->getTlatSecond()<($use_norms["Tlat2Min"]))) $result=$result."Замедленная реакция зрачка на выключение света; ";*/
            if ($analyzer->getAmplitudeRatio()<($use_norms["AmplitudeMin"]))
            {
                if ($analyzer->getAverage()<$use_norms["AverageMin"]) {$arr_res['getAverage']='Зрачки сужены (миоз)' ; $result_code_arr[$this->pp_result_code['pupils_narrowed']]=1;}
                elseif ($analyzer->getAverage()>$use_norms["AverageMax"])
                {   $arr_res['getAverage']='Зрачки расширены' ;
                    $result_code_arr[$this->pp_result_code['pupils_dilated']]=1;
                    if($analyzer->getAmplitudeRatio()<1.1)  {$arr_res['getAmplitudeRatio']='реакция отсутствует' ; $result_code_arr[$this->pp_result_code['no_reaction']]=1;} //$result = $result . "Зрачки расширены, реакция отсутствует; ";
                    else                                    {$arr_res['getAmplitudeRatio']='реакция ослаблена' ; $result_code_arr[$this->pp_result_code['weak_reaction']]=1;}//$result = $result . "Зрачки расширены, реакция ослаблена; ";
                }
                else
                {
                    if($analyzer->getAmplitudeRatio()<1.1)  {$arr_res['getAmplitudeRatio']='реакция отсутствует' ; $result_code_arr[$this->pp_result_code['no_reaction']]=1;}//$result = $result . "реакция отсутствует; ";
                    else                                    {$arr_res['getAmplitudeRatio']='реакция ослаблена' ; $result_code_arr[$this->pp_result_code['weak_reaction']]=1;} //$result = $result . "реакция ослаблена; ";
                }

            }

            //if ($analyzer->getAmplitudeRatio()>($use_norms["AmplitudeMax"])) {   $arr_res['getAmplitudeRatio']='реакция повышена'  ; }

            if ($analyzer->getAverage()>$use_norms["AverageMax"])       {$arr_res['getAverage']='Зрачки расширены (мидриаз)' ; $result_code_arr[$this->pp_result_code['pupils_dilated']]=1;} //$result=$result."Зрачки расширены (мидриаз); ";
            elseif ($analyzer->getAverage()<$use_norms["AverageMin"])   {$arr_res['getAverage']='Зрачки сужены' ; $result_code_arr[$this->pp_result_code['pupils_narrowed']]=1;} // $result=$result."Зрачки сужены, реакция повышена; ";


            //if ($analyzer->getTlatFirst()>($use_norms["Tlat1Max"]))         $result=$result."Время реакции выше нормы; ";
            //if ($analyzer->getFirstAngleTan()<($use_norms["Angle1Min"]))    $result=$result."Малая скорость реакции на включение света; ";
            //if ($analyzer->getFirstAngleTan()>($use_norms["Angle1Max"]))    $result=$result."Повышенная скорость реакции на включение света; ";
            /*if (($analyzer->getSecondAngleTan()>($use_norms["Angle2Max"]))||($analyzer->getSecondAngleTan()<($use_norms["Angle2Min"]))) $result=$result."Малая скорость реакции на выключение света; ";*/

            //if ($analyzer->)
            $result=(_sizeof($arr_res))? implode(', ',$arr_res):'' ; ;
            if (_sizeof($arr_res)) $code=3 ;

            if ($options['debug'])
            {   $analyzer->debug['personal_id']=$rec['personal_id'] ;
                $analyzer->debug['left']['FPS']=$analyzerLeft->FPS ;
                $analyzer->debug['left']['percent']=$analyzerLeft->percent ;
                $analyzer->debug['left']['Eye']=$rec['leftEye'] ;
                $analyzer->debug['right']['FPS']=$analyzerRight->FPS ;
                $analyzer->debug['right']['percent']=$analyzerRight->percent ;
                $analyzer->debug['right']['Eye']=$rec['rightEye'] ;
                $analyzer->debug['norms']=$norms ; // нажо передать именно норму сотрудника
                $analyzer->debug['arr_res']=$arr_res ;
                $analyzer->debug['result']=$result ;
                return($analyzer) ;
            }
        }
        $res_db=(!$result)? 'ok':$result ;
        if (!_sizeof($result_code_arr)) $result_code=1;
        else {
            foreach ($result_code_arr as $key=>$value) {
                $result_code+=$key;
            }
        }
        update_rec_in_table('obj_site_narko_data',array('result'=>$res_db,'result_code'=>$result_code),'pkey='.$rec['pkey']) ;
        // по аналнии с давлением, температурой и алкоголем возвращаем id результата, для более удобной обработки в отчетах
        // внимание!! изменяется формат возвращаемого функцией значекния, вместо строки результата - массив(результат, код результата)
        // соответствененные правки сделаны во всех местах вызова функции  check_narko
        // code: 1 - тест пройден
        //       2 - Недостаточный процент распознания
        //       3 - Тест не пройден
        return(array($result,$code));
    }

    function check_narko($pp_id,$options=array())  //функция, вызываемая сторонним скриптом и выносящая вердикт
    {   $rec=execSQL_van("select pkey,leftEye,rightEye,personal_id from obj_site_narko_data  where pkey='".$pp_id."'");
        $norms=execSQL_van("select * from obj_site_narko_data_out_303 where user_id=".$rec['personal_id']);
        // если данных по ПП нет в базе, значит они в файле на сервере
        if (!$rec['leftEye']) $rec['leftEye']=$this->get_pp_value($rec['pkey'],'left') ;
        if (!$rec['rightEye']) $rec['rightEye']=$this->get_pp_value($rec['pkey'],'right') ;

        return $this->check_narko_by_data($rec, $norms, $options);
    }


    // считаем норму на основании записей $pp_ids для $personal_id
    // переделаено, считается только одна запись
    function calculate_norm($pp_ids,$personal_id, $auto = false)
    {
        $recs=execSQL('select * from '.$this->table_name.' where enabled=1 and pkey in ('.$pp_ids.')');
        if (_sizeof($recs)) foreach ($recs as &$rec){
            $rec['leftEye']=$this->get_pp_value($rec['pkey'],'left');
            $rec['rightEye']=$this->get_pp_value($rec['pkey'],'right');
        }
        $norms=execSQL_van("select * from obj_site_narko_data_out_303 where user_id=".$personal_id);
        $count=0; $TLatFirst=array() ;   $TLatSecond=array() ;  $amplitudeRatio=array() ; $firstAngleTan=array() ;  $secondAngleTan=array() ; $average=array() ;

        foreach($recs as $rec)
        {   $analyzerLeft=new Analyzer();
            $leftEye=($rec['leftEye'])? $rec['leftEye']:$this->get_pp_value($rec['pkey'],'left') ;
            $analyzerLeft->stringToArrays($leftEye);
            $analyzerRight=new Analyzer();
            $rightEye=($rec['rightEye'])? $rec['rightEye']:$this->get_pp_value($rec['pkey'],'right') ;
            $analyzerRight->stringToArrays($rightEye);
            if($analyzerLeft->percent>$analyzerRight->percent)  $analyzer=$analyzerLeft;
            else                                                $analyzer=$analyzerRight;
            $TLatFirst[$count]=$analyzer->getTlatFirst();
            $TLatSecond[$count]=$analyzer->getTlatSecond();
            $amplitudeRatio[$count]=$analyzer->getAmplitudeRatio();
            $firstAngleTan[$count]=$analyzer->getFirstAngleTan();
            $secondAngleTan[$count]=$analyzer->getSecondAngleTan();
            $average[$count]=$analyzer->getAverage();
            $count++;
        }

        $mistakePercent_m=($norms['koof_m'])? $norms['koof_m']/100:$GLOBALS['LS_pp_norma_koof_m']/100;
        $mistakePercent_p=($norms['koof_p'])? $norms['koof_p']/100:$GLOBALS['LS_pp_norma_koof_p']/100;

        //echo  '$mistakePercent_m='.$mistakePercent_m.'<br>' ;
        //echo  '$mistakePercent_p='.$mistakePercent_p.'<br>' ;

        $TLatFirstMax=max($TLatFirst)*(1+$mistakePercent_p);
        $TLatFirstMin=min($TLatFirst)*(1-$mistakePercent_m);
        $TLatSecondMax=max($TLatSecond)*(1+$mistakePercent_p);
        $TLatSecondMin=min($TLatSecond)*(1-$mistakePercent_m);
        $amplitudeRatioMin=min($amplitudeRatio)*(1-$mistakePercent_m);
        $amplitudeRatioMax=max($amplitudeRatio)*(1+$mistakePercent_p);
        $firstAngleTanMax=max($firstAngleTan)*(1+$mistakePercent_p);
        $firstAngleTanMin=min($firstAngleTan)*(1-$mistakePercent_m);
        $secondAngleTanMax=max($secondAngleTan)*(1+$mistakePercent_p);
        $secondAngleTanMin=min($secondAngleTan)*(1-$mistakePercent_m);
        $averageMax=max($average)*(1+$mistakePercent_p);
        $averageMin=min($average)*(1-$mistakePercent_m);

        execSQL_update("delete from obj_site_narko_data_out_303 where user_id=".$personal_id.";");

        $data=array('clss'=>303,
            'user_id'=>$personal_id,
            'pp_id'=>$pp_ids,   // тут должна быть только одна запись
            'Tlat1Min'=>$TLatFirstMin,
            'Tlat1Max'=>$TLatFirstMax,
            'Tlat2Min'=>$TLatSecondMin,
            'Tlat2Max'=>$TLatSecondMax,
            'Angle1Min'=>$firstAngleTanMin,
            'Angle1Max'=>$firstAngleTanMax,
            'Angle2Min'=>$secondAngleTanMin,
            'Angle2Max'=>$secondAngleTanMax,
            'AmplitudeMin'=>$amplitudeRatioMin,
            'AmplitudeMax'=>$amplitudeRatioMax,
            'AverageMin'=>$averageMin,
            'AverageMax'=>$averageMax,
            'koof_m'=>$mistakePercent_m*100,
            'koof_p'=>$mistakePercent_p*100,
            'auto' => $auto
        ) ;

        $id=adding_rec_to_table('obj_site_narko_data_out_303',$data) ;
        return($id) ;
    }

    // считаем норму автоматически, кластеризуем данные и выбираем запись с наивысшим процентом совпадения
    function calculate_norm_auto($personal_id)
    {
        //$res = execSQL("select * from obj_site_narko_data where id_mo>0 and personal_id=" . $personal_id . " order by pkey");
        $res = execSQL("select pkey from obj_site_narko_data  where enabled=1 and personal_id='" . $personal_id . "'");
        if (_sizeof($res)) {
            foreach ($res as &$rec){
                $rec['leftEye']=$this->get_pp_value($rec['pkey'],'left');
                $rec['rightEye']=$this->get_pp_value($rec['pkey'],'right');
            }

            $rawData = array();
            $dataAnalyzer = array();

            foreach ($res as $rec)
            {
                $analyzer = $this->getAnalyzerByNarko($rec);

                //не учитываем плохо распознанные результаты для подсчета нормы
                if ($analyzer->debug['left']->percent<=$GLOBALS['LS_pp_minPercentForNormCalculate'] or $analyzer->debug['right']->percent<=$GLOBALS['LS_pp_minPercentForNormCalculate']) continue;

                $rawData[] = array(//$analyzer->getTlatFirst(),
                    //$analyzer->getTlatSecond(),
                    //$analyzer->getFirstAngleTan(),
                    //$analyzer->getSecondAngleTan(),
                    $analyzer->getAmplitudeRatio(),
                    $analyzer->getAverage());
                /*
                if((!($analyzerLeft->getQuality()))&&(!($analyzerRight->getQuality())))  {
                    $rawData[] = array(0, 0);
                } else {

                }
                $analyzer = $this->check_narko($rec['pkey'],array('debug'=>1)) ;

                if (isset($analyzer) && is_object($analyzer)) {
                    $rawData[] = array(//$analyzer->getTlatFirst(),
                                    //$analyzer->getTlatSecond(),
                                    //$analyzer->getFirstAngleTan(),
                                    //$analyzer->getSecondAngleTan(),
                                    $analyzer->getAmplitudeRatio(),
                                    $analyzer->getAverage());
                } else {
                    $rawData[] = array(0, 0);
                }
                 */
                $dataAnalyzer[] = array('id' => $rec['pkey'], 'data' => $analyzer);
            }

            //если на выходе результатов меньше чем необходимо для расчета нормы -> выход
            if (_sizeof($rawData)<$GLOBALS['LS_pp_MinMOToAutoSetNorma']) return;

            //Заглушка, нужно реализовать подсчет оптимального количества кластеров в алгоритме
            $numClusters = $this->getNumClusters($rawData);

            $clusteredData = $this->getClusterdData($rawData, $numClusters);

            $maxCluster = array();

            for ($k = 0; $k < $numClusters; ++$k) {
                if (_sizeof($maxCluster) < _sizeof($clusteredData[$k])) {
                    $maxCluster = $clusteredData[$k];
                }
            }

            $dataAnalyzer = array_intersect_key($dataAnalyzer, $maxCluster);

            /*
            usort($dataAnalyzer, function($a, $b) {
                return $a['data']->percent < $b['data']->percent;
            });

            usort($dataAnalyzer, function($a, $b) {
                return $a['data']->debug['other']->percent < $b['data']->debug['other']->percent;
            });
             */

            usort($dataAnalyzer, function($a, $b) {
                return $a['data']->debug['deviation'] > $b['data']->debug['deviation'];
            });

            $analyzerWithMaxPercent = array_shift($dataAnalyzer);
            if ($analyzerWithMaxPercent['data']->getQuality()) {
                $this->calculate_norm($analyzerWithMaxPercent['id'], $personal_id, true);
            }
        }
    }

    function checkAnalyzerForNorm($analyzer)
    {
        //не учитываем плохо распознанные результаты для подсчета нормы
        if ($analyzer->debug['left']->percent<=$GLOBALS['LS_pp_minPercentForNormCalculate']) {
            throw new Exception('Процент распознавания левого глаза меньше чем '.$GLOBALS['LS_pp_minPercentForNormCalculate']);
        }
        if ($analyzer->debug['right']->percent<=$GLOBALS['LS_pp_minPercentForNormCalculate']) {
            throw new Exception('Процент распознавания правого глаза меньше чем '.$GLOBALS['LS_pp_minPercentForNormCalculate']);
        }
        //не учитываем результаты где амплитуда меньше 1
        if ($analyzer->getAmplitudeRatio()<1) {
            throw new Exception('Значение амплитуды меньше 1');
        }
    }

    public function getAnalyzerByNarko($rec) {
        $analyzerLeft=new Analyzer();
        $leftEye=($rec['leftEye'])? $rec['leftEye']:$this->get_pp_value($rec['pkey'],'left') ;
        $analyzerLeft->stringToArrays($leftEye,'left');
        $analyzerRight=new Analyzer();
        $rightEye=($rec['rightEye'])? $rec['rightEye']:$this->get_pp_value($rec['pkey'],'right') ;
        $analyzerRight->stringToArrays($rightEye,'right');


        if($analyzerLeft->percent > $analyzerRight->percent) {
            $analyzer=$analyzerLeft;
            $analyzer->debug['other']=$analyzerRight;
        }
        else {
            $analyzer=$analyzerRight;
            $analyzer->debug['other']=$analyzerLeft;
        }

        $analyzer->debug['left']=$analyzerLeft;
        $analyzer->debug['right']=$analyzerRight;
        $analyzer->debug['deviation']=$this->getAnalyzerDeviation($analyzerLeft->getMassive(), $analyzerRight->getMassive());

        //DTW долго считается для всех записей
        //$leftDTW = $this->getTransformToDTW($analyzerLeft->getMassive());
        //$rightDTW = $this->getTransformToDTW($analyzerRight->getMassive());
        //$analyzer->debug['DTW']=print_r($leftDTW, 1);
        //include_once(_DIR_EXT.'/aspmo_signature/class.DTW.php') ;
        //$obj_DTW = new DTW();
        //$obj_DTW->setReference($leftDTW);
        //$obj_DTW->setCheck($rightDTW);

        //$obj_DTW->getMinCost();

        //$analyzer->debug['DTW'] = $obj_DTW->getMinPath();

        return $analyzer;
    }
    /*
public function getTransformToDTW($arr) {
    $res = array();
    for ($ii = 0; $ii < _sizeof($arr); $ii++) {
        array_push($res, array('x' => $ii, 'y' => $arr[$ii]));
    }

    return $res;
    }
    */
    public function getAnalyzerDeviation($leftEye, $rightEye) {
        $maxSize = max(_sizeof($leftEye), _sizeof($rightEye));

        $sum = 0;

        for ($ii = 0; $ii < $maxSize; $ii++) {

            $l = $this->getElementNumericalSeries($leftEye, $ii);
            $r = $this->getElementNumericalSeries($rightEye, $ii);
            $sum += pow($l - $r, 2);
        }

        return sqrt($sum)/$maxSize;
    }

    private function getElementNumericalSeries($arr, $index) {
        if (isset($arr[$index])) {
            return $arr[$index];
        } else {
            return end($arr);
        }
    }

    public function getNumClusters($rawData) {
        $numClusters = ceil(sqrt(_sizeof($rawData)));
        return $numClusters;
    }

    public function getClusterdData($rawData, $numClusters) {

        $clustering = $this->getClustering($rawData, $numClusters);

        $clusteredData = array();
        for ($k = 0; $k < $numClusters; ++$k)
        {
            for ($i = 0; $i < _sizeof($rawData); ++$i)
            {
                $clusterID = $clustering[$i];
                if ($clusterID != $k) continue;

                $clusteredData[$clusterID][$i] = $rawData[$i];
            }
        }

        return $clusteredData;
    }

    public function getClustering($rawData, $numClusters) {
        //include_once(_DIR_TO_MODULES . '/aspmo/KMeans.class.php');
        include_once(_DIR_TO_SITE_EXT .'/aspmo_pp/KMeans.class.php');
        return ClusteringKMeans\KMeans::Cluster($rawData, $numClusters);
    }

    public function getPPTestsCountByMemberId($memberId) {
        return execSQL_value("select COUNT(pkey) AS COUNT from obj_site_narko_data where enabled=1 and id_mo>0 and personal_id=".$memberId);
    }

    public function hasNormByMemberId($memberId) {
        return execSQL_value("select COUNT(pkey) AS COUNT from obj_site_narko_data_out_303 where user_id=".$memberId);
    }

    public function getPPResultList(){
        $recs=execSQL('select result_code from obj_site_narko_data group by result_code'); //все возможные коды результатов
        $result=[];
        if (_sizeof($recs)) foreach ($recs as $rec){
            $result[$rec['result_code']]=$this->getPPResultName($rec['result_code']);
        }
        return $result;
    }

    public function getPPResultName($result_code){
        $temp_arr=[];
        foreach ($this->pp_result_name as $code=>$name) {
            if ($code & $result_code) $temp_arr[]=$name; //побитовый поиск совпадений результатов
        }
        return implode(', ',$temp_arr);
    }

    public function getTimepickQuery($mode,$timepick_first,$timepick_second=''){
        $result='';
        switch ($mode){
            case 0:
                $timepick_from_array=explode(':',$timepick_first);
                $timepick_from=$timepick_from_array[0]*60+$timepick_from_array[1];
                $timepick_to_array=explode(':',$timepick_second);
                $timepick_to=$timepick_to_array[0]*60+$timepick_to_array[1];
                if ($timepick_from<=$timepick_to) {
                    $result='mo_time between '.$timepick_from.' and '.$timepick_to;
                }
                break;
            case 1:
                $timepick_from_array=explode(':',$timepick_first);
                $timepick_from=$timepick_from_array[0]*60+$timepick_from_array[1];
                $result='mo_time>='.$timepick_from;
                break;
            case 2:
                $timepick_to_array=explode(':',$timepick_first);
                $timepick_to=$timepick_to_array[0]*60+$timepick_to_array[1];
                $result='mo_time<='.$timepick_to;
        }
        return $result;
    }

    function on_event_examination_result_data_read(&$data,$rec_personal,$rec_mo)
     {   // сохраняем данные по Пупиллометрия в отдельную таблицу
             // вызываем функцию для обработки
             // она выдаст результа нарко теста
             $pp_code=0 ; $personal_id=$rec_personal['pkey'] ; $session_id=$rec_mo['pkey'] ;
             if (function_exists('PP') and ($data['args']['data']['pupilometer_left'] or $data['args']['data']['pupilometer_right']))
             {
                 $pp_id=$this->create_pp_rec($session_id,$personal_id,$data['args']['data']['pupilometer_left'],$data['args']['data']['pupilometer_right'],$data['args']['data']['pupilometer_lux'],$data['args']['data']['pupilometer_args']['left_detected_percent'],$data['args']['data']['pupilometer_args']['right_detected_percent']) ;

                 $this->increase_pp_cnt($personal_id); //увеличение кол-ва ПП сотрудника

                 // Считаем норму сотрудника автоматически, если пройдено нужное количество медосмотров
                 $pp_count = $this->getPPTestsCountByMemberId($personal_id);
                 $has_pp_norm = $this->hasNormByMemberId($personal_id);

                 if ($GLOBALS['LS_pp_MinMOToAutoSetNorma'] <= $pp_count && $has_pp_norm == 0)
                 {
                     $this->calculate_norm_auto($personal_id);
                 }
                 // провести ЦНС, только если есть норма для сотрудника
                 list($result_check_narko,$pp_code)=$this->check_narko($pp_id,array('no_check_outer_norm'=>1)) ; // возвращает текстовое сообщение по результату расшифровки данных. Если все в порядке, вернуть пустую строку
                 // только для виртуального терминала - подменяем результат  ПП на требуемый по проверке
                 //if ($data['args']['data']['pupilometer_left']) $pp_code=$data['args']['data']['pupilometer_result'] ;
                 // временно, пока не будет починена ПП
                 //$result_check_narko='' ;
                 // $pp_code='1' ;

                 // сохраняем данные измерений в запись МО
                 ESMO()->update_mo_session($session_id,array('narko'=>$result_check_narko,'res_n'=>$pp_code)) ;
             }
             else
             {
                 $result_check_narko = '';
                 ESMO()->update_mo_session($session_id,array('narko'=>$result_check_narko,'res_n'=>$pp_code)) ;
             }

             // возвращаем данные для работы после обработчка
             $data['esmo_out']['pp_code']=$pp_code ;
             $data['esmo_out']['result_check_narko']=$result_check_narko ;

     }

}


class Analyzer{  //класс, отвечающий за анализ графиков
    //variables
    var $FPS=120;
    var $percent=100;
    var $massiveOfMassives=array();
    var $name='';
    var $debug=array();

    public function getMassive()
    {
        $first=$this->massiveOfMassives[0];
        $second=$this->massiveOfMassives[1];
        $third=$this->massiveOfMassives[2];
        $first[]=0;
        $second[]=0;
        $third[]=0;
        $result=array_merge($first,$second,$third);
        return $result;
    }

    public function getQuality()  //получить процент распознания
    {
        if($this->percent<$GLOBALS['LS_pp_MinRecognitionPercent'])
            return false;
        return true;

    }

    private function getPercent()  //оценка процента распознания. 1-процент распознавания хороший
    {
        $massive=$this->getMassive();
        $detected=0;
        foreach ($massive as $elem)
        {
            if(($elem>400)&&($elem<9000))
            {
                $detected++;
            }
        }
        $this->percent=(100*$detected)/(count($massive));
        return;
    }

    public function stringToArrays($inputString,$name='',$options=array()) //парсинг строки результата на 3 массива
    {   $this->name=$name ;
        $delay=array();
        $work=array();
        $recovery=array();
        $length=stripos($inputString,' ');
        //$this->FPS=substr($inputString,0,$length);
        $inputString=substr($inputString,$length+1,strlen($inputString)-$length-1);
        while(strlen($inputString)!=0)
        {
            $length=stripos($inputString,' ');
            $item=substr($inputString,0,$length);
            $inputString=substr($inputString,$length+1,strlen($inputString)-$length-1);
            if (!is_numeric($item))
                break;
            $delay[]=$item;
        }
        while(strlen($inputString)!=0)
        {
            $length=stripos($inputString,' ');
            $item=substr($inputString,0,$length);
            $inputString=substr($inputString,$length+1,strlen($inputString)-$length-1);
            if (!is_numeric($item))
                break;
            $work[]=$item;
        }
        while(strlen($inputString)!=0)
        {
            $length=stripos($inputString,' ');
            $item=substr($inputString,0,$length);
            $inputString=substr($inputString,$length+1,strlen($inputString)-$length-1);
            if (!is_numeric($item))
                break;
            $recovery[]=$item;
        }
        $this->massiveOfMassives[0]=$delay;
        $this->massiveOfMassives[1]=$work;
        $this->massiveOfMassives[2]=$recovery;
        $this->getPercent();
        if (!$options['no_approximate']) $this->approximate();
        return true;
    }

    private function getDelayLastAvg()   //среднее значение площади зрачка до начала реакции
    {
        $delay=$this->massiveOfMassives[0];
        $result=0;
        for($i=0;$i<6;$i++)
        {
            $result+=$delay[count($delay)-$i-1];
        }
        $result=(int)($result/6);
        //echo " ".$result." ";
        return $result;
    }

    public function getTlatFirst()  //латентное время на включение светодиода
    {
        $massiveOfMassives=$this->massiveOfMassives;
        $delayLastMiddleValue=$this->getDelayLastAvg();
        $work=array_merge($massiveOfMassives[1],$massiveOfMassives[2]);
        $result=0;

        for ($i=0;$i<count($work);$i++)
        {
            $result=$i;
            if($work[$i]<($delayLastMiddleValue-$delayLastMiddleValue*($GLOBALS['LS_pp_MinTlatFirstPercent']*0.01)))
                break;
        }
        return (round($result/$this->FPS,2));

    }

    public function getTlatSecond(){  //латентное время выключения светодиода
        $massiveOfMassives=$this->massiveOfMassives;
        $work=$massiveOfMassives[1];
        $recovery=$massiveOfMassives[2];
        $arr=array_merge($work,$recovery);
        list($minValue,$minNumber)=$this->getMin();
        for ($i=$minNumber+1;$i<count($arr);$i++)
            if ($arr[$i]>$minValue+$minValue*0.05) {
                $result=$i;
                break;
            }
        //вычитаем кол-во элементов массива нагрузки
        $result-=count($work);

        return (round($result/$this->FPS,2));

    }

    private function  getMin()  //минимальное значение  на участке нагрузки
    {
        $massiveOfMassives=$this->massiveOfMassives;
        $work=array_merge($massiveOfMassives[1],$massiveOfMassives[2]);

        $minNumber=0;
        $minValue=$work[$minNumber];
        for ($i=0;$i<(_sizeof($work));$i++){
            if ($work[$i]<$minValue) {
                $minNumber=$i;
                $minValue=$work[$i];
            }
        }

        return(array($minValue,$minNumber));
    }

    private function  getRecoveryMax()  //максимальное значение на восстановлении
    {
        $recovery=$this->massiveOfMassives[2];
        $max=$recovery[0];
        for($i=0;$i<count($recovery);$i++)
            $max = max($recovery[$i], $max);
        for($i=0;$i<count($recovery);$i++)
        {
            if ($recovery[$i]>$max-$max*0.05)
                return $recovery[$i];
        }
    }

    public function getAverage() //среднее значение на участке адаптации
    {
        $massive=$this->massiveOfMassives[0];
        $sum=0;
        $count=0;
        foreach ($massive as $value)
        {
            $sum+=$value;
            $count++;
        }
        if ($count) $result=$sum/$count;
        else $result=0;
        return round($result,2);
    }

    private function getNumberOfValue($value,$massive)   //номер заданного значения
    {
        for ($i=0;$i<count($massive);$i++)
        {
            if ($massive[$i]==$value)
                return $i;
        }
    }

    public function getAmplitude()  //амплитуда реакции
    {
        $max=$this->getDelayLastAvg();
        list($min,$minNumber)=$this->getMin();
//           $min=$this->getMin();
        $amplitude=($max-0.05*$max)-($min+$min*0.05);
        return $amplitude;
    }

    public  function getAmplitudeRatio()  //отношение максимального значения к минимальному
    {
        $max=$this->getDelayLastAvg();
        list($min,$minNumber)=$this->getMin();
        //echo 'max='.$max.'<br>' ;
        //echo 'min='.$min.'<br>' ;
        if ($min!=0)
            $ratio=($max-0.05*$max)/($min+$min*0.05);
        else
            $ratio=0;
        //echo $ratio." ";
        return round($ratio,2);
    }

    public  function getFirstAngleTan()  //тангенс угла наклона реакции на включение
    {
        $massiveOfMassives=$this->massiveOfMassives;
        $result=0;
        list($min,$minNumber)=$this->getMin();
        $work=array_merge($massiveOfMassives[1],$massiveOfMassives[2]);
        $secondPointX=0;
        $firstPointX=0;
        $amplitudeY=$this->getAmplitude();
        for($i=$minNumber;$i>=0;$i--)
        {
            $secondPointX=$i;
            if($work[$i]>($min+$min*0.05))
            {
                break;
            }
        }
        $firstPointX=round($this->getTlatFirst()*$this->FPS);
        if ($secondPointX==$firstPointX)
            $result=0;
        else
            $result=($amplitudeY/(($secondPointX-$firstPointX)/$this->FPS));
        //echo " !".$result."! ";
        return round($result,2);
    }

    public function getSecondAngleTan()  //тангенс угла наклона при выключении светодиода
    {
        $recovery=$this->massiveOfMassives[2];
        $result=0;
        $max=$this->getRecoveryMax();
        $maxNumber=$this->getNumberOfValue($max,$recovery);
        $firstPointX=round($this->getTlatSecond()*$this->FPS);
        $secondPointX=$this->getNumberOfValue($max,$recovery);

        $amplitudeY=$max-$recovery[$firstPointX];
        if ($secondPointX==$firstPointX)
            $result=0;
        else
            $result=($amplitudeY/(($secondPointX-$firstPointX)/$this->FPS));
        /*echo " !".$result."! ";*/
        return round($result,2);
    }

    private function getFlux($massive,$numberFirst,$numberSecond)  //производная
    {
        return (($massive[$numberSecond]-$massive[$numberFirst])/($numberSecond-$numberFirst));

    }

    private function approximate()  //апроксимация пропавших участков графика
    {
        $val1=0;
        $val2=0;
        //$val3=0;
        if ($this->percent<60)
        {
            for ($i=0;$i<count($this->massiveOfMassives[0]);$i++)
            {
                $this->massiveOfMassives[0][$i]=0;
            }
            for ($i=0;$i<count($this->massiveOfMassives[1]);$i++)
            {
                $this->massiveOfMassives[1][$i]=0;
            }
            for ($i=0;$i<count($this->massiveOfMassives[2]);$i++)
            {
                $this->massiveOfMassives[2][$i]=0;
            }
            return true;
        }

        $delayWorkRecovery=array_merge($this->massiveOfMassives[0],$this->massiveOfMassives[1],$this->massiveOfMassives[2]);
        if($delayWorkRecovery[0]<400)
        {
            $val2=0;
            $count=0;
            for ($i=0;$i<count($delayWorkRecovery);$i++)
            {
                $count++;
                if($delayWorkRecovery[$i+1]>400)
                {
                    $val2=$delayWorkRecovery[$i+1];
                    break;
                }
            }
            for($i=0;$i<$count;$i++)
                $delayWorkRecovery[$i]=$val2;
        }
        for ($i=0;$i<(count($delayWorkRecovery)-1);$i++)
        {
            $val1=$delayWorkRecovery[$i];
            $val2=$delayWorkRecovery[$i+1];
            $minValCount=0;
            /*if ((($val2/$val1)>1.1)||(($val2/$val1)<0.9))
            {
                //$delayWorkRecovery[$i+1]=$val1;
            }*/
            if ((($val2/($val1+1))>1.14)||(($val2/($val1+1))<0.86))
            {
                while ((($this->getFlux($delayWorkRecovery,$i,$i+1+$minValCount)>16)||($this->getFlux($delayWorkRecovery,$i,$i+1+$minValCount)<-16))&&(($i+1+$minValCount)<count($delayWorkRecovery)))
                {
                    //echo "сглаживание ".$i." ".$this->getFlux($delayWorkRecovery,$i,$i+1+$minValCount);
                    $minValCount++;
                }
                $val2=$delayWorkRecovery[$i+1+$minValCount];
                if (($minValCount==0)||($minValCount+$i+4>=count($delayWorkRecovery)))
                    $k=0;
                else
                    $k=($val2-$val1)/$minValCount;

                if ($i==0)
                {
                    $k=0;
                    $delayWorkRecovery[$i]=$val2;
                }

                for ($m=0;$m<$minValCount;$m++)
                {
                    $delayWorkRecovery[$i+$m+1]=round($k+$val1);
                    $val1=$delayWorkRecovery[$i+$m+1];
                }
                $i=$i+$m;

            }
        }
        $values=array(0,0,0,0,0,0,0,0,0,0);


        for ($i=0;$i<count($delayWorkRecovery)-count($values)+1;$i++)
        {
            $avg=0;
            for($m=0;$m<count($values);$m++)
            {
                $avg+=$delayWorkRecovery[$i+$m];
            }
            $avg=round($avg/count($values));
            $delayWorkRecovery[$i]=$avg;
        }

        for ($i=0;$i<count($this->massiveOfMassives[0]);$i++)
        {
            $this->massiveOfMassives[0][$i]=$delayWorkRecovery[$i];
        }
        for ($i=0;$i<count($this->massiveOfMassives[1]);$i++)
        {
            $this->massiveOfMassives[1][$i]=$delayWorkRecovery[$i+count($this->massiveOfMassives[0])];
        }
        for ($i=0;$i<count($this->massiveOfMassives[2]);$i++)
        {
            $this->massiveOfMassives[2][$i]=$delayWorkRecovery[$i+count($this->massiveOfMassives[0])+count($this->massiveOfMassives[1])];
        }

    }

}

?>
